fix: RANK_INVALID 落地 + 空门槛库不判安全(改判边缘+V1)+ 批量坏文件跳过 + 教授补一作/通讯独立门槛

This commit is contained in:
chenfuqiang
2026-09-27 23:00:21 +08:00
parent 04026d0372
commit b859a89222
8 changed files with 191 additions and 43 deletions

View File

@@ -2,6 +2,9 @@
"""职称硬指标核验内核金标准用例(离线、确定性)。"""
from __future__ import annotations
import json
import os
import tempfile
import unittest
from _support import IsolatedDataRoot
@@ -65,6 +68,33 @@ class TestMatrixColors(VerifyRuleTestBase):
colors = {m["metric_key"]: m["color"] for m in result["matrix"]}
self.assertEqual(colors["teaching_hours"], "green")
def test_yellow_boundary_at_109_percent(self) -> None:
# 1.09 倍(174.4 = 160×1.09,余量 9%)属黄
result = self._verify(_metrics(teachingHours=174.4))
colors = {m["metric_key"]: m["color"] for m in result["matrix"]}
self.assertEqual(colors["teaching_hours"], "yellow")
def test_alias_metric_without_threshold_gets_v1(self) -> None:
# 副教授种子未配置 awards:提供该指标须出 V1 灰行,不得当作已核验跳过
result = self._verify(_metrics(awards=2))
v1 = [m for m in result["matrix"] if m["metric_key"] == "awards"]
self.assertEqual(len(v1), 1)
self.assertIn("V1", v1[0]["advice"])
def test_empty_thresholds_never_safe(self) -> None:
"""该级别门槛库为空 → 一律不判"安全"(V1 灰行 + 边缘结论)。"""
from service.verify_service import verify_metrics
result = verify_metrics(_metrics(), {})
self.assertEqual(result["verdict"], "边缘")
self.assertIn("门槛库未配置", result["summary"])
provided = [m for m in result["matrix"] if m["metric_key"] in ("presiding_provincial", "core_papers")]
self.assertTrue(provided and all(m["color"] == "gray" for m in provided))
bare = verify_metrics({"teacherName": "某人", "applyRank": "副教授"}, {})
self.assertEqual(bare["verdict"], "边缘")
self.assertIn("门槛库未配置", bare["summary"])
def test_r4_missing_metric_pending(self) -> None:
result = self._verify(_metrics(corePapers=None))
self.assertEqual(result["verdict"], "边缘")
@@ -106,6 +136,26 @@ class TestVerifyKernel(VerifyRuleTestBase):
result = self._run([_metrics(rank="教授", presidingProvincial=2, corePapers=7, monographs=2)])
self.assertEqual(result["reports"][0]["verdict"], "边缘") # core 7/6 余量 16.7% 绿、presiding 2/2 黄
def test_rank_invalid_raises(self) -> None:
with self.assertRaises(ValueError) as ctx:
self._run([_metrics(rank="院士", presidingProvincial=0)])
self.assertIn("RANK_INVALID", str(ctx.exception))
def test_professor_first_author_threshold_enforced(self) -> None:
"""教授"一作/通讯 ≥3"为独立门槛:缺数据判待核(边缘),提供后参与判定。"""
from service.verify_service import load_thresholds, verify_metrics
base = _metrics(rank="教授", presidingProvincial=3, corePapers=7, monographs=2, currentTitleYears=6)
without = verify_metrics(base, load_thresholds("教授"))
self.assertEqual(without["verdict"], "边缘")
pending = next(m for m in without["matrix"] if m["metric_key"] == "first_author_core_papers")
self.assertEqual(pending["color"], "gray")
full = verify_metrics(dict(base, firstAuthorCorePapers=4), load_thresholds("教授"))
fa = next(m for m in full["matrix"] if m["metric_key"] == "first_author_core_papers")
self.assertEqual(fa["color"], "green")
self.assertEqual(fa["required"], 3.0)
def test_set_threshold_changes_verdict(self) -> None:
from service.threshold_service import upsert_threshold
@@ -152,5 +202,37 @@ class TestThresholdService(VerifyRuleTestBase):
self.assertTrue(payload["red_cases"])
class TestBatchInputCollection(VerifyRuleTestBase):
"""input/ 目录收集:坏文件跳过并记录原因,不中断整批。"""
def test_collect_input_cases_skips_bad_files(self) -> None:
from service.verify_service import collect_input_cases
with tempfile.TemporaryDirectory() as tmp:
with open(os.path.join(tmp, "a.json"), "w", encoding="utf-8") as f:
json.dump([_metrics(), _metrics(teacherName="李老师")], f, ensure_ascii=False)
with open(os.path.join(tmp, "single.json"), "w", encoding="utf-8") as f:
json.dump({"teacherName": "王老师", "applyRank": "副教授"}, f, ensure_ascii=False)
with open(os.path.join(tmp, "bad.json"), "w", encoding="utf-8") as f:
f.write("{not-json")
with open(os.path.join(tmp, "scalar.json"), "w", encoding="utf-8") as f:
json.dump("just-a-string", f, ensure_ascii=False)
cases, skipped = collect_input_cases(tmp)
names = [c.get("teacherName") for c in cases]
self.assertEqual(names, ["张老师", "李老师", "王老师"])
self.assertEqual({e["file"] for e in skipped}, {"bad.json", "scalar.json"})
self.assertTrue(all(e["reason"] for e in skipped))
def test_collect_input_cases_empty_dir(self) -> None:
from service.verify_service import collect_input_cases
with tempfile.TemporaryDirectory() as tmp:
cases, skipped = collect_input_cases(tmp)
self.assertEqual(cases, [])
self.assertEqual(skipped, [])
if __name__ == "__main__":
unittest.main()