|
|
@@ -1,96 +1,49 @@
|
|
|
"""
|
|
|
-标准库匹配规则单元测试 - 内存处理版本
|
|
|
-测试文档中定义的5种匹配情况
|
|
|
+标准库匹配规则单元测试
|
|
|
+通过 conftest.py 加载生产代码 standard_service.py(绕过 package __init__.py 重依赖)
|
|
|
"""
|
|
|
import sys
|
|
|
import os
|
|
|
-# 添加项目根目录到 Python 路径
|
|
|
-current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
-project_root = os.path.dirname(os.path.dirname(current_dir))
|
|
|
-if project_root not in sys.path:
|
|
|
- sys.path.insert(0, project_root)
|
|
|
-
|
|
|
import pytest
|
|
|
-import asyncio
|
|
|
-from utils_test.standard_new_Test.standard_service import (
|
|
|
- StandardRepository,
|
|
|
- StandardMatcher,
|
|
|
- StandardMatchingService,
|
|
|
- MatchResultCode,
|
|
|
- ValidityStatus,
|
|
|
- StandardRecord
|
|
|
-)
|
|
|
-
|
|
|
-
|
|
|
-@pytest.fixture
|
|
|
-def mock_repository():
|
|
|
- """创建模拟的数据仓库"""
|
|
|
- repo = StandardRepository()
|
|
|
- mock_data = [
|
|
|
- # 情况1: 正常现行标准
|
|
|
- {"id": 1, "standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017", "validity": "XH"},
|
|
|
- {"id": 2, "standard_name": "铁路工程抗震设计规范", "standard_number": "GB 50111-2006", "validity": "XH"},
|
|
|
- {"id": 3, "standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018", "validity": "XH"},
|
|
|
-
|
|
|
- # 情况4: 不匹配(年份错误)- 输入2023,实际2024
|
|
|
- {"id": 4, "standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2024", "validity": "XH"},
|
|
|
-
|
|
|
- # 情况2: 被替代(废止+有现行替代)
|
|
|
- {"id": 5, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016", "validity": "FZ"},
|
|
|
- {"id": 6, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2023", "validity": "XH"},
|
|
|
-
|
|
|
- # 情况3: 废止无替代
|
|
|
- {"id": 7, "standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012", "validity": "FZ"},
|
|
|
- {"id": 8, "standard_name": "电力高处作业防坠器", "standard_number": "DL/T 1147-2009", "validity": "FZ"},
|
|
|
- ]
|
|
|
- repo.load_data(mock_data)
|
|
|
- return repo
|
|
|
|
|
|
-
|
|
|
-@pytest.fixture
|
|
|
-def matcher(mock_repository):
|
|
|
- """创建匹配器实例"""
|
|
|
- return StandardMatcher(mock_repository)
|
|
|
-
|
|
|
-
|
|
|
-@pytest.fixture
|
|
|
-def mock_service():
|
|
|
- """创建使用Mock数据的服务"""
|
|
|
- service = StandardMatchingService(db_pool=None)
|
|
|
- # 同步初始化(测试时使用event loop)
|
|
|
- loop = asyncio.get_event_loop()
|
|
|
- loop.run_until_complete(service.initialize())
|
|
|
- return service
|
|
|
+# 使用 conftest.py 中的共享 fixtures 和工厂函数
|
|
|
+from conftest import (
|
|
|
+ StandardRepository, StandardMatcher, MatchResultCode,
|
|
|
+ create_matcher, DEFAULT_MOCK_DATA,
|
|
|
+)
|
|
|
|
|
|
|
|
|
class TestStandardRepository:
|
|
|
"""测试数据仓库"""
|
|
|
|
|
|
- def test_load_data(self, mock_repository):
|
|
|
+ def test_load_data(self, repository):
|
|
|
"""测试数据加载"""
|
|
|
- assert len(mock_repository.get_all_records()) == 8
|
|
|
+ assert len(repository.get_all_records()) == 8
|
|
|
|
|
|
- def test_find_by_number_exact(self, mock_repository):
|
|
|
+ def test_find_by_number_exact(self, repository):
|
|
|
"""测试精确匹配标准号"""
|
|
|
- result = mock_repository.find_by_number_exact("TB 10002-2017")
|
|
|
+ result = repository.find_by_number_exact("TB 10002-2017")
|
|
|
assert result is not None
|
|
|
assert result.standard_name == "铁路桥涵设计规范"
|
|
|
|
|
|
- def test_find_by_name_exact(self, mock_repository):
|
|
|
+ def test_find_by_name_exact(self, repository):
|
|
|
"""测试精确匹配名称"""
|
|
|
- result = mock_repository.find_by_name_exact("铁路桥涵设计规范")
|
|
|
+ result = repository.find_by_name_exact("铁路桥涵设计规范")
|
|
|
assert result is not None
|
|
|
assert result.standard_number == "TB 10002-2017"
|
|
|
|
|
|
- def test_find_current_by_name(self, mock_repository):
|
|
|
- """测试查询现行标准"""
|
|
|
- results = mock_repository.find_current_by_name("起重机 钢丝绳 保养、维护、检验和报废")
|
|
|
+ def test_find_current_by_name(self, repository):
|
|
|
+ """测试查询现行标准(使用规范化名称)"""
|
|
|
+ # 生产代码的 find_current_by_normalized_name 使用归一化名称
|
|
|
+ # 注意:归一化保留了中文顿号"、"
|
|
|
+ normalized = repository._normalize_for_matching("起重机 钢丝绳 保养、维护、检验和报废")
|
|
|
+ results = repository.find_current_by_normalized_name(normalized)
|
|
|
assert len(results) == 1
|
|
|
assert results[0].standard_number == "GB/T 5972-2023"
|
|
|
|
|
|
|
|
|
class TestStandardMatcher:
|
|
|
- """测试匹配器"""
|
|
|
+ """测试匹配器(基于生产代码的归一化匹配逻辑)"""
|
|
|
|
|
|
def test_case1_ok(self, matcher):
|
|
|
"""情况1: 状态正常"""
|
|
|
@@ -102,7 +55,7 @@ class TestStandardMatcher:
|
|
|
"""情况2: 被替代"""
|
|
|
result = matcher.match(1, "起重机 钢丝绳 保养、维护、检验和报废", "GB/T 5972-2016")
|
|
|
assert result.status_code == MatchResultCode.SUBSTITUTED.value
|
|
|
- assert result.substitute_number == "GB/T 5972-2023"
|
|
|
+ assert result.substitute_number == "(GB/T 5972-2023)"
|
|
|
assert "已废止" in result.final_result
|
|
|
|
|
|
def test_case3_abolished(self, matcher):
|
|
|
@@ -115,91 +68,96 @@ class TestStandardMatcher:
|
|
|
"""情况4: 不匹配"""
|
|
|
result = matcher.match(1, "公路水运危险性较大工程专项施工方案编制审查规程", "JT/T 1495-2023")
|
|
|
assert result.status_code == MatchResultCode.MISMATCH.value
|
|
|
- assert result.substitute_number == "JT/T 1495-2024"
|
|
|
+ assert result.substitute_number == "(JT/T 1495-2024)"
|
|
|
|
|
|
def test_case5_not_found(self, matcher):
|
|
|
"""情况5: 不存在"""
|
|
|
result = matcher.match(1, "不存在的标准", "GB/T 99999-9999")
|
|
|
assert result.status_code == MatchResultCode.NOT_FOUND.value
|
|
|
|
|
|
+ def test_empty_name_returns_none(self, matcher):
|
|
|
+ """空名称应返回 None(生产代码跳过审查逻辑)"""
|
|
|
+ result = matcher.match(1, "", "TB 10002-2017")
|
|
|
+ assert result is None
|
|
|
|
|
|
-class TestStandardMatchingService:
|
|
|
- """测试服务类"""
|
|
|
-
|
|
|
- def test_check_single(self, mock_service):
|
|
|
- """测试单个检查"""
|
|
|
- result = mock_service.check_single(1, "铁路桥涵设计规范", "TB 10002-2017")
|
|
|
+ def test_raw_fields_preserved(self, matcher):
|
|
|
+ """验证原始输入字段正确保存"""
|
|
|
+ result = matcher.match(1, "《铁路桥涵设计规范》", "(TB 10002-2017)")
|
|
|
+ assert result is not None
|
|
|
+ assert result.raw_name == "《铁路桥涵设计规范》"
|
|
|
+ assert result.raw_number == "(TB 10002-2017)"
|
|
|
+ assert result.normalized_name == "铁路桥涵设计规范"
|
|
|
+ assert result.normalized_number == "TB100022017"
|
|
|
assert result.status_code == MatchResultCode.OK.value
|
|
|
|
|
|
- def test_check_standards_batch(self, mock_service):
|
|
|
+
|
|
|
+class TestStandardMatchingBatch:
|
|
|
+ """测试批量检查(模拟 StandardMatchingService.check_standards 逻辑)"""
|
|
|
+
|
|
|
+ def test_check_standards_batch(self, matcher):
|
|
|
"""测试批量检查"""
|
|
|
standards = [
|
|
|
{"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
|
|
|
{"standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012"},
|
|
|
{"standard_name": "不存在的标准", "standard_number": "GB/T 99999-9999"},
|
|
|
]
|
|
|
- results = mock_service.check_standards(standards)
|
|
|
+ from conftest import check_standards_via_matcher
|
|
|
+ results = check_standards_via_matcher(matcher, standards)
|
|
|
assert len(results) == 3
|
|
|
assert results[0].status_code == MatchResultCode.OK.value
|
|
|
assert results[1].status_code == MatchResultCode.ABOLISHED.value
|
|
|
assert results[2].status_code == MatchResultCode.NOT_FOUND.value
|
|
|
|
|
|
+ def test_batch_filters_none(self, matcher):
|
|
|
+ """空名称应被过滤(生产代码行为)"""
|
|
|
+ standards = [
|
|
|
+ {"standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017"},
|
|
|
+ {"standard_name": "", "standard_number": "TB 10002-2017"}, # 空名称
|
|
|
+ ]
|
|
|
+ from conftest import check_standards_via_matcher
|
|
|
+ results = check_standards_via_matcher(matcher, standards)
|
|
|
+ assert len(results) == 1
|
|
|
+ assert results[0].status_code == MatchResultCode.OK.value
|
|
|
+
|
|
|
|
|
|
class TestDocumentExamples:
|
|
|
"""测试文档中列出的7个示例"""
|
|
|
|
|
|
- @pytest.fixture
|
|
|
- def doc_matcher(self):
|
|
|
- """文档测试专用匹配器"""
|
|
|
- repo = StandardRepository()
|
|
|
- mock_data = [
|
|
|
- {"id": 1, "standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017", "validity": "XH"},
|
|
|
- {"id": 2, "standard_name": "铁路工程抗震设计规范", "standard_number": "GB 50111-2006", "validity": "XH"},
|
|
|
- {"id": 3, "standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018", "validity": "XH"},
|
|
|
- {"id": 4, "standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2024", "validity": "XH"},
|
|
|
- {"id": 5, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016", "validity": "FZ"},
|
|
|
- {"id": 6, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2023", "validity": "XH"},
|
|
|
- {"id": 7, "standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012", "validity": "FZ"},
|
|
|
- {"id": 8, "standard_name": "电力高处作业防坠器", "standard_number": "DL/T 1147-2009", "validity": "FZ"},
|
|
|
- ]
|
|
|
- repo.load_data(mock_data)
|
|
|
- return StandardMatcher(repo)
|
|
|
-
|
|
|
- def test_example_1_railway_bridge(self, doc_matcher):
|
|
|
+ def test_example_1_railway_bridge(self, matcher):
|
|
|
"""(1)《铁路桥涵设计规范》(TB 10002-2017) - 正常"""
|
|
|
- result = doc_matcher.match(1, "铁路桥涵设计规范", "TB 10002-2017")
|
|
|
+ result = matcher.match(1, "铁路桥涵设计规范", "TB 10002-2017")
|
|
|
assert result.status_code == MatchResultCode.OK.value
|
|
|
|
|
|
- def test_example_2_seismic_design(self, doc_matcher):
|
|
|
+ def test_example_2_seismic_design(self, matcher):
|
|
|
"""(2)《铁路工程抗震设计规范》(GB 50111-2006) - 正常"""
|
|
|
- result = doc_matcher.match(1, "铁路工程抗震设计规范", "GB 50111-2006")
|
|
|
+ result = matcher.match(1, "铁路工程抗震设计规范", "GB 50111-2006")
|
|
|
assert result.status_code == MatchResultCode.OK.value
|
|
|
|
|
|
- def test_example_3_concrete(self, doc_matcher):
|
|
|
+ def test_example_3_concrete(self, matcher):
|
|
|
"""(3)《铁路混凝土工程施工质量验收标准》(TB 10424-2018) - 正常"""
|
|
|
- result = doc_matcher.match(1, "铁路混凝土工程施工质量验收标准", "TB 10424-2018")
|
|
|
+ result = matcher.match(1, "铁路混凝土工程施工质量验收标准", "TB 10424-2018")
|
|
|
assert result.status_code == MatchResultCode.OK.value
|
|
|
|
|
|
- def test_example_4_highway_mismatch(self, doc_matcher):
|
|
|
+ def test_example_4_highway_mismatch(self, matcher):
|
|
|
"""(4)年份不匹配 - 应为2024"""
|
|
|
- result = doc_matcher.match(1, "公路水运危险性较大工程专项施工方案编制审查规程", "JT/T 1495-2023")
|
|
|
+ result = matcher.match(1, "公路水运危险性较大工程专项施工方案编制审查规程", "JT/T 1495-2023")
|
|
|
assert result.status_code == MatchResultCode.MISMATCH.value
|
|
|
- assert result.substitute_number == "JT/T 1495-2024"
|
|
|
+ assert result.substitute_number == "(JT/T 1495-2024)"
|
|
|
|
|
|
- def test_example_5_crane_wire_substituted(self, doc_matcher):
|
|
|
+ def test_example_5_crane_wire_substituted(self, matcher):
|
|
|
"""(5)被替代 GB/T 5972-2016 -> GB/T 5972-2023"""
|
|
|
- result = doc_matcher.match(1, "起重机 钢丝绳 保养、维护、检验和报废", "GB/T 5972-2016")
|
|
|
+ result = matcher.match(1, "起重机 钢丝绳 保养、维护、检验和报废", "GB/T 5972-2016")
|
|
|
assert result.status_code == MatchResultCode.SUBSTITUTED.value
|
|
|
- assert result.substitute_number == "GB/T 5972-2023"
|
|
|
+ assert result.substitute_number == "(GB/T 5972-2023)"
|
|
|
|
|
|
- def test_example_6_cable_crane_abolished(self, doc_matcher):
|
|
|
+ def test_example_6_cable_crane_abolished(self, matcher):
|
|
|
"""(6)废止无替代"""
|
|
|
- result = doc_matcher.match(1, "缆索起重机", "GB/T 28756-2012")
|
|
|
+ result = matcher.match(1, "缆索起重机", "GB/T 28756-2012")
|
|
|
assert result.status_code == MatchResultCode.ABOLISHED.value
|
|
|
|
|
|
- def test_example_7_safety_device_abolished(self, doc_matcher):
|
|
|
+ def test_example_7_safety_device_abolished(self, matcher):
|
|
|
"""(7)废止无替代"""
|
|
|
- result = doc_matcher.match(1, "电力高处作业防坠器", "DL/T 1147-2009")
|
|
|
+ result = matcher.match(1, "电力高处作业防坠器", "DL/T 1147-2009")
|
|
|
assert result.status_code == MatchResultCode.ABOLISHED.value
|
|
|
|
|
|
|