from __future__ import annotations import importlib.util from pathlib import Path import sys def _load_semantic_lint_module(): repo_root = Path(__file__).resolve().parents[2] module_path = repo_root / "scripts" / "semantic_lint.py" spec = importlib.util.spec_from_file_location("semantic_lint", module_path) assert spec is not None and spec.loader is not None module = importlib.util.module_from_spec(spec) sys.modules[spec.name] = module spec.loader.exec_module(module) return module def test_semantic_lint_flags_new_scattered_hint_tables(tmp_path) -> None: module = _load_semantic_lint_module() target = tmp_path / "backend" / "app" / "services" / "integrations" / "foo.py" target.parent.mkdir(parents=True, exist_ok=True) target.write_text('PROJECT_CREATE_HINTS = ("创建项目",)\\n', encoding="utf-8") problems = module.collect_problems( root=tmp_path, scan_roots=(Path("backend/app/services/integrations"),), baseline_path=Path("scripts/semantic_lint_baseline.txt"), ) assert len(problems) == 1 assert problems[0].rule_id == "semantic_hint_constant" def test_semantic_lint_flags_workflow_semantic_planner_directory(tmp_path) -> None: module = _load_semantic_lint_module() target = tmp_path / "backend" / "app" / "services" / "ai_pmo" / "workflow_semantic_planner" / "foo.py" target.parent.mkdir(parents=True, exist_ok=True) target.write_text('PROJECT_CREATE_HINTS = ("创建项目",)\\n', encoding="utf-8") problems = module.collect_problems( root=tmp_path, scan_roots=(Path("backend/app/services/ai_pmo"),), baseline_path=Path("scripts/semantic_lint_baseline.txt"), ) assert len(problems) == 1 assert problems[0].rule_id == "semantic_hint_constant" def test_semantic_lint_flags_query_route_object_tables_in_adaptive_rag(tmp_path) -> None: module = _load_semantic_lint_module() target = tmp_path / "backend" / "app" / "services" / "adaptive_rag_service" / "route_gateway.py" target.parent.mkdir(parents=True, exist_ok=True) target.write_text('_INTERNAL_ROUTE_OBJECTS = {"system_config"}\n', encoding="utf-8") problems = module.collect_problems( root=tmp_path, scan_roots=(Path("backend/app/services/adaptive_rag_service"),), baseline_path=Path("scripts/semantic_lint_baseline.txt"), ) assert len(problems) == 1 assert problems[0].rule_id == "semantic_route_object_table" def test_semantic_lint_flags_query_route_probe_helpers_in_adaptive_rag(tmp_path) -> None: module = _load_semantic_lint_module() target = tmp_path / "backend" / "app" / "services" / "adaptive_rag_service" / "route_gateway.py" target.parent.mkdir(parents=True, exist_ok=True) target.write_text("def _external_probe_is_strong(snapshot):\n return bool(snapshot)\n", encoding="utf-8") problems = module.collect_problems( root=tmp_path, scan_roots=(Path("backend/app/services/adaptive_rag_service"),), baseline_path=Path("scripts/semantic_lint_baseline.txt"), ) assert len(problems) == 1 assert problems[0].rule_id == "semantic_route_probe_helper" def test_semantic_lint_flags_external_object_kind_mapping(tmp_path) -> None: module = _load_semantic_lint_module() target = tmp_path / "backend" / "app" / "services" / "external_query_service" / "planner.py" target.parent.mkdir(parents=True, exist_ok=True) target.write_text( 'if "weather" in referenced_objects:\n query_kind = "weather"\n', encoding="utf-8", ) problems = module.collect_problems( root=tmp_path, scan_roots=(Path("backend/app/services/external_query_service"),), baseline_path=Path("scripts/semantic_lint_baseline.txt"), ) assert len(problems) == 1 assert problems[0].rule_id == "semantic_object_kind_mapping" def test_semantic_lint_flags_runtime_marker_tables(tmp_path) -> None: module = _load_semantic_lint_module() target = tmp_path / "backend" / "app" / "services" / "adaptive_rag_service" / "guards.py" target.parent.mkdir(parents=True, exist_ok=True) target.write_text('EXTERNAL_FACT_FRESHNESS_MARKERS = ("最新", "当前")\n', encoding="utf-8") problems = module.collect_problems( root=tmp_path, scan_roots=(Path("backend/app/services/adaptive_rag_service"),), baseline_path=Path("scripts/semantic_lint_baseline.txt"), ) assert len(problems) == 1 assert problems[0].rule_id == "semantic_term_constant" def test_semantic_lint_flags_lowercase_keyword_tables(tmp_path) -> None: module = _load_semantic_lint_module() target = tmp_path / "backend" / "app" / "services" / "integrations" / "reply_guard.py" target.parent.mkdir(parents=True, exist_ok=True) target.write_text('strict_keywords = ("只入库", "仅入库")\n', encoding="utf-8") problems = module.collect_problems( root=tmp_path, scan_roots=(Path("backend/app/services/integrations"),), baseline_path=Path("scripts/semantic_lint_baseline.txt"), ) assert len(problems) == 1 assert problems[0].rule_id == "semantic_lowercase_term_table" def test_semantic_lint_flags_followup_selection_regex_constants(tmp_path) -> None: module = _load_semantic_lint_module() target = tmp_path / "backend" / "app" / "services" / "adaptive_rag_service" / "followup.py" target.parent.mkdir(parents=True, exist_ok=True) target.write_text( 'FOLLOWUP_SELECTION_DIGIT_RE = re.compile(r"^\\\\s*([1-3])\\\\s*$")\n', encoding="utf-8", ) problems = module.collect_problems( root=tmp_path, scan_roots=(Path("backend/app/services/adaptive_rag_service"),), baseline_path=Path("scripts/semantic_lint_baseline.txt"), ) assert len(problems) == 1 assert problems[0].rule_id == "semantic_regex_constant" def test_semantic_lint_flags_frontend_command_keyword_arrays(tmp_path) -> None: module = _load_semantic_lint_module() target = tmp_path / "frontend" / "src" / "components" / "command" / "items.ts" target.parent.mkdir(parents=True, exist_ok=True) target.write_text("const items = [{ keywords: ['dashboard', '首页'] }]\n", encoding="utf-8") problems = module.collect_problems( root=tmp_path, scan_roots=(Path("frontend/src"),), baseline_path=Path("scripts/semantic_lint_baseline.txt"), ) assert len(problems) == 1 assert problems[0].rule_id == "semantic_keywords_property" def test_semantic_lint_default_roots_cover_adaptive_rag_and_external_query() -> None: module = _load_semantic_lint_module() assert Path("backend/app/services/adaptive_rag_service") in module.DEFAULT_SCAN_ROOTS assert Path("backend/app/services/external_query_service") in module.DEFAULT_SCAN_ROOTS assert Path("frontend/src") in module.DEFAULT_SCAN_ROOTS assert Path("scripts") in module.DEFAULT_SCAN_ROOTS def test_semantic_lint_repo_has_no_compat_baseline_exceptions() -> None: repo_root = Path(__file__).resolve().parents[2] baseline_path = repo_root / "scripts" / "semantic_lint_baseline.txt" baseline_rows = [ line.strip() for line in baseline_path.read_text(encoding="utf-8").splitlines() if line.strip() and not line.strip().startswith("#") ] assert baseline_rows == [] module = _load_semantic_lint_module() problems = module.collect_problems( root=repo_root, scan_roots=module.DEFAULT_SCAN_ROOTS, baseline_path=Path("scripts/semantic_lint_baseline.txt"), ) assert problems == []