test_standard_matching.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. """
  2. 标准库匹配规则单元测试
  3. 通过 conftest.py 加载生产代码 standard_service.py(绕过 package __init__.py 重依赖)
  4. """
  5. import sys
  6. import os
  7. import pytest
  8. # 使用 conftest.py 中的共享 fixtures 和工厂函数
  9. from conftest import (
  10. StandardRepository, StandardMatcher, MatchResultCode,
  11. create_matcher, DEFAULT_MOCK_DATA,
  12. )
  13. class TestStandardRepository:
  14. """测试数据仓库"""
  15. def test_load_data(self, repository):
  16. """测试数据加载"""
  17. assert len(repository.get_all_records()) == 8
  18. def test_find_by_number_exact(self, repository):
  19. """测试精确匹配标准号"""
  20. result = repository.find_by_number_exact("TB 10002-2017")
  21. assert result is not None
  22. assert result.standard_name == "铁路桥涵设计规范"
  23. def test_find_by_name_exact(self, repository):
  24. """测试精确匹配名称"""
  25. result = repository.find_by_name_exact("铁路桥涵设计规范")
  26. assert result is not None
  27. assert result.standard_number == "TB 10002-2017"
  28. def test_find_current_by_name(self, repository):
  29. """测试查询现行标准(使用规范化名称)"""
  30. # 生产代码的 find_current_by_normalized_name 使用归一化名称
  31. # 注意:归一化保留了中文顿号"、"
  32. normalized = repository._normalize_for_matching("起重机 钢丝绳 保养、维护、检验和报废")
  33. results = repository.find_current_by_normalized_name(normalized)
  34. assert len(results) == 1
  35. assert results[0].standard_number == "GB/T 5972-2023"
  36. class TestStandardMatcher:
  37. """测试匹配器(基于生产代码的归一化匹配逻辑)"""
  38. def test_case1_ok(self, matcher):
  39. """情况1: 状态正常"""
  40. result = matcher.match(1, "铁路桥涵设计规范", "TB 10002-2017")
  41. assert result.status_code == MatchResultCode.OK.value
  42. assert result.final_result == "无问题"
  43. def test_case2_substituted(self, matcher):
  44. """情况2: 被替代"""
  45. result = matcher.match(1, "起重机 钢丝绳 保养、维护、检验和报废", "GB/T 5972-2016")
  46. assert result.status_code == MatchResultCode.SUBSTITUTED.value
  47. assert result.substitute_number == "(GB/T 5972-2023)"
  48. assert "已废止" in result.final_result
  49. def test_case3_abolished(self, matcher):
  50. """情况3: 废止无替代"""
  51. result = matcher.match(1, "缆索起重机", "GB/T 28756-2012")
  52. assert result.status_code == MatchResultCode.ABOLISHED.value
  53. assert "无现行状态" in result.final_result
  54. def test_case4_mismatch(self, matcher):
  55. """情况4: 不匹配"""
  56. result = matcher.match(1, "公路水运危险性较大工程专项施工方案编制审查规程", "JT/T 1495-2023")
  57. assert result.status_code == MatchResultCode.MISMATCH.value
  58. assert result.substitute_number == "(JT/T 1495-2024)"
  59. def test_case5_not_found(self, matcher):
  60. """情况5: 不存在"""
  61. result = matcher.match(1, "不存在的标准", "GB/T 99999-9999")
  62. assert result.status_code == MatchResultCode.NOT_FOUND.value
  63. def test_empty_name_returns_none(self, matcher):
  64. """空名称应返回 None(生产代码跳过审查逻辑)"""
  65. result = matcher.match(1, "", "TB 10002-2017")
  66. assert result is None
  67. def test_raw_fields_preserved(self, matcher):
  68. """验证原始输入字段正确保存"""
  69. result = matcher.match(1, "《铁路桥涵设计规范》", "(TB 10002-2017)")
  70. assert result is not None
  71. assert result.raw_name == "《铁路桥涵设计规范》"
  72. assert result.raw_number == "(TB 10002-2017)"
  73. assert result.normalized_name == "铁路桥涵设计规范"
  74. assert result.normalized_number == "TB100022017"
  75. assert result.status_code == MatchResultCode.OK.value
  76. class TestStandardMatchingBatch:
  77. """测试批量检查(模拟 StandardMatchingService.check_standards 逻辑)"""
  78. def test_check_standards_batch(self, matcher):
  79. """测试批量检查"""
  80. standards = [
  81. {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
  82. {"standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012"},
  83. {"standard_name": "不存在的标准", "standard_number": "GB/T 99999-9999"},
  84. ]
  85. from conftest import check_standards_via_matcher
  86. results = check_standards_via_matcher(matcher, standards)
  87. assert len(results) == 3
  88. assert results[0].status_code == MatchResultCode.OK.value
  89. assert results[1].status_code == MatchResultCode.ABOLISHED.value
  90. assert results[2].status_code == MatchResultCode.NOT_FOUND.value
  91. def test_batch_filters_none(self, matcher):
  92. """空名称应被过滤(生产代码行为)"""
  93. standards = [
  94. {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
  95. {"standard_name": "", "standard_number": "TB 10002-2017"}, # 空名称
  96. ]
  97. from conftest import check_standards_via_matcher
  98. results = check_standards_via_matcher(matcher, standards)
  99. assert len(results) == 1
  100. assert results[0].status_code == MatchResultCode.OK.value
  101. class TestDocumentExamples:
  102. """测试文档中列出的7个示例"""
  103. def test_example_1_railway_bridge(self, matcher):
  104. """(1)《铁路桥涵设计规范》(TB 10002-2017) - 正常"""
  105. result = matcher.match(1, "铁路桥涵设计规范", "TB 10002-2017")
  106. assert result.status_code == MatchResultCode.OK.value
  107. def test_example_2_seismic_design(self, matcher):
  108. """(2)《铁路工程抗震设计规范》(GB 50111-2006) - 正常"""
  109. result = matcher.match(1, "铁路工程抗震设计规范", "GB 50111-2006")
  110. assert result.status_code == MatchResultCode.OK.value
  111. def test_example_3_concrete(self, matcher):
  112. """(3)《铁路混凝土工程施工质量验收标准》(TB 10424-2018) - 正常"""
  113. result = matcher.match(1, "铁路混凝土工程施工质量验收标准", "TB 10424-2018")
  114. assert result.status_code == MatchResultCode.OK.value
  115. def test_example_4_highway_mismatch(self, matcher):
  116. """(4)年份不匹配 - 应为2024"""
  117. result = matcher.match(1, "公路水运危险性较大工程专项施工方案编制审查规程", "JT/T 1495-2023")
  118. assert result.status_code == MatchResultCode.MISMATCH.value
  119. assert result.substitute_number == "(JT/T 1495-2024)"
  120. def test_example_5_crane_wire_substituted(self, matcher):
  121. """(5)被替代 GB/T 5972-2016 -> GB/T 5972-2023"""
  122. result = matcher.match(1, "起重机 钢丝绳 保养、维护、检验和报废", "GB/T 5972-2016")
  123. assert result.status_code == MatchResultCode.SUBSTITUTED.value
  124. assert result.substitute_number == "(GB/T 5972-2023)"
  125. def test_example_6_cable_crane_abolished(self, matcher):
  126. """(6)废止无替代"""
  127. result = matcher.match(1, "缆索起重机", "GB/T 28756-2012")
  128. assert result.status_code == MatchResultCode.ABOLISHED.value
  129. def test_example_7_safety_device_abolished(self, matcher):
  130. """(7)废止无替代"""
  131. result = matcher.match(1, "电力高处作业防坠器", "DL/T 1147-2009")
  132. assert result.status_code == MatchResultCode.ABOLISHED.value
  133. if __name__ == "__main__":
  134. pytest.main([__file__, "-v"])