Coverage for tests/test_converter_serializer.py: 95%

152 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-23 23:00 +0800

1"""測試數據類型轉換器和序列化器""" 

2 

3import pytest 

4from dataclasses import dataclass 

5from autocrud import ModelConverter, SerializerFactory, MemoryStorage, AutoCRUD 

6 

7# 嘗試導入可選依賴 

8try: 

9 from pydantic import BaseModel 

10 HAS_PYDANTIC = True 

11except ImportError: 

12 HAS_PYDANTIC = False 

13 

14try: 

15 from typing import TypedDict 

16except ImportError: 

17 from typing_extensions import TypedDict 

18 

19 

20@dataclass 

21class DataclassUser: 

22 name: str 

23 email: str 

24 age: int 

25 

26 

27if HAS_PYDANTIC: 

28 class PydanticUser(BaseModel): 

29 name: str 

30 email: str 

31 age: int 

32else: 

33 PydanticUser = None 

34 

35 

36class TypedDictUser(TypedDict): 

37 name: str 

38 email: str 

39 age: int 

40 

41 

42class TestModelConverter: 

43 """測試模型轉換器""" 

44 

45 def test_detect_dataclass_type(self): 

46 """測試檢測 dataclass 類型""" 

47 converter = ModelConverter() 

48 model_type = converter.detect_model_type(DataclassUser) 

49 assert model_type == "dataclass" 

50 

51 @pytest.mark.skipif(not HAS_PYDANTIC, reason="Pydantic not available") 

52 def test_detect_pydantic_type(self): 

53 """測試檢測 Pydantic 類型""" 

54 converter = ModelConverter() 

55 model_type = converter.detect_model_type(PydanticUser) 

56 assert model_type == "pydantic" 

57 

58 def test_detect_typeddict_type(self): 

59 """測試檢測 TypedDict 類型""" 

60 converter = ModelConverter() 

61 model_type = converter.detect_model_type(TypedDictUser) 

62 assert model_type == "typeddict" 

63 

64 def test_extract_dataclass_fields(self): 

65 """測試提取 dataclass 欄位""" 

66 converter = ModelConverter() 

67 fields = converter.extract_fields(DataclassUser) 

68 

69 assert 'name' in fields 

70 assert 'email' in fields 

71 assert 'age' in fields 

72 assert fields['name'] == str 

73 assert fields['age'] == int 

74 

75 @pytest.mark.skipif(not HAS_PYDANTIC, reason="Pydantic not available") 

76 def test_extract_pydantic_fields(self): 

77 """測試提取 Pydantic 欄位""" 

78 converter = ModelConverter() 

79 fields = converter.extract_fields(PydanticUser) 

80 

81 assert 'name' in fields 

82 assert 'email' in fields 

83 assert 'age' in fields 

84 

85 def test_dataclass_to_dict(self, sample_user_data): 

86 """測試 dataclass 轉字典""" 

87 converter = ModelConverter() 

88 user = DataclassUser(**sample_user_data) 

89 

90 user_dict = converter.to_dict(user) 

91 

92 assert user_dict['name'] == sample_user_data['name'] 

93 assert user_dict['email'] == sample_user_data['email'] 

94 assert user_dict['age'] == sample_user_data['age'] 

95 

96 def test_dict_to_dataclass(self, sample_user_data): 

97 """測試字典轉 dataclass""" 

98 converter = ModelConverter() 

99 

100 user = converter.from_dict(DataclassUser, sample_user_data) 

101 

102 assert isinstance(user, DataclassUser) 

103 assert user.name == sample_user_data['name'] 

104 assert user.email == sample_user_data['email'] 

105 assert user.age == sample_user_data['age'] 

106 

107 @pytest.mark.skipif(not HAS_PYDANTIC, reason="Pydantic not available") 

108 def test_pydantic_conversion(self, sample_user_data): 

109 """測試 Pydantic 模型轉換""" 

110 converter = ModelConverter() 

111 

112 # 字典轉 Pydantic 

113 user = converter.from_dict(PydanticUser, sample_user_data) 

114 assert isinstance(user, PydanticUser) 

115 assert user.name == sample_user_data['name'] 

116 

117 # Pydantic 轉字典 

118 user_dict = converter.to_dict(user) 

119 assert user_dict['name'] == sample_user_data['name'] 

120 assert user_dict['email'] == sample_user_data['email'] 

121 

122 def test_typeddict_conversion(self, sample_user_data): 

123 """測試 TypedDict 轉換""" 

124 converter = ModelConverter() 

125 

126 # 字典轉 TypedDict(實際上還是字典) 

127 user = converter.from_dict(TypedDictUser, sample_user_data) 

128 assert isinstance(user, dict) 

129 assert user['name'] == sample_user_data['name'] 

130 

131 # TypedDict 轉字典 

132 user_dict = converter.to_dict(user) 

133 assert user_dict == sample_user_data 

134 

135 

136class TestSerializerFactory: 

137 """測試序列化器工廠""" 

138 

139 def test_available_serializers(self): 

140 """測試可用的序列化器""" 

141 available = SerializerFactory.available_types() 

142 

143 assert 'json' in available 

144 assert 'pickle' in available 

145 assert 'msgpack' in available 

146 

147 def test_create_json_serializer(self): 

148 """測試創建 JSON 序列化器""" 

149 serializer = SerializerFactory.create('json') 

150 

151 test_data = {"name": "test", "value": 123} 

152 serialized = serializer.serialize(test_data) 

153 deserialized = serializer.deserialize(serialized) 

154 

155 assert isinstance(serialized, bytes) 

156 assert deserialized == test_data 

157 

158 def test_create_pickle_serializer(self): 

159 """測試創建 Pickle 序列化器""" 

160 serializer = SerializerFactory.create('pickle') 

161 

162 test_data = {"name": "test", "value": 123, "list": [1, 2, 3]} 

163 serialized = serializer.serialize(test_data) 

164 deserialized = serializer.deserialize(serialized) 

165 

166 assert isinstance(serialized, bytes) 

167 assert deserialized == test_data 

168 

169 def test_create_msgpack_serializer(self): 

170 """測試創建 MsgPack 序列化器""" 

171 try: 

172 serializer = SerializerFactory.create('msgpack') 

173 

174 test_data = {"name": "test", "value": 123} 

175 serialized = serializer.serialize(test_data) 

176 deserialized = serializer.deserialize(serialized) 

177 

178 assert isinstance(serialized, bytes) 

179 assert deserialized == test_data 

180 except ImportError: 

181 pytest.skip("msgpack not available") 

182 

183 def test_invalid_serializer_type(self): 

184 """測試無效的序列化器類型""" 

185 with pytest.raises(ValueError): 

186 SerializerFactory.create('invalid_type') 

187 

188 

189class TestIntegratedDataTypes: 

190 """測試整合的數據類型支援""" 

191 

192 def test_dataclass_with_memory_storage(self, sample_user_data): 

193 """測試 dataclass 與內存存儲的整合""" 

194 storage = MemoryStorage() 

195 crud = AutoCRUD(model=DataclassUser, storage=storage, resource_name="users") 

196 

197 created_user = crud.create(sample_user_data) 

198 retrieved_user = crud.get(created_user['id']) 

199 

200 assert retrieved_user is not None 

201 assert retrieved_user['name'] == sample_user_data['name'] 

202 

203 @pytest.mark.skipif(not HAS_PYDANTIC, reason="Pydantic not available") 

204 def test_pydantic_with_memory_storage(self, sample_user_data): 

205 """測試 Pydantic 與內存存儲的整合""" 

206 storage = MemoryStorage() 

207 crud = AutoCRUD(model=PydanticUser, storage=storage, resource_name="users") 

208 

209 created_user = crud.create(sample_user_data) 

210 retrieved_user = crud.get(created_user['id']) 

211 

212 assert retrieved_user is not None 

213 assert retrieved_user['name'] == sample_user_data['name'] 

214 

215 def test_typeddict_with_memory_storage(self, sample_user_data): 

216 """測試 TypedDict 與內存存儲的整合""" 

217 storage = MemoryStorage() 

218 crud = AutoCRUD(model=TypedDictUser, storage=storage, resource_name="users") 

219 

220 created_user = crud.create(sample_user_data) 

221 retrieved_user = crud.get(created_user['id']) 

222 

223 assert retrieved_user is not None 

224 assert retrieved_user['name'] == sample_user_data['name'] 

225 

226 def test_different_serializers_with_storage(self, sample_user_data): 

227 """測試不同序列化器與存儲的組合""" 

228 serializer_types = ['json', 'pickle'] 

229 

230 for serializer_type in serializer_types: 

231 serializer = SerializerFactory.create(serializer_type) 

232 storage = MemoryStorage(serializer=serializer) 

233 crud = AutoCRUD(model=DataclassUser, storage=storage, resource_name="users") 

234 

235 created_user = crud.create(sample_user_data) 

236 retrieved_user = crud.get(created_user['id']) 

237 

238 assert retrieved_user is not None 

239 assert retrieved_user['name'] == sample_user_data['name']