Files
gaokao-volunteer-system/data/crowd_db/tests/test_crowd_db_data_quality.py
hermes 6eafe1fc9b
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(ops 6/20 v2.1.3): 生产加固 + L-A 送审前修复 + crowd_db 质量契约
## 实现内容 (6 项改动)

1. **admin /health 端点增强** — 主键契约 status:ok 保持 + checks 子对象
   - db_writable: connect + CREATE TEMP TABLE + INSERT + SELECT + DROP
   - disk_writable: 在 ops_alert_log_path 目录创建临时文件 + 删除
   - settings_valid: 复用 is_jwt_secret_secure 判断
2. **_enforce_jwt_secret_policy** — prod env 使用 dev 默认 JWT / 长度<32 → fail-closed
3. **_enforce_default_admin_password_policy** — prod env 用 admin123 / 长度<10 / 字符类<3 → fail-closed
4. **L-A R7: admin UI footer** — dashboard.html (592 行) + ui.py 内联 admin/orders/new
   模板加 footer 隐私政策 + 数据删除 + 服务说明链接, 与 portal _render_footer_links() 同口径
5. **L-A R1+R4: LEGAL_PRIVACY_BASELINE 文档同步**
   - §6 移除孤儿 'admin' consent_channel 值 (代码侧从未实际产生)
   - §7 已具备/尚缺 重写, 显式归到 6/20 增量
6. **Q-A: tests/test_crowd_db_data_quality.py** — 8 个测试锁住
   - 27 省总数 + 仅湖南 high + 其它 26 省 ≤ usable
   - 高考生源大省 (广东/江苏/北京/上海/山东/河南/四川/湖北) 不在 high 集合
   - data_year=2025 (6/25 后需显式更新)

## 测试

- 4/4 RED → GREEN (admin/tests/test_health.py: JWT/admin password 拒绝 dev 默认值)
- 8/8 GREEN (data/crowd_db/tests/test_crowd_db_data_quality.py: 数据质量契约)
- 3 回归修复 (test_app.py + test_routes.py + test_health.py 适配新 /health checks 字段)
- 31/31 GREEN (admin/tests/test_app.py + test_routes.py + test_health.py)

## 报告

- reports/LA_LEGAL_PRIVACY_PRE_AUDIT_2026-06-20.md (363 行, 9 风险 0 阻塞)
- reports/QA_CROWD_DB_NON_HUNAN_DENSITY_AUDIT.md (45 行, CRITICAL 文档失真已规避)

## 文件

M CHANGELOG.md (v2.1.3)
M admin/config.py (2 个 _enforce_*_policy + load_settings post-load)
M admin/routes/health.py (3 个 _check_* + checks 子对象)
M admin/routes/ui.py (admin/orders/new 模板加 footer)
M admin/static/dashboard.html (footer 块)
M admin/tests/test_app.py (适配 checks 字段 + regex 兼容)
M admin/tests/test_health.py (4 个新测试)
M admin/tests/test_routes.py (适配 checks 字段)
M docs/CURRENT_STATE.md (0.3-0.5 增量段)
M docs/LEGAL_PRIVACY_BASELINE.md (§6 清理 + §7 重写)
+ data/crowd_db/tests/test_crowd_db_data_quality.py (8 tests)
+ reports/LA_LEGAL_PRIVACY_PRE_AUDIT_2026-06-20.md
+ reports/QA_CROWD_DB_NON_HUNAN_DENSITY_AUDIT.md
2026-06-20 18:36:23 +08:00

133 lines
5.2 KiB
Python

"""crowd_db 数据质量契约测试 (6/20 Q-A 闭环).
CROWD_DB_DATA_QUALITY.md §7 承诺的锁死文件, 实际仓库中缺失。
本测试锁住以下契约:
- 27 省总数 (23 省 + 4 直辖市, 不含 5 自治区/港澳台)
- 湖南为 high (A级) 数据, confidence >= 0.8
- 其它 26 省质量分级不超过 skeleton (C级) 或 usable (B级)
- 高考生源大省 (广东/江苏/北京/上海/山东/河南/四川) 不在 high 集合
- 新增 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
# 高考生源大省 (按历年统计 top 8) — 6/20 Q-A 审计明确这些省当前都是 skeleton
# 任何升级到 usable / high 必须显式更新本测试, 避免回归到"27 省高置信"假象
HIGH_POPULATION_PROVINCES = frozenset({
"广东",
"江苏",
"北京",
"上海",
"山东",
"河南",
"四川",
"湖北",
})
@pytest.fixture(scope="module")
def summary():
return build_quality_summary(CrowdDBLoader(warn_low_confidence=False))
def test_total_provinces_is_27(summary):
"""6/20 真相: 27 个 JSON (23 省 + 4 直辖市), 不含 5 自治区/港澳台。"""
assert summary["total_provinces"] == 27
assert len(summary["provinces"]) == 27
def test_hunan_is_the_only_high_quality_province(summary):
"""湖南是唯一 high 级 (A级) 数据, 其它 26 省不能跳跃升级。
锁住这一点防止:
1. 有人手工把 0.45 的省标成 high (合规/对外口径风险)
2. 后续数据补充时静默升级 (CROWD_DB_DATA_QUALITY §2 明确要求分级流程)
"""
high_provinces = [
p["province"] for p in summary["provinces"] if p["quality_level"] == "high"
]
assert high_provinces == ["湖南"], (
f"预期仅湖南为 high, 实际: {high_provinces}"
"新增 high 省份必须显式更新本测试, 避免'27 省高置信'假象。"
)
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_hunan_provinces_not_high(summary):
"""非湖南 26 省 quality_level 不能是 high。
显式枚举, 不依赖 quality_level 集合是否包含 high (避免有人偷偷改白名单)。
"""
non_hunan = [p for p in summary["provinces"] if p["province"] != "湖南"]
assert len(non_hunan) == 26
leaked = [p["province"] for p in non_hunan if p["quality_level"] == "high"]
assert leaked == [], (
f"以下省份被错标为 high (仅湖南应为 high): {leaked}"
"如需新增 high, 同步更新 test_hunan_is_the_only_high_quality_province"
)
def test_high_population_provinces_acknowledged_as_skeleton_or_lower(summary):
"""广东/江苏/北京/上海/山东/河南/四川/湖北 8 个高考生源大省当前都是 skeleton/usable,
不在 high 集合。
Q-A 审计发现这些省当前 confidence=0.45 / 1-3 分数段 / 2-8 条推荐, 不构成强推荐。
后续扩到 usable/high 必须显式更新本测试, 防止"静默升级"
"""
by_name = {p["province"]: p for p in summary["provinces"]}
for province in HIGH_POPULATION_PROVINCES:
p = by_name.get(province)
assert p is not None, f"高考生源大省 {province} 不在 27 省列表内"
assert p["quality_level"] != "high", (
f"{province} 当前被标为 high, 与 Q-A 6/20 审计事实不符。"
"如数据已升级, 同步更新 test_hunan_is_the_only_high_quality_province"
" 并补充 Q-A 复审报告。"
)
def test_quality_levels_are_valid_enum(summary):
"""所有 province 的 quality_level 必须是 high / usable / skeleton 之一。"""
valid_levels = {"high", "usable", "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):
"""6/20 处于 2026 高考季真空期: 所有文件 data_year=2025。
6/25 后真实 2026 录取数据公布, 本测试需同步更新。
锁住这一点防止:
1. 有人用 2024 旧数据假装 2026 (招生政策已变)
2. 有人提前编造 2026 模拟数据 (合规风险)
"""
for p in summary["provinces"]:
assert p["data_year"] == 2025, (
f"{p['province']} data_year={p['data_year']} 偏离 2025 基线。"
"如 6/25 后 2026 数据已公布, 显式更新本测试。"
)