按用户提供的31省2026分数线汇总继续补充最新数据。 本次补录: - 浙江:官方页确认 2026 分数线为 特控594 / 一段494 / 二段266 - 四川:准官方汇总源确认 2026 分数线为 本科历史455 / 物理435 / 特控历史525 落地方式: - zhejiang.json / sichuan.json data_year -> 2026 - 更新 source_url / quality_note - 保持现有高信任推荐内容,仅做元数据与数据年份收口,不做无依据的大幅 score_ranges 重排 同步修复: - test_crowd_db_data_quality.py: 已确认2026省份集合扩展到8省 - test_provenance_query.py: 2025/2026 过渡期断言从 25 调整到 23 - test_risk_report.py / test_trace_cli.py: 允许 2025/2026 data_year - check_crowd_db_consistency.py: 明确允许 2025+2026 多年份共存 验证: - pytest crowd_db: 155 passed, 3 skipped - consistency: high=31 usable=0 low=0 skeleton=0 - balance check: 通过
217 lines
7.5 KiB
Python
217 lines
7.5 KiB
Python
"""crowd_db 数据质量契约测试 (6/20 Q-A 闭环).
|
||
|
||
CROWD_DB_DATA_QUALITY.md §7 承诺的锁死文件, 实际仓库中缺失。
|
||
本测试锁住以下契约:
|
||
- 31 省总数 (23 省 + 4 直辖市 + 4 自治区, 不含港澳台)
|
||
- high 白名单显式枚举(当前为 湖南/广东/江苏/山东/河北)
|
||
- 其余省份可以是 usable 或 skeleton,但非白名单省份不得进入 high
|
||
- 高考生源大省中仍未进入白名单者不得高于 usable
|
||
- 新增 high 省份必须显式更新本测试, 避免"小变化悄悄升级"
|
||
|
||
Q-A 审计依据: reports/QA_CROWD_DB_NON_HUNAN_DENSITY_AUDIT.md (6/20)
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from data.crowd_db.quality_summary import build_quality_summary
|
||
from data.crowd_db.loader import CrowdDBLoader
|
||
|
||
|
||
# 高信任白名单(当前 controller 允许进入 high 的省份)
|
||
# 任何新增/移除 high 省份都必须显式更新本测试,避免"静默升级"。
|
||
# 6/25 Stage 1: 新增河南/四川/湖北/北京/上海(5 省从 usable 升 high)
|
||
# 6/25 Stage 2: 27 省全部达 high(15 省批量扩容完毕)
|
||
# 6/25 Stage 4: 新增 4 自治区,全国 31 省全部达 high
|
||
HIGH_TRUST_PROVINCES = frozenset({
|
||
"湖南",
|
||
"广东",
|
||
"江苏",
|
||
"山东",
|
||
"河北",
|
||
"浙江",
|
||
"福建",
|
||
"河南",
|
||
"四川",
|
||
"湖北",
|
||
"北京",
|
||
"上海",
|
||
"安徽",
|
||
"重庆",
|
||
"甘肃",
|
||
"贵州",
|
||
"海南",
|
||
"黑龙江",
|
||
"江西",
|
||
"吉林",
|
||
"辽宁",
|
||
"青海",
|
||
"陕西",
|
||
"山西",
|
||
"天津",
|
||
"新疆",
|
||
"云南",
|
||
"内蒙古",
|
||
"广西",
|
||
"西藏",
|
||
"宁夏",
|
||
})
|
||
|
||
# 仍不允许进入 high 的高考生源大省(除已进入白名单者外)
|
||
# 6/25 Stage 1 后:四川/河南/湖北/北京/上海 已升 high;其余高考生源大省仍非 high
|
||
HIGH_POPULATION_PROVINCES_NOT_YET_HIGH: frozenset[str] = frozenset({
|
||
# 当前 27 个 high 已覆盖主要高考生源大省,此处保留为约束锚点
|
||
# 如未来有新高考生源大省进入 31 省范围,需重新评估
|
||
})
|
||
|
||
|
||
@pytest.fixture(scope="module")
|
||
def summary():
|
||
return build_quality_summary(CrowdDBLoader(warn_low_confidence=False))
|
||
|
||
|
||
def test_total_provinces_is_31(summary):
|
||
"""6/20 真相: 31 个 JSON (23 省 + 4 直辖市), 不含 5 自治区/港澳台。"""
|
||
assert summary["total_provinces"] == 31
|
||
assert len(summary["provinces"]) == 31
|
||
|
||
|
||
def test_high_quality_province_whitelist(summary):
|
||
"""高信任省份必须显式落在白名单中,避免静默升级。"""
|
||
high_provinces = {
|
||
p["province"] for p in summary["provinces"] if p["quality_level"] == "high"
|
||
}
|
||
assert high_provinces == HIGH_TRUST_PROVINCES, (
|
||
f"预期 high 白名单为 {sorted(HIGH_TRUST_PROVINCES)},实际: {sorted(high_provinces)}。"
|
||
"新增/移除 high 省份必须显式更新本测试。"
|
||
)
|
||
|
||
|
||
def test_hunan_confidence_meets_high_threshold(summary):
|
||
"""湖南 confidence 必须 >= 0.8 (high 入门门槛, 见 quality_summary.py)。"""
|
||
hunan = next(p for p in summary["provinces"] if p["province"] == "湖南")
|
||
assert hunan["confidence"] >= 0.8, (
|
||
f"湖南 confidence={hunan['confidence']} 不满足 high 门槛 >= 0.8"
|
||
)
|
||
assert hunan["quality_level"] == "high"
|
||
|
||
|
||
def test_non_whitelist_provinces_not_high(summary):
|
||
"""不在 high 白名单中的省份不能被判为 high。"""
|
||
non_whitelist = [
|
||
p for p in summary["provinces"] if p["province"] not in HIGH_TRUST_PROVINCES
|
||
]
|
||
assert len(non_whitelist) == 31 - len(HIGH_TRUST_PROVINCES)
|
||
leaked = [p["province"] for p in non_whitelist if p["quality_level"] == "high"]
|
||
assert leaked == [], (
|
||
f"以下省份被错标为 high(不在白名单): {leaked}。"
|
||
"如需新增 high, 必须同步更新 HIGH_TRUST_PROVINCES。"
|
||
)
|
||
|
||
|
||
def test_shandong_is_high_quality_province(summary):
|
||
"""山东已进入 high 白名单。"""
|
||
by_name = {p["province"]: p for p in summary["provinces"]}
|
||
shandong = by_name["山东"]
|
||
assert shandong["quality_level"] == "high"
|
||
assert shandong["confidence"] >= 0.8
|
||
|
||
|
||
def test_guangdong_is_high_quality_province(summary):
|
||
"""广东已进入 high 白名单。"""
|
||
by_name = {p["province"]: p for p in summary["provinces"]}
|
||
guangdong = by_name["广东"]
|
||
assert guangdong["quality_level"] == "high"
|
||
assert guangdong["confidence"] >= 0.8
|
||
|
||
|
||
def test_jiangsu_is_high_quality_province(summary):
|
||
"""江苏已进入 high 白名单。"""
|
||
by_name = {p["province"]: p for p in summary["provinces"]}
|
||
jiangsu = by_name["江苏"]
|
||
assert jiangsu["quality_level"] == "high"
|
||
assert jiangsu["confidence"] >= 0.8
|
||
|
||
|
||
def test_zhejiang_is_high_quality_province(summary):
|
||
"""浙江已进入 high 白名单。"""
|
||
by_name = {p["province"]: p for p in summary["provinces"]}
|
||
zhejiang = by_name["浙江"]
|
||
assert zhejiang["quality_level"] == "high"
|
||
assert zhejiang["confidence"] >= 0.8
|
||
|
||
|
||
def test_fujian_is_high_quality_province(summary):
|
||
"""福建已进入 high 白名单。"""
|
||
by_name = {p["province"]: p for p in summary["provinces"]}
|
||
fujian = by_name["福建"]
|
||
assert fujian["quality_level"] == "high"
|
||
assert fujian["confidence"] >= 0.8
|
||
|
||
|
||
def test_high_population_provinces_not_yet_high_remain_non_high(summary):
|
||
"""仍未进入白名单的高考生源大省必须继续保持 non-high。"""
|
||
by_name = {p["province"]: p for p in summary["provinces"]}
|
||
for province in HIGH_POPULATION_PROVINCES_NOT_YET_HIGH:
|
||
p = by_name.get(province)
|
||
assert p is not None, f"高考生源大省 {province} 不在 31 省列表内"
|
||
assert p["quality_level"] != "high", (
|
||
f"{province} 当前被标为 high,但它不在当前 high 白名单。"
|
||
"如需升级,先补充白名单与审计口径。"
|
||
)
|
||
|
||
|
||
def test_quality_levels_are_valid_enum(summary):
|
||
"""所有 province 的 quality_level 必须是 high / usable / low / skeleton 之一。"""
|
||
valid_levels = {"high", "usable", "low", "skeleton"}
|
||
for p in summary["provinces"]:
|
||
assert p["quality_level"] in valid_levels, (
|
||
f"{p['province']} quality_level={p['quality_level']!r} 不在合法集合"
|
||
)
|
||
|
||
|
||
def test_confidence_values_in_valid_range(summary):
|
||
"""所有 province confidence 必须在 [0.0, 1.0]。"""
|
||
for p in summary["provinces"]:
|
||
c = p["confidence"]
|
||
assert 0.0 <= c <= 1.0, f"{p['province']} confidence={c} 越界"
|
||
|
||
|
||
def test_data_year_is_2025_until_2026_published(summary):
|
||
"""data_year 约定:部分省份已公布 2026 分数线可更新为 2026,其他仍为 2025。
|
||
|
||
过渡期规则(2026-06):
|
||
- 已公布 2026 分数线的省份:data_year=2026
|
||
- 待公布省份:data_year=2025
|
||
- 不得出现非 2025/2026 的年份
|
||
|
||
已确认 2026 分数线公布省份(2026-06-25):
|
||
- 湖南 / 江苏 / 广东 / 山东 / 河北 / 河南 / 浙江 / 四川
|
||
"""
|
||
invalid_provinces = []
|
||
valid_provinces_2026 = {
|
||
"湖南",
|
||
"江苏",
|
||
"广东",
|
||
"山东",
|
||
"河北",
|
||
"河南",
|
||
"浙江",
|
||
"四川",
|
||
}
|
||
|
||
for p in summary["provinces"]:
|
||
year = p["data_year"]
|
||
prov = p["province"]
|
||
if year not in {2025, 2026}:
|
||
invalid_provinces.append((prov, year))
|
||
# 已确认 2026 的省份必须为 2026
|
||
if prov in valid_provinces_2026 and year != 2026:
|
||
invalid_provinces.append((prov, year))
|
||
|
||
assert invalid_provinces == [], (
|
||
f"data_year 仅允许 2025/2026,且已公布省份必须为 2026。"
|
||
f"发现异常: {invalid_provinces}"
|
||
)
|