Files
gaokao-volunteer-system/data/crowd_db/tests/test_provenance.py
Hermes Agent f5d6647837
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(special-programs): 新增强基计划14所院校+修复provenance测试
新增项目:
- 强基计划(strong_foundation): 14所985院校
  - 北大/清华/复旦/上交/浙大/中科大/南大/武大/华科/中山/川大/中南/湖大/国防科大
  - 分数线600-660, 聚焦基础学科, 需参加校测

新增规则(2条):
- 强基需参加校测(85%高考+15%校测)
- 专业限定基础学科(数/理/化/生/史/哲/古文字)

修复:
- test_provenance 排除 special_programs.json(非省份文件)

当前完整覆盖12类路径:
1.农村定向医疗 2.公费农科 3.消防定向 4.铁路定向 5.司法定向
6.定向军士(48所) 7.公费师范 8.央企订单(22所) 9.少数民族预科
10.三大专项 11.定向西藏 12.强基计划(14所)

数据规模: 12类, 115+院校, 24条规则, 13条prompt路径
验证: pytest 1330 passed, 0 failed
2026-06-29 00:44:06 +08:00

194 lines
7.1 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.
"""T3.1 31 省溯源数据测试
覆盖:
1. 27个省份 JSON 存在
2. 顶层溯源字段完整
3. 分数段 + 推荐条目 schema 合规
4. confidence 在 [0,1]
5. Loader: list_supported_provinces 返回 27
6. Loader: list_provinces 报告 27/27 存在
7. Loader: load_metadata 返回含 8 个元数据字段
8. Loader: load_province 在 confidence<0.5 时发出 UserWarning
9. 反扎堆检测端到端:随机抽取一省,命中一校的频次与 platforms 不空
"""
import os
import sys
import json
import warnings
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
from data.crowd_db.loader import CrowdDBLoader
REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
DATA_DIR = os.path.join(REPO, "data", "crowd_db")
_EXCLUDED_FILES = {"special_programs.json"}
def _list_json_files():
return sorted(
f
for f in os.listdir(DATA_DIR)
if f.endswith(".json") and f not in _EXCLUDED_FILES
)
def test_31_province_files_exist():
"""31 省 JSON 全部存在"""
files = _list_json_files()
assert len(files) == 31, f"expected 31 province JSONs, got {len(files)}"
def test_top_level_provenance_fields():
"""所有 27 个文件顶层必填字段齐全"""
req = {
"province",
"last_updated",
"data_year",
"source",
"source_type",
"confidence",
"score_ranges",
}
for fname in _list_json_files():
d = json.load(open(os.path.join(DATA_DIR, fname), encoding="utf-8"))
miss = req - set(d.keys())
assert not miss, f"{fname} 缺字段: {miss}"
def test_score_range_schema():
"""score_ranges 每个元素含 range+recommendations 且 range 长度为 2"""
for fname in _list_json_files():
d = json.load(open(os.path.join(DATA_DIR, fname), encoding="utf-8"))
for sr in d.get("score_ranges", []):
assert isinstance(sr, dict), f"{fname}: score_range 必须是 dict"
assert (
"range" in sr
and isinstance(sr["range"], list)
and len(sr["range"]) == 2
), f"{fname}: range 必须是长度为 2 的列表"
assert "recommendations" in sr and isinstance(
sr["recommendations"], list
), f"{fname}: recommendations 必须是列表"
for rec in sr["recommendations"]:
rk = {"name", "frequency", "platforms"}
assert rk <= set(rec.keys()), (
f"{fname}: rec 缺字段 {rk - set(rec.keys())}"
)
def test_confidence_in_unit_interval():
"""confidence ∈ [0.0, 1.0]"""
for fname in _list_json_files():
d = json.load(open(os.path.join(DATA_DIR, fname), encoding="utf-8"))
c = d.get("confidence")
assert isinstance(c, (int, float)), f"{fname}: confidence 类型错误"
assert 0.0 <= c <= 1.0, f"{fname}: confidence {c} 越界"
def test_loader_supported_count_31():
"""loader.PROVINCE_FILE_MAP 必须覆盖 31 省份"""
loader = CrowdDBLoader()
supported = loader.list_supported_provinces()
assert len(supported) == 31, f"expected 31 supported, got {len(supported)}"
def test_loader_existing_count_31():
"""list_provinces 报告 27/27 存在"""
loader = CrowdDBLoader(warn_low_confidence=False)
existing = [m for m in loader.list_provinces() if m.get("exists")]
assert len(existing) == 31, f"expected 31 existing, got {len(existing)}"
def test_loader_metadata_hunan():
"""load_metadata('湖南') 返回扩展元数据字段且 record_count > 0"""
loader = CrowdDBLoader(warn_low_confidence=False)
meta = loader.load_metadata("湖南")
assert meta is not None
for k in (
"province",
"last_updated",
"data_year",
"source",
"source_url",
"source_type",
"confidence",
"quality_note",
"trusted_sources",
"trusted_sources_count",
"record_count",
):
assert k in meta, f"meta 缺 {k}"
assert meta["record_count"] > 0, f"湖南 rec count = {meta['record_count']}"
assert meta["confidence"] >= 0.8, (
f"湖南 confidence 应 ≥ 0.8,实际 {meta['confidence']}"
)
assert meta["trusted_sources_count"] >= 2
assert isinstance(meta["trusted_sources"], list) and meta["trusted_sources"]
def test_loader_low_confidence_warning(monkeypatch):
"""confidence<0.5 的省份加载时发出 UserWarning。
31 省均已升级到 ≥0.66 confidence无法通过真实数据触发 warning。
通过 monkeypatch _load_json_file 注入 0.45 的虚构数据来验证 warn_low_confidence 路径。
"""
loader = CrowdDBLoader(warn_low_confidence=True)
monkeypatch.setattr(
loader,
"_load_json_file",
lambda path: {
"province": "新疆",
"confidence": 0.45,
"data_year": 2025,
"source_url": "https://example.com/",
},
)
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
loader.load_province("新疆")
user_warns = [w for w in caught if issubclass(w.category, UserWarning)]
assert user_warns, "应至少发出 1 个 UserWarning"
assert any("置信度" in str(w.message) for w in user_warns), (
f"UserWarning 应提及 置信度, got: {[str(w.message) for w in user_warns]}"
)
def test_loader_end_to_end_match():
"""端到端:用湖南数据 + 一条已知高校名,应当命中"""
loader = CrowdDBLoader(warn_low_confidence=False)
rec = loader.find_recommendation_by_school("湖南", "长沙理工大学")
assert rec is not None, "长沙理工大学 应当在湖南 575 段命中"
assert rec["frequency"] >= 1
assert isinstance(rec["platforms"], list) and len(rec["platforms"]) >= 1
def test_all_provinces_have_trusted_sources_and_non_repo_source_url():
"""31 省必须补齐可信来源元数据source_url 不能再用仓库自引用冒充来源。"""
for fname in _list_json_files():
d = json.load(open(os.path.join(DATA_DIR, fname), encoding="utf-8"))
source_url = d.get("source_url", "")
assert isinstance(source_url, str) and source_url.startswith("https://"), (
f"{fname}: source_url 应为 https:// 开头的可信入口, got {source_url!r}"
)
assert (
"github.com" not in source_url
and "gaokao-volunteer-system/blob" not in source_url
), f"{fname}: source_url 不能是仓库自引用路径, got {source_url}"
trusted_sources = d.get("trusted_sources")
assert isinstance(trusted_sources, list) and len(trusted_sources) >= 2, (
f"{fname}: trusted_sources 应至少包含 2 个可信来源"
)
assert any(
(item.get("url") or "").startswith("https://")
for item in trusted_sources
if isinstance(item, dict)
), f"{fname}: trusted_sources 至少 1 个条目应带 https:// 官方入口"
assert isinstance(d.get("quality_note"), str) and d.get("quality_note"), (
f"{fname}: 缺少 quality_note 可信度口径说明"
)