Files
creator/backend/tests/test_guide_qa.py
2026-07-04 03:25:02 +02:00

107 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Guide-QA: Fehler-Injektion auf Mini-Guide-Karten — deterministisch, ohne LLM."""
import guide_qa as gq
import qa
def _card(block, md):
return {"block": block, "block_norm": block.casefold(), "md": md}
AUSF = ("<!-- section: Alpha -->\n<!-- compact -->\n- m\n<!-- ausführlich -->\n"
"Einstieg in den Block.\n"
"<!-- sub: beginner | Kantenzug Definition -->\n"
"Ein Kantenzug verbindet Knoten über Kanten im Graphen.\n")
def test_ausfuehrlich_extrahiert_lerntext():
assert gq._ausfuehrlich(AUSF).startswith("\nEinstieg")
assert gq._ausfuehrlich("nur text") == "nur text"
def test_marker_fehlend():
cards = [_card("Alpha", AUSF)]
rel = {"alpha": {"kantenzug definition", "fehlender aspekt"}}
out = gq.marker_fehlend(cards, rel)
assert out == ["Alpha · fehlender aspekt"]
def test_ziel_ohne_anker():
cards = [_card("Alpha", AUSF)]
ziele = [{"block_norm": "alpha", "ziel_id": "z1", "text": "Kantenzug im Graphen erklären"},
{"block_norm": "alpha", "ziel_id": "z2", "text": "Adjazenzmatrix aufstellen können"}]
out = gq.ziel_ohne_anker(cards, ziele)
assert len(out) == 1 and "z2" in out[0]
def test_laengen_ausreisser():
duenn = _card("Alpha", "<!-- ausführlich -->\nkurz")
ok = _card("Beta", "<!-- ausführlich -->\n" + "x" * 500)
out = gq.laengen_ausreisser([duenn, ok], {"alpha": {"s1"}, "beta": {"s1"}})
assert [x["block"] for x in out] == ["Alpha"]
def test_redundanz_findet_absatz_doppel():
a = "Der Kantenzug verbindet Knoten über mehrere Kanten und darf Knoten wiederholen. " * 3
b = "Der Kantenzug verbindet Knoten über mehrere Kanten und darf Knoten wiederholen, genau. " * 3
c = "Völlig anderes Thema: Matrizen, Determinanten und lineare Abbildungen im Vektorraum. " * 3
cards = [_card("Alpha", f"<!-- ausführlich -->\n{a}\n\n{c}"),
_card("Beta", f"<!-- ausführlich -->\n{b}")]
out = gq.redundanz(cards)
assert len(out) == 1 and out[0]["a"].startswith("Alpha")
def test_lesbarkeit_fail_open(monkeypatch):
def kaputt(md_by_num):
raise RuntimeError("Modell fehlt")
monkeypatch.setattr(gq.readability, "rate_sections", kaputt)
assert gq.lesbarkeit([_card("Alpha", AUSF)]) == []
def test_note_guide_kalibrierung():
"""Gewicht = Punktabzug bei 100 %: 10 % fachlich falsch × 3.0 → 7.0; ungemessen zählt nicht."""
assert qa.note({"fachlich_falsch": 0.1}, gq.NOTE_GEWICHTE_GUIDE) == 7.0
ohne = {"marker_fehlend": 0.0, "ziel_ohne_anker": 0.0}
assert qa.note(ohne, gq.NOTE_GEWICHTE_GUIDE) == 10.0
def test_marker_escaping_tolerant():
"""Escapte Titel (h\\~2\\~o) sind kein „Marker fehlt" — Writer und DB escapen verschieden."""
md = "<!-- ausführlich -->\n<!-- sub: beginner | ^ und ~ müssen escaped werden (h\\\\~2\\\\~o) -->\nText."
cards = [{"block": "Hoch", "block_norm": "hoch", "md": md}]
rel = {"hoch": {gq._norm_title("^ und ~ müssen escaped werden (h~2~o)")}}
assert gq.marker_fehlend(cards, rel) == []
async def test_fachlich_falsch_braucht_beide_bestaetiger(monkeypatch):
"""Befund zählt nur, wenn BEIDE Bestätiger zustimmen — Einzel-/Zweifach-Urteile
ließen die Note desselben Guides zwischen 2.0 und 6.6 springen."""
import agents
calls = {"n": 0}
async def fake_agent(key, prompt, timeout, **kw):
calls["n"] += 1
if "-fakten-3-" in key: # Bestätiger 2: nur #1 bleibt
return 0, '{"relevant": {"1": "ja"}}', ""
if "-fakten-2-" in key: # Bestätiger 1: #1 und #2
return 0, '{"relevant": {"1": "ja", "2": "ja"}}', ""
return 0, '{"relevant": {"1": "ja", "2": "ja", "3": "nein"}}', "" # Pass 1
monkeypatch.setattr(agents, "run_agent", fake_agent)
cards = [_card("Alpha", AUSF), _card("Beta", AUSF), _card("Gamma", AUSF)]
out = await gq._fachlich_falsch("t", cards)
assert out == ["Alpha"] and calls["n"] == 3
async def test_fachlich_falsch_ohne_verdacht_kein_zweiter_pass(monkeypatch):
import agents
calls = {"n": 0}
async def fake_agent(key, prompt, timeout, **kw):
calls["n"] += 1
return 0, '{"relevant": {"1": "nein", "2": "nein"}}', ""
monkeypatch.setattr(agents, "run_agent", fake_agent)
out = await gq._fachlich_falsch("t", [_card("Alpha", AUSF), _card("Beta", AUSF)])
assert out == [] and calls["n"] == 1