test_json_parser.py 7.09 KB
"""
测试RobustJSONParser的各种修复能力。

验证解析器能够处理:
1. 基本的markdown包裹
2. 思考内容清理
3. 缺少逗号的修复
4. 括号不平衡的修复
5. 控制字符转义
6. 尾随逗号移除
"""

import json
import unittest
from json_parser import RobustJSONParser, JSONParseError


class TestRobustJSONParser(unittest.TestCase):
    """测试鲁棒JSON解析器的各种修复策略。"""

    def setUp(self):
        """初始化解析器。"""
        self.parser = RobustJSONParser(
            enable_json_repair=False,  # 先测试本地修复
            enable_llm_repair=False,
        )

    def test_basic_json(self):
        """测试解析基本的合法JSON。"""
        json_str = '{"name": "test", "value": 123}'
        result = self.parser.parse(json_str, "基本测试")
        self.assertEqual(result["name"], "test")
        self.assertEqual(result["value"], 123)

    def test_markdown_wrapped(self):
        """测试解析被```json包裹的JSON。"""
        json_str = """```json
{
  "name": "test",
  "value": 123
}
```"""
        result = self.parser.parse(json_str, "Markdown包裹测试")
        self.assertEqual(result["name"], "test")
        self.assertEqual(result["value"], 123)

    def test_thinking_content_removal(self):
        """测试清理思考内容。"""
        json_str = """<thinking>让我想想如何构造这个JSON</thinking>
{
  "name": "test",
  "value": 123
}"""
        result = self.parser.parse(json_str, "思考内容清理测试")
        self.assertEqual(result["name"], "test")
        self.assertEqual(result["value"], 123)

    def test_missing_comma_fix(self):
        """测试修复缺少的逗号。"""
        # 这是实际错误中常见的情况:数组元素之间缺少逗号
        json_str = """{
  "totalWords": 40000,
  "globalGuidelines": [
    "重点突出技术红利分配失衡"
    "详略策略:技术创新"
  ],
  "chapters": []
}"""
        result = self.parser.parse(json_str, "缺少逗号修复测试")
        self.assertEqual(len(result["globalGuidelines"]), 2)

    def test_unbalanced_brackets(self):
        """测试修复括号不平衡。"""
        # 缺少结束括号
        json_str = """{
  "name": "test",
  "nested": {
    "value": 123
  }
"""  # 缺少最外层的 }
        result = self.parser.parse(json_str, "括号不平衡测试")
        self.assertEqual(result["name"], "test")
        self.assertEqual(result["nested"]["value"], 123)

    def test_control_character_escape(self):
        """测试转义控制字符。"""
        # JSON字符串中的裸换行符应该被转义
        json_str = """{
  "text": "这是第一行
这是第二行",
  "value": 123
}"""
        result = self.parser.parse(json_str, "控制字符转义测试")
        # 确保换行符被正确处理
        self.assertIn("第一行", result["text"])
        self.assertIn("第二行", result["text"])

    def test_trailing_comma_removal(self):
        """测试移除尾随逗号。"""
        json_str = """{
  "name": "test",
  "value": 123,
  "items": [1, 2, 3,],
}"""
        result = self.parser.parse(json_str, "尾随逗号测试")
        self.assertEqual(result["name"], "test")
        self.assertEqual(len(result["items"]), 3)

    def test_colon_equals_fix(self):
        """测试修复冒号等号错误。"""
        json_str = """{
  "name":= "test",
  "value": 123
}"""
        result = self.parser.parse(json_str, "冒号等号测试")
        self.assertEqual(result["name"], "test")

    def test_extract_first_json(self):
        """测试从文本中提取第一个JSON结构。"""
        json_str = """这是一些说明文字,下面是JSON:
{
  "name": "test",
  "value": 123
}
后面还有一些其他文字"""
        result = self.parser.parse(json_str, "提取JSON测试")
        self.assertEqual(result["name"], "test")
        self.assertEqual(result["value"], 123)

    def test_complex_real_world_case(self):
        """测试真实世界的复杂案例(类似实际错误)。"""
        # 模拟实际错误:缺少逗号、有markdown包裹、有思考内容
        json_str = """<thinking>我需要构造一个篇幅规划</thinking>
```json
{
  "totalWords": 40000,
  "tolerance": 2000,
  "globalGuidelines": [
    "重点突出技术红利分配失衡、人才流失与职业认同危机等结构性矛盾"
    "详略策略:技术创新与传统技艺的碰撞"
    "案例导向:优先引用真实数据和调研"
  ],
  "chapters": [
    {
      "chapterId": "ch1",
      "targetWords": 5000
    }
  ]
}
```"""
        result = self.parser.parse(json_str, "复杂真实案例测试")
        self.assertEqual(result["totalWords"], 40000)
        self.assertEqual(result["tolerance"], 2000)
        self.assertEqual(len(result["globalGuidelines"]), 3)
        self.assertEqual(len(result["chapters"]), 1)

    def test_expected_keys_validation(self):
        """测试期望键的验证。"""
        json_str = '{"name": "test"}'
        # 不应该因为缺少键而失败,只是警告
        result = self.parser.parse(
            json_str, "键验证测试", expected_keys=["name", "value"]
        )
        self.assertIn("name", result)

    def test_wrapper_key_extraction(self):
        """测试从包裹键中提取数据。"""
        json_str = """{
  "wrapper": {
    "name": "test",
    "value": 123
  }
}"""
        result = self.parser.parse(
            json_str, "包裹键测试", extract_wrapper_key="wrapper"
        )
        self.assertEqual(result["name"], "test")
        self.assertEqual(result["value"], 123)

    def test_empty_input(self):
        """测试空输入。"""
        with self.assertRaises(JSONParseError):
            self.parser.parse("", "空输入测试")

    def test_invalid_json_after_all_repairs(self):
        """测试所有修复策略都无法处理的情况。"""
        # 这是一个严重损坏的JSON,无法修复
        json_str = "{完全不是JSON格式的内容###"
        with self.assertRaises(JSONParseError):
            self.parser.parse(json_str, "无法修复测试")


def run_manual_test():
    """手动运行测试,打印详细信息。"""
    print("=" * 60)
    print("开始测试RobustJSONParser")
    print("=" * 60)

    parser = RobustJSONParser(enable_json_repair=False, enable_llm_repair=False)

    # 测试实际错误案例
    test_case = """```json
{
  "totalWords": 40000,
  "tolerance": 2000,
  "globalGuidelines": [
    "重点突出技术红利分配失衡、人才流失与职业认同危机等结构性矛盾"
    "详略策略:技术创新与传统技艺的碰撞"
  ],
  "chapters": []
}
```"""

    print("\n测试案例:")
    print(test_case)
    print("\n" + "=" * 60)

    try:
        result = parser.parse(test_case, "手动测试")
        print("\n✓ 解析成功!")
        print("\n解析结果:")
        print(json.dumps(result, ensure_ascii=False, indent=2))
    except Exception as e:
        print(f"\n✗ 解析失败: {e}")

    print("\n" + "=" * 60)


if __name__ == "__main__":
    # 运行手动测试
    run_manual_test()

    # 运行单元测试
    print("\n\n运行单元测试...")
    unittest.main(verbosity=2)