2026-06-28 15:17:42 +08:00
|
|
|
|
"""特殊批次定向培养计划模块测试。"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
from data.crowd_db.special_programs_loader import SpecialProgramsLoader
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_DATA_PATH = Path(__file__).resolve().parent.parent / "special_programs.json"
|
|
|
|
|
|
_RULES_PATH = (
|
|
|
|
|
|
Path(__file__).resolve().parents[3]
|
|
|
|
|
|
/ "data"
|
|
|
|
|
|
/ "rules"
|
|
|
|
|
|
/ "special_programs_rules.json"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestSpecialProgramsLoader:
|
|
|
|
|
|
def setup_method(self):
|
|
|
|
|
|
self.loader = SpecialProgramsLoader(
|
|
|
|
|
|
data_path=_DATA_PATH, rules_path=_RULES_PATH
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-06-28 22:12:28 +08:00
|
|
|
|
def test_loads_8_program_types(self):
|
2026-06-28 15:17:42 +08:00
|
|
|
|
programs = self.loader.list_programs()
|
|
|
|
|
|
types = {p["program_type"] for p in programs}
|
2026-06-29 00:31:22 +08:00
|
|
|
|
expected = {
|
|
|
|
|
|
"rural_medical",
|
|
|
|
|
|
"public_agriculture",
|
|
|
|
|
|
"fire_rescue",
|
|
|
|
|
|
"railway_directed",
|
|
|
|
|
|
"judicial_directed",
|
|
|
|
|
|
"military_nco",
|
|
|
|
|
|
"public_teacher",
|
|
|
|
|
|
"enterprise_order",
|
|
|
|
|
|
"minority_prep",
|
|
|
|
|
|
"targeted_poverty",
|
|
|
|
|
|
"tibet_directed",
|
2026-06-29 00:44:06 +08:00
|
|
|
|
"strong_foundation",
|
2026-06-29 00:31:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
for t in expected:
|
|
|
|
|
|
assert t in types, f"缺少项目类型: {t}"
|
2026-06-28 15:17:42 +08:00
|
|
|
|
|
|
|
|
|
|
def test_get_program(self):
|
|
|
|
|
|
p = self.loader.get_program("rural_medical")
|
|
|
|
|
|
assert p is not None
|
|
|
|
|
|
assert p["program_name"] == "农村订单定向免费医学生"
|
|
|
|
|
|
assert p["key_features"]["service_years"] == 6
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_program_not_found(self):
|
|
|
|
|
|
assert self.loader.get_program("nonexistent") is None
|
|
|
|
|
|
|
|
|
|
|
|
def test_list_programs_for_hunan(self):
|
|
|
|
|
|
programs = self.loader.list_programs_for_province("湖南")
|
|
|
|
|
|
types = {p["program_type"] for p in programs}
|
|
|
|
|
|
# 湖南应该有 rural_medical(applicable_provinces 含"湖南")
|
|
|
|
|
|
# fire_rescue/railway_directed/judicial_directed 是"全国"
|
|
|
|
|
|
assert "rural_medical" in types
|
|
|
|
|
|
assert "fire_rescue" in types # 全国
|
|
|
|
|
|
assert "railway_directed" in types # 全国
|
|
|
|
|
|
|
|
|
|
|
|
def test_find_matching_schools_for_hunan_578(self):
|
|
|
|
|
|
"""湖南 578 分应该能匹配到铁路/消防/医疗等院校。"""
|
|
|
|
|
|
results = self.loader.find_matching_schools("湖南", 578)
|
|
|
|
|
|
assert len(results) > 0
|
|
|
|
|
|
# 应包含铁路(湖南铁道职业技术学院 340 分)
|
|
|
|
|
|
types = {r["program_type"] for r in results}
|
|
|
|
|
|
assert "railway_directed" in types
|
|
|
|
|
|
|
|
|
|
|
|
def test_find_matching_schools_low_score(self):
|
|
|
|
|
|
"""低分考生(250)应该匹配到铁路专科。"""
|
|
|
|
|
|
results = self.loader.find_matching_schools("湖北", 250)
|
|
|
|
|
|
# 武汉铁路职业技术学院 223 分
|
|
|
|
|
|
railway = [r for r in results if r["program_type"] == "railway_directed"]
|
|
|
|
|
|
assert len(railway) > 0
|
|
|
|
|
|
assert any("武汉铁路" in r["school"] for r in railway)
|
|
|
|
|
|
|
|
|
|
|
|
def test_find_matching_schools_high_score_excludes_far(self):
|
|
|
|
|
|
"""高分考生不应匹配到分数差距过大的院校。"""
|
|
|
|
|
|
results = self.loader.find_matching_schools("湖南", 650)
|
|
|
|
|
|
# 650 分不应匹配到 223 分的武汉铁路(差距 427 分,223*0.85=189.55)
|
|
|
|
|
|
# 但仍然匹配(因为 650 >= 189.55),只是 gap 很大
|
|
|
|
|
|
# 验证至少不报错
|
|
|
|
|
|
assert isinstance(results, list)
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_applicable_rules(self):
|
|
|
|
|
|
rules = self.loader.get_applicable_rules("rural_medical")
|
|
|
|
|
|
rule_ids = {r["rule_id"] for r in rules}
|
|
|
|
|
|
assert "special.rural_medical.hukou_requirement" in rule_ids
|
|
|
|
|
|
assert "special.rural_medical.service_commitment" in rule_ids
|
|
|
|
|
|
assert "special.general.tuition_free" in rule_ids
|
|
|
|
|
|
|
|
|
|
|
|
def test_build_recommendation_for_review(self):
|
|
|
|
|
|
"""审核场景推荐摘要。"""
|
|
|
|
|
|
recs = self.loader.build_recommendation_for_review("湖南", 450)
|
|
|
|
|
|
assert len(recs) > 0
|
|
|
|
|
|
# 450 分应该能匹配到定向医疗(444/441分)
|
|
|
|
|
|
types = {r["program_type"] for r in recs}
|
|
|
|
|
|
assert "rural_medical" in types
|
|
|
|
|
|
# 每条都有 match_score
|
|
|
|
|
|
for r in recs:
|
|
|
|
|
|
assert "match_score" in r
|
|
|
|
|
|
assert 0 <= r["match_score"] <= 100
|
|
|
|
|
|
|
|
|
|
|
|
def test_build_recommendation_no_match(self):
|
|
|
|
|
|
"""不存在的省份只返回全国性项目(fire/railway/judicial),不报错。"""
|
|
|
|
|
|
recs = self.loader.build_recommendation_for_review("火星", 500)
|
|
|
|
|
|
# 全国性项目仍可能返回,但不应报错
|
|
|
|
|
|
assert isinstance(recs, list)
|
|
|
|
|
|
|
|
|
|
|
|
def test_rules_loaded(self):
|
|
|
|
|
|
rules = self.loader.rules
|
|
|
|
|
|
assert "rules" in rules
|
|
|
|
|
|
assert len(rules["rules"]) >= 9
|