conftest.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. """
  2. 共享测试 fixtures
  3. 直接加载生产代码 standard_service.py(绕过 package __init__.py 的重依赖链)
  4. """
  5. import sys
  6. import os
  7. import types
  8. import importlib.util
  9. import pytest
  10. # 添加项目根目录到 Python 路径
  11. current_dir = os.path.dirname(os.path.abspath(__file__))
  12. project_root = os.path.dirname(os.path.dirname(current_dir))
  13. if project_root not in sys.path:
  14. sys.path.insert(0, project_root)
  15. def _load_production_module():
  16. """
  17. 直接从文件加载生产代码 standard_service.py
  18. 绕过 core.construction_review.component.__init__.py 的重依赖链
  19. """
  20. # Mock 生产代码依赖的 logger 和 config
  21. if 'foundation.observability.logger.loggering' not in sys.modules:
  22. mock_logger_mod = types.ModuleType('foundation.observability.logger.loggering')
  23. _null_logger = type('_NullLogger', (), {
  24. 'info': lambda self, *a, **kw: None,
  25. 'warning': lambda self, *a, **kw: None,
  26. 'error': lambda self, *a, **kw: None,
  27. 'debug': lambda self, *a, **kw: None,
  28. })()
  29. mock_logger_mod.review_logger = _null_logger
  30. sys.modules['foundation.observability.logger.loggering'] = mock_logger_mod
  31. if 'foundation.infrastructure.config.config' not in sys.modules:
  32. mock_config_mod = types.ModuleType('foundation.infrastructure.config.config')
  33. mock_config_mod.config_handler = type('_ConfigHandler', (), {
  34. 'get': staticmethod(lambda section, key, default=None: default)
  35. })()
  36. sys.modules['foundation.infrastructure.config.config'] = mock_config_mod
  37. # 从文件直接加载 standard_service.py
  38. module_path = os.path.join(
  39. project_root,
  40. 'core', 'construction_review', 'component', 'standard_matching', 'standard_service.py'
  41. )
  42. spec = importlib.util.spec_from_file_location('production_standard_service', module_path)
  43. mod = importlib.util.module_from_spec(spec)
  44. spec.loader.exec_module(mod)
  45. return mod
  46. # 加载生产模块
  47. _prod = _load_production_module()
  48. # 导出生产代码的类
  49. StandardRepository = _prod.StandardRepository
  50. StandardMatcher = _prod.StandardMatcher
  51. StandardMatchResult = _prod.StandardMatchResult
  52. StandardRecord = _prod.StandardRecord
  53. MatchResultCode = _prod.MatchResultCode
  54. ValidityStatus = _prod.ValidityStatus
  55. # ========================================
  56. # 默认测试数据集(覆盖5种匹配状态)
  57. # ========================================
  58. DEFAULT_MOCK_DATA = [
  59. # 情况1: 正常现行标准
  60. {"id": 1, "standard_name": "铁路桥涵设计规范", "standard_number": "TB 10002-2017", "validity": "XH"},
  61. {"id": 2, "standard_name": "铁路工程抗震设计规范", "standard_number": "GB 50111-2006", "validity": "XH"},
  62. {"id": 3, "standard_name": "铁路混凝土工程施工质量验收标准", "standard_number": "TB 10424-2018", "validity": "XH"},
  63. # 情况4: 不匹配(年份错误)- 输入2023,实际2024
  64. {"id": 4, "standard_name": "公路水运危险性较大工程专项施工方案编制审查规程", "standard_number": "JT/T 1495-2024", "validity": "XH"},
  65. # 情况2: 被替代(废止+有现行替代)
  66. {"id": 5, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2016", "validity": "FZ"},
  67. {"id": 6, "standard_name": "起重机 钢丝绳 保养、维护、检验和报废", "standard_number": "GB/T 5972-2023", "validity": "XH"},
  68. # 情况3: 废止无替代
  69. {"id": 7, "standard_name": "缆索起重机", "standard_number": "GB/T 28756-2012", "validity": "FZ"},
  70. {"id": 8, "standard_name": "电力高处作业防坠器", "standard_number": "DL/T 1147-2009", "validity": "FZ"},
  71. ]
  72. def create_matcher(mock_data=None):
  73. """工厂函数:创建加载了测试数据的 matcher"""
  74. repo = StandardRepository()
  75. repo.load_data(mock_data or DEFAULT_MOCK_DATA)
  76. return StandardMatcher(repo)
  77. def check_standards_via_matcher(matcher, standards):
  78. """模拟 StandardMatchingService.check_standards 的批量检查逻辑"""
  79. results = []
  80. for idx, std in enumerate(standards, start=1):
  81. result = matcher.match(
  82. seq_no=idx,
  83. input_name=std.get("standard_name", ""),
  84. input_number=std.get("standard_number", "")
  85. )
  86. if result is not None:
  87. results.append(result)
  88. return results
  89. @pytest.fixture
  90. def repository():
  91. """创建并加载默认测试数据的仓库"""
  92. repo = StandardRepository()
  93. repo.load_data(DEFAULT_MOCK_DATA)
  94. return repo
  95. @pytest.fixture
  96. def matcher(repository):
  97. """创建匹配器实例(基于默认数据)"""
  98. return StandardMatcher(repository)