Files
gaokao-volunteer-system/tests/test_rules_truth_phase1.py
Hermes Agent 246f21c7f2
Some checks failed
CI / pytest (Python 3.10) (push) Has been cancelled
CI / pytest (Python 3.11) (push) Has been cancelled
CI / pytest (Python 3.12) (push) Has been cancelled
feat(review): land unified remediation and rules evidence closure
2026-06-18 10:21:38 +08:00

348 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
import importlib.util
from importlib.machinery import SourceFileLoader
from pathlib import Path
import yaml # type: ignore[import-untyped]
PROJECT_ROOT = Path(__file__).resolve().parents[1]
LEGACY_CHECKER_PATH = PROJECT_ROOT / "scripts" / "gaokao-checker"
def _load_python_module(name: str, path: Path):
loader = SourceFileLoader(name, str(path))
spec = importlib.util.spec_from_loader(loader.name, loader)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def _load_legacy_checker_module():
loader = SourceFileLoader("legacy_gaokao_checker", str(LEGACY_CHECKER_PATH))
spec = importlib.util.spec_from_loader(loader.name, loader)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_migrate_province_rules_to_truth_exports_all_legacy_provinces(
tmp_path: Path,
) -> None:
legacy = _load_legacy_checker_module()
migrate = _load_python_module(
'migrate_province_rules_to_truth',
PROJECT_ROOT / 'scripts' / 'migrate_province_rules_to_truth.py',
)
summary = migrate.migrate_legacy_rules_to_truth(output_root=tmp_path)
province_dir = tmp_path / "province"
province_files = sorted(province_dir.glob("*.yaml"))
assert summary["province_count"] == len(legacy.PROVINCE_RULES)
assert len(province_files) == len(legacy.PROVINCE_RULES)
assert (tmp_path / "national.yaml").is_file()
hunan_truth = yaml.safe_load(
(province_dir / "hunan.yaml").read_text(encoding="utf-8")
)
assert hunan_truth["province"] == "湖南"
assert hunan_truth["year"] == 2026
assert hunan_truth["status"] == "active"
assert hunan_truth["rules"]["max_volunteers"]["value"] == {"max_volunteers": 45}
assert hunan_truth["rules"]["max_volunteers"]["source_evidence_id"]
def test_rule_loader_reads_national_and_province_truth_tree(tmp_path: Path) -> None:
truth_root = tmp_path
(truth_root / "province").mkdir(parents=True, exist_ok=True)
(truth_root / "national.yaml").write_text(
yaml.safe_dump(
{
"scope": "national",
"year": 2026,
"version": "2026.1",
"last_verified_at": "2026-06-17",
"rules": {
"parallel_volunteer_principle": {
"title": "平行志愿原则",
"severity": "info",
"value": {"rule": "分数优先、遵循志愿、一次投档"},
"source_evidence_id": "national-2026-parallel-rule",
"effective_date": "2026-01-01",
"last_verified_at": "2026-06-17",
"status": "active",
}
},
},
allow_unicode=True,
sort_keys=False,
),
encoding="utf-8",
)
(truth_root / "province" / "hunan.yaml").write_text(
yaml.safe_dump(
{
"scope": "province",
"province": "湖南",
"year": 2026,
"version": "2026.1",
"last_verified_at": "2026-06-17",
"status": "active",
"rules": {
"max_volunteers": {
"title": "本科批志愿上限",
"severity": "fatal",
"value": {"max_volunteers": 45},
"source_evidence_id": "hunan-2026-max-volunteers",
"effective_date": "2026-01-01",
"last_verified_at": "2026-06-17",
"status": "active",
}
},
},
allow_unicode=True,
sort_keys=False,
),
encoding="utf-8",
)
from data.rules.loader import RuleLoader
evidence_root = tmp_path.parent / "_evidence" / "hunan"
evidence_root.mkdir(parents=True, exist_ok=True)
(evidence_root / "hunan-2026-max-volunteers.md").write_text(
"\n".join(
[
"# hunan-2026-max-volunteers",
"",
"> 对应规则: `HUNAN.max_volunteers`",
"> 所属: 省级",
"> 规则版本: `2026.1`",
"> 摘录时间: 2026-06-17",
"> 摘录人: pytest",
"",
"## 1. 官方原文摘录",
"",
'> "湖南本科批院校专业组最多 45 个志愿。" —— 出处: 湖南省招生规则, http://example.com, 2026-06-01',
"",
"## 2. 转写为机读规则",
"",
"```yaml",
"HUNAN.max_volunteers:",
" severity: fatal",
" value:",
" max_volunteers: 45",
" effective_date: 2026-01-01",
" source_evidence_id: hunan-2026-max-volunteers",
" status: active",
"```",
"",
"## 3. 关键边界与例外",
"",
"- 例 1示例",
"- 例 2示例",
"",
"## 4. 后续维护",
"",
"- 下次复核时间: 2026-09-01",
"- 复核来源: http://example.com",
"- 复核负责人: pytest",
"",
]
),
encoding="utf-8",
)
loader = RuleLoader.from_truth_root(truth_root)
status = loader.build_status()
rules = loader.list_province_rules("湖南")
national = loader.list_national_rules()
assert status.province_count == 1
assert status.national_rule_count == 1
assert status.active_provinces == ["湖南"]
assert status.national_version == "2026.1"
assert status.stale_rule_max_age_days == 90
assert status.stale_rule_count == 0
assert status.stale_rule_ids == []
assert rules[0].rule_id == "HUNAN.max_volunteers"
assert rules[0].value["max_volunteers"] == 45
assert rules[0].last_verified_at == "2026-06-17"
assert rules[0].evidence_exists is True
assert national[0].rule_id == "NATIONAL.parallel_volunteer_principle"
def test_rule_loader_verify_flags_stale_or_missing_evidence(tmp_path: Path) -> None:
truth_root = tmp_path / "truth"
province_dir = truth_root / "province"
province_dir.mkdir(parents=True, exist_ok=True)
(truth_root / "national.yaml").write_text(
yaml.safe_dump(
{
"scope": "national",
"year": 2026,
"version": "2026.1",
"last_verified_at": "2026-01-01",
"rules": {
"parallel_volunteer_principle": {
"title": "平行志愿原则",
"severity": "info",
"value": {"rule": "分数优先、遵循志愿、一次投档"},
"source_evidence_id": "national-2026-parallel-rule",
"effective_date": "2026-01-01",
"last_verified_at": "2026-01-01",
"status": "active",
}
},
},
allow_unicode=True,
sort_keys=False,
),
encoding="utf-8",
)
(province_dir / "hunan.yaml").write_text(
yaml.safe_dump(
{
"scope": "province",
"province": "湖南",
"year": 2026,
"version": "2026.1",
"last_verified_at": "2026-01-01",
"status": "active",
"rules": {
"max_volunteers": {
"title": "本科批志愿上限",
"severity": "fatal",
"value": {"max_volunteers": 45},
"source_evidence_id": "hunan-2026-max-volunteers",
"effective_date": "2026-01-01",
"last_verified_at": "2026-01-01",
"status": "active",
}
},
},
allow_unicode=True,
sort_keys=False,
),
encoding="utf-8",
)
from data.rules.loader import RuleLoader
loader = RuleLoader.from_truth_root(truth_root)
payload = loader.verify(strict_evidence=True, max_rule_age_days=90)
assert payload["ok"] is False
assert payload["stale_rule_count"] == 2
assert "NATIONAL.parallel_volunteer_principle" in payload["stale_rule_ids"]
assert "HUNAN.max_volunteers" in payload["stale_rule_ids"]
assert payload["missing_evidence_rule_count"] == 2
assert "NATIONAL.parallel_volunteer_principle" in payload["missing_evidence_rule_ids"]
assert "HUNAN.max_volunteers" in payload["missing_evidence_rule_ids"]
def test_rule_loader_scaffold_templates_do_not_count_as_completed_evidence(
tmp_path: Path,
) -> None:
truth_root = tmp_path / "truth"
province_dir = truth_root / "province"
province_dir.mkdir(parents=True, exist_ok=True)
(truth_root / "national.yaml").write_text(
yaml.safe_dump(
{
"scope": "national",
"year": 2026,
"version": "2026.1",
"last_verified_at": "2026-06-17",
"rules": {},
},
allow_unicode=True,
sort_keys=False,
),
encoding="utf-8",
)
(province_dir / "hunan.yaml").write_text(
yaml.safe_dump(
{
"scope": "province",
"province": "湖南",
"year": 2026,
"version": "2026.1",
"last_verified_at": "2026-06-17",
"status": "active",
"rules": {
"max_volunteers": {
"title": "本科批志愿上限",
"severity": "fatal",
"value": {"max_volunteers": 45},
"source_evidence_id": "hunan-2026-max-volunteers",
"effective_date": "2026-01-01",
"last_verified_at": "2026-06-17",
"status": "active",
}
},
},
allow_unicode=True,
sort_keys=False,
),
encoding="utf-8",
)
from data.rules.loader import RuleLoader
loader = RuleLoader.from_truth_root(truth_root)
payload = loader.scaffold_missing_evidence(province="湖南")
reloaded = RuleLoader.from_truth_root(truth_root)
rules = reloaded.list_province_rules("湖南")
assert payload["created_rule_count"] == 1
assert payload["touched_index_count"] == 1
evidence_path = tmp_path / "_evidence" / "hunan" / "hunan-2026-max-volunteers.md"
assert evidence_path.is_file()
assert "证据状态: draft_template" in evidence_path.read_text(encoding="utf-8")
assert rules[0].evidence_exists is False
def test_migrated_truth_set_matches_legacy_checker_provinces(tmp_path: Path) -> None:
legacy = _load_legacy_checker_module()
migrate = _load_python_module(
'migrate_province_rules_to_truth',
PROJECT_ROOT / 'scripts' / 'migrate_province_rules_to_truth.py',
)
from data.rules.loader import RuleLoader
migrate.migrate_legacy_rules_to_truth(output_root=tmp_path)
loader = RuleLoader.from_truth_root(tmp_path)
assert set(loader.active_provinces()) == set(legacy.PROVINCE_RULES.keys())
def test_audit_engine_exposes_province_snapshot_from_truth(tmp_path: Path) -> None:
from data.rules.audit_engine import AuditEngine
from data.rules.loader import RuleLoader
migrate = _load_python_module(
'migrate_province_rules_to_truth',
PROJECT_ROOT / 'scripts' / 'migrate_province_rules_to_truth.py',
)
migrate.migrate_legacy_rules_to_truth(output_root=tmp_path)
loader = RuleLoader.from_truth_root(tmp_path)
engine = AuditEngine(loader)
snapshot = engine.get_province_snapshot("湖南")
assert snapshot.province == "湖南"
assert snapshot.mode == "院校专业组"
assert snapshot.max_volunteers == 45
assert snapshot.retrieval_rule == "分数优先、遵循志愿、一次投档"
assert snapshot.rule_count >= 5