test_json_utils.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. """
  2. JSON 安全解析功能组件的单元测试
  3. """
  4. import pytest
  5. import json
  6. from src.components.functions.json_utils import (
  7. JsonSafeParseFunction,
  8. JSONParseError,
  9. safe_json_parse,
  10. batch_json_parse,
  11. validate_json_structure
  12. )
  13. class TestJsonSafeParseFunction:
  14. """测试 JsonSafeParseFunction 类"""
  15. def setup_method(self):
  16. """设置测试方法"""
  17. self.parser = JsonSafeParseFunction()
  18. def test_execute_basic(self):
  19. """测试基本 JSON 解析"""
  20. # 测试简单对象
  21. json_str = '{"name": "Alice", "age": 30}'
  22. result = self.parser.execute(json_str)
  23. assert result == {"name": "Alice", "age": 30}
  24. # 测试数组
  25. json_str = '[1, 2, 3, "test"]'
  26. result = self.parser.execute(json_str)
  27. assert result == [1, 2, 3, "test"]
  28. def test_execute_wrapped_json(self):
  29. """测试包装的 JSON 解析"""
  30. # 测试 ```json``` 包装
  31. json_str = '```json\n{"key": "value"}\n```'
  32. result = self.parser.execute(json_str)
  33. assert result == {"key": "value"}
  34. # 测试 ``` 包装
  35. json_str = '```\n{"key": "value"}\n```'
  36. result = self.parser.execute(json_str)
  37. assert result == {"key": "value"}
  38. # 测试带额外空白的包装
  39. json_str = ' ```json \n {"key": "value"} \n ``` '
  40. result = self.parser.execute(json_str)
  41. assert result == {"key": "value"}
  42. def test_execute_with_context(self):
  43. """测试带上下文的解析"""
  44. json_str = '{"name": "Alice", "age": 30}'
  45. context = {"options": {"strict": False}}
  46. result = self.parser.execute(json_str, context)
  47. assert result == {"name": "Alice", "age": 30}
  48. def test_pure_function_property(self):
  49. """测试纯函数特性"""
  50. json_str = '{"test": "data"}'
  51. # 多次调用应返回相同结果
  52. result1 = self.parser.execute(json_str)
  53. result2 = self.parser.execute(json_str)
  54. assert result1 == result2
  55. # 不同实例应返回相同结果
  56. parser2 = JsonSafeParseFunction()
  57. result3 = parser2.execute(json_str)
  58. assert result1 == result3
  59. def test_invalid_input_type(self):
  60. """测试无效输入类型"""
  61. with pytest.raises(JSONParseError) as exc_info:
  62. self.parser.execute(123) # 非字符串输入
  63. assert "Expected string input" in str(exc_info.value)
  64. def test_invalid_json(self):
  65. """测试无效 JSON"""
  66. with pytest.raises(JSONParseError) as exc_info:
  67. self.parser.execute('{"invalid": json}') # 无效 JSON
  68. assert "JSON decode error" in str(exc_info.value)
  69. def test_empty_input(self):
  70. """测试空输入"""
  71. with pytest.raises(JSONParseError) as exc_info:
  72. self.parser.execute('')
  73. assert "Empty or invalid JSON content" in str(exc_info.value)
  74. def test_extract_json_from_wrapped_string(self):
  75. """测试 JSON 提取方法"""
  76. # 测试各种格式
  77. test_cases = [
  78. ('```json\n{"key": "value"}\n```', '{"key": "value"}'),
  79. ('```\n[1, 2, 3]\n```', '[1, 2, 3]'),
  80. ('{"key": "value"}', '{"key": "value"}'),
  81. ('[1, 2, 3]', '[1, 2, 3]'),
  82. (' ```JSON \n {"test": true} \n ``` ', '{"test": true}'),
  83. ]
  84. for input_str, expected in test_cases:
  85. result = self.parser._extract_json_from_wrapped_string(input_str)
  86. assert result == expected
  87. def test_complex_nested_json(self):
  88. """测试复杂嵌套 JSON"""
  89. complex_json = {
  90. "users": [
  91. {"name": "Alice", "age": 30, "active": True},
  92. {"name": "Bob", "age": 25, "active": False}
  93. ],
  94. "metadata": {
  95. "total": 2,
  96. "timestamp": "2024-01-01T00:00:00Z"
  97. }
  98. }
  99. json_str = f'```json\n{json.dumps(complex_json, indent=2)}\n```'
  100. result = self.parser.execute(json_str)
  101. assert result == complex_json
  102. class TestSafeJsonParse:
  103. """测试 safe_json_parse 便捷函数"""
  104. def test_basic_parsing(self):
  105. """测试基本解析功能"""
  106. result = safe_json_parse('{"name": "test"}')
  107. assert result == {"name": "test"}
  108. def test_wrapped_parsing(self):
  109. """测试包装格式解析"""
  110. result = safe_json_parse('```json\n{"wrapped": true}\n```')
  111. assert result == {"wrapped": True}
  112. def test_error_handling(self):
  113. """测试错误处理"""
  114. with pytest.raises(JSONParseError):
  115. safe_json_parse('invalid json')
  116. class TestBatchJsonParse:
  117. """测试批量 JSON 解析功能"""
  118. def test_batch_parsing_success(self):
  119. """测试成功的批量解析"""
  120. json_strings = [
  121. '{"name": "Alice"}',
  122. '```json\n{"name": "Bob"}\n```',
  123. '[1, 2, 3]'
  124. ]
  125. results = batch_json_parse(json_strings)
  126. expected = [
  127. {"name": "Alice"},
  128. {"name": "Bob"},
  129. [1, 2, 3]
  130. ]
  131. assert results == expected
  132. def test_batch_parsing_error(self):
  133. """测试批量解析中的错误"""
  134. json_strings = [
  135. '{"name": "Alice"}',
  136. 'invalid json', # 这个会导致错误
  137. '[1, 2, 3]'
  138. ]
  139. with pytest.raises(JSONParseError) as exc_info:
  140. batch_json_parse(json_strings)
  141. assert "Failed to parse JSON at index 1" in str(exc_info.value)
  142. def test_invalid_input_type(self):
  143. """测试无效输入类型"""
  144. with pytest.raises(JSONParseError) as exc_info:
  145. batch_json_parse("not a list")
  146. assert "Expected list input" in str(exc_info.value)
  147. class TestValidateJsonStructure:
  148. """测试 JSON 结构验证功能"""
  149. def test_validate_string_input(self):
  150. """测试字符串输入验证"""
  151. result = validate_json_structure('{"name": "Alice", "age": 30}')
  152. assert result['valid'] is True
  153. assert result['data'] == {"name": "Alice", "age": 30}
  154. assert len(result['errors']) == 0
  155. def test_validate_dict_input(self):
  156. """测试字典输入验证"""
  157. data = {"name": "Alice", "age": 30}
  158. result = validate_json_structure(data)
  159. assert result['valid'] is True
  160. assert result['data'] == data
  161. assert len(result['errors']) == 0
  162. def test_validate_with_required_keys(self):
  163. """测试必需字段验证"""
  164. context = {
  165. 'validation_rules': {
  166. 'required_keys': ['name', 'age', 'email']
  167. }
  168. }
  169. # 缺少字段的情况
  170. result = validate_json_structure('{"name": "Alice", "age": 30}', context)
  171. assert result['valid'] is False
  172. assert any("Missing required keys" in error for error in result['errors'])
  173. # 包含所有字段的情况
  174. result = validate_json_structure(
  175. '{"name": "Alice", "age": 30, "email": "alice@test.com"}',
  176. context
  177. )
  178. assert result['valid'] is True
  179. def test_validate_expected_type(self):
  180. """测试期望类型验证"""
  181. # 期望对象但得到数组
  182. context = {'validation_rules': {'expected_type': 'object'}}
  183. result = validate_json_structure('[1, 2, 3]', context)
  184. assert result['valid'] is False
  185. assert any("Expected JSON object" in error for error in result['errors'])
  186. # 期望数组但得到对象
  187. context = {'validation_rules': {'expected_type': 'array'}}
  188. result = validate_json_structure('{"key": "value"}', context)
  189. assert result['valid'] is False
  190. assert any("Expected JSON array" in error for error in result['errors'])
  191. def test_validate_invalid_json(self):
  192. """测试无效 JSON 验证"""
  193. result = validate_json_structure('invalid json')
  194. assert result['valid'] is False
  195. assert result['data'] is None
  196. assert len(result['errors']) > 0
  197. def test_validate_non_json_types(self):
  198. """测试非 JSON 类型验证"""
  199. # 测试无法解析为 JSON 的字符串
  200. result = validate_json_structure("just a string")
  201. assert result['valid'] is False
  202. assert any("JSON decode error" in error for error in result['errors'])
  203. # 测试解析成功但不是 JSON 对象或数组的情况(如字符串、数字等)
  204. result = validate_json_structure('"just a string"') # 这是有效的 JSON 字符串
  205. assert result['valid'] is False
  206. assert any("not a JSON object or array" in error for error in result['errors'])
  207. class TestPipelineComposition:
  208. """测试管道组合能力"""
  209. def test_json_parse_in_pipeline(self):
  210. """测试 JSON 解析在管道中的使用"""
  211. from src.components.functions import create_simple_function, create_pipeline
  212. # 创建一个简单的数据转换函数
  213. def extract_name(data, context=None):
  214. if isinstance(data, dict) and 'name' in data:
  215. return data['name']
  216. raise ValueError("No name field found")
  217. # 创建管道
  218. parse_func = JsonSafeParseFunction()
  219. extract_func = create_simple_function("extract_name", "提取姓名", extract_name)
  220. pipeline = create_pipeline("parse_and_extract", "解析并提取", [parse_func, extract_func])
  221. # 测试管道执行
  222. json_str = '```json\n{"name": "Alice", "age": 30}\n```'
  223. result = pipeline(json_str)
  224. assert result == "Alice"
  225. def test_error_propagation_in_pipeline(self):
  226. """测试管道中的错误传播"""
  227. from src.components.functions import create_simple_function, create_pipeline
  228. def dummy_transform(data, context=None):
  229. return data
  230. parse_func = JsonSafeParseFunction()
  231. transform_func = create_simple_function("dummy", "虚拟转换", dummy_transform)
  232. pipeline = create_pipeline("parse_pipeline", "解析管道", [parse_func, transform_func])
  233. # 测试无效 JSON 在管道中的错误传播
  234. with pytest.raises(JSONParseError):
  235. pipeline('invalid json')
  236. class TestContextHandling:
  237. """测试上下文处理"""
  238. def test_context_options(self):
  239. """测试上下文选项"""
  240. parser = JsonSafeParseFunction()
  241. # 测试严格模式
  242. context_strict = {'options': {'strict': True}}
  243. result = parser.execute('{"name": "Alice"}', context_strict)
  244. assert result == {"name": "Alice"}
  245. # 测试非严格模式
  246. context_non_strict = {'options': {'strict': False}}
  247. result = parser.execute('{"name": "Alice"}', context_non_strict)
  248. assert result == {"name": "Alice"}
  249. def test_context_preservation(self):
  250. """测试上下文保持"""
  251. # 验证函数不会修改传入的上下文
  252. original_context = {'options': {'strict': True}, 'other': 'data'}
  253. context_copy = original_context.copy()
  254. safe_json_parse('{"test": true}', context_copy)
  255. assert context_copy == original_context
  256. if __name__ == "__main__":
  257. pytest.main([__file__])