feat: add question_units helper as pair contract entry point

build_units/flatten_units/validate_units/unit_correctness——pair 契约唯一入口。
build_units 对孤儿/超员/角色缺失重复 fail-fast raise ValueError(防 next 静默
StopIteration);unit_correctness 走 per_q[qid] KeyError 防静默兜底。
This commit is contained in:
2026-07-15 05:52:30 -04:00
parent 7ef9b99217
commit bef46636fe
2 changed files with 170 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
"""question_units helper 单元测试:组装/展开/校验/单元正确性。
覆盖 pair 契约唯一入口的核心行为:
- build_units 聚合孪生对、保留 single
- 孤儿 pair、角色缺失/重复直接报错(fail-fast);
- flatten_units 无损展开;
- unit_correctness 对 pair 走 AND、缺题走 KeyError(防静默)。
"""
import pytest
from app.harness.question_units import (
build_units,
flatten_units,
unit_correctness,
validate_units,
)
from core.types import GeneratedQuestion
def _q(qid: str, role: str = "single", pid: str | None = None) -> GeneratedQuestion:
"""构造测试题目;pid 非空时视为孪生对成员。"""
return GeneratedQuestion(
question_id=qid,
video_id="v",
task_type="AR",
question="?",
options=("A. a", "B. b", "C. c", "D. d"),
answer="A",
source_nodes=("n",),
difficulty="hard",
pair_id=pid,
question_role=role,
unit_id=pid or qid,
flip_axis="ax" if pid else None,
)
def test_build_units_groups_pair_and_keeps_single():
qs = [_q("s1"), _q("po", "pair_original", "p"), _q("pm", "pair_mirror", "p")]
units = build_units(qs)
kinds = sorted(u.kind for u in units)
assert kinds == ["pair", "single"]
def test_validate_units_rejects_orphan_pair():
with pytest.raises(ValueError):
validate_units(build_units([_q("po", "pair_original", "p")])) # 只 1 条
def test_flatten_roundtrip():
qs = [_q("po", "pair_original", "p"), _q("pm", "pair_mirror", "p")]
assert {q.question_id for q in flatten_units(build_units(qs))} == {"po", "pm"}
def test_unit_correctness_bidirectional_and():
qs = [_q("po", "pair_original", "p"), _q("pm", "pair_mirror", "p")]
u = build_units(qs)[0]
assert unit_correctness(u, {"po": True, "pm": True}) is True
assert unit_correctness(u, {"po": True, "pm": False}) is False # AND
def test_build_units_rejects_duplicate_role():
qs = [_q("po1", "pair_original", "p"), _q("po2", "pair_original", "p")]
with pytest.raises(ValueError):
build_units(qs)
def test_unit_correctness_missing_question_raises_keyerror():
qs = [_q("po", "pair_original", "p"), _q("pm", "pair_mirror", "p")]
u = build_units(qs)[0]
with pytest.raises(KeyError):
unit_correctness(u, {"po": True}) # 缺 pm → KeyError 防静默