新增项目: - 强基计划(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
194 lines
7.1 KiB
Python
194 lines
7.1 KiB
Python
"""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 可信度口径说明"
|
||
)
|