Files
creator2/tests/test_inventar.py
2026-07-10 15:43:11 +02:00

95 lines
4.7 KiB
Python
Raw 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.
"""Anker-Repair: unverankerte Zitate (start=-1) werden erst deterministisch
neu gematcht — ohne LLM-Call."""
import db
import inventar
import llm
from conftest import run_anlegen, topic_anlegen
def test_merge_kollision_und_kette():
topic = topic_anlegen("mergekante")
a = db.insert("atome", topic=topic, titel="A", typ="begriff", definition="lang genug",
status="neu", braucht=db.j([]))
b = db.insert("atome", topic=topic, titel="B", typ="begriff", definition="kurz",
status="neu", braucht=db.j([]))
c = db.insert("atome", topic=topic, titel="C", typ="begriff", definition="x",
status="neu", braucht=db.j([]))
# beide haben dieselbe Kante zu C → blindes Umhängen würde UNIQUE verletzen
db.execute("INSERT INTO kanten(topic, von_atom, zu_atom, art) VALUES(?,?,?,'braucht')",
(topic, a, c))
db.execute("INSERT INTO kanten(topic, von_atom, zu_atom, art) VALUES(?,?,?,'braucht')",
(topic, b, c))
# und eine Kante zwischen den Merge-Partnern → würde Selbstkante
db.execute("INSERT INTO kanten(topic, von_atom, zu_atom, art) VALUES(?,?,?,'braucht')",
(topic, b, a))
inventar._merge(topic, a, b)
kanten = db.query("SELECT * FROM kanten WHERE topic=?", (topic,))
assert len(kanten) == 1 and kanten[0]["von_atom"] == a and kanten[0]["zu_atom"] == c
# Merge-Kette: (B, C) — B ist schon gemerged, Wurzel A übernimmt
inventar._merge(topic, b, c)
assert db.one("SELECT merged_into FROM atome WHERE id=?", (c,))["merged_into"] == a
async def test_anker_rematch_ohne_llm(tmp_path):
topic = topic_anlegen("rematch")
run = run_anlegen(topic)
snap = tmp_path / "q.md"
snap.write_text("Vorher. Der Satz steht hier drin. Nachher.", encoding="utf-8")
q = db.insert("quellen", topic=topic, art="datei", titel="q",
snapshot=str(snap), hash="h", status="atome")
a = db.insert("atome", topic=topic, titel="T", typ="begriff", definition="d",
status="ohne_anker", braucht=db.j([]))
# Extraktion fand das Zitat nicht (Whitespace-Differenz), hat es aber gespeichert
db.insert("anker", atom_id=a, quelle_id=q, start=-1, ende=-1,
zitat="Der Satz steht hier drin.")
ctx = llm.Kontext(run, topic, "minimax")
ctx.ebene = "inventar"
assert await inventar._anker_fixen_batch(ctx, [a]) is True
row = db.one("SELECT * FROM anker WHERE atom_id=? AND start>=0", (a,))
assert row is not None
assert db.one("SELECT status FROM atome WHERE id=?", (a,))["status"] == "neu"
assert db.query("SELECT id FROM events WHERE run_id=?", (run,)) == [] # kein LLM-Call
assert inventar.messen(ctx) == [] or all(
b["art"] != "atom_ohne_anker" for b in inventar.messen(ctx))
def test_titel_kern_faltet_schreibvarianten():
import textkit
assert textkit.titel_kern("ΔTSP2 TourLänge") == textkit.titel_kern("ΔTSP2 Tour-Länge")
assert textkit.titel_kern("FeedbackVertexSet ∈ NP") == textkit.titel_kern("Feedback Vertex Set ∈ NP")
assert textkit.titel_kern("!!!") == ""
def test_fallback_nutzt_jaccard_schwelle(monkeypatch):
import embedding
a = "Polynomialzeitreduktionen sind transitiv wenn L1 auf L2 und L2 auf L3 reduzierbar sind"
b = ("Polynomialzeitreduktionen sind transitiv wenn eine Sprache L1 auf L2"
" und L2 auf L3 in Polynomialzeit reduzierbar ist")
# Kosinus-Schwelle allein: Paraphrase rutscht durch (aak Lauf 8)
assert embedding.kandidaten_paare([a, b], 0.75) == []
paare = embedding.kandidaten_paare([a, b], 0.75, 0.3)
assert [(i, j) for i, j, _ in paare] == [(0, 1)]
async def test_titel_dublette_wird_gemerged_trotz_ferner_definition():
topic = topic_anlegen("titeldup")
run = run_anlegen(topic)
# identischer Titel, Definitionen lexikalisch fern (Jaccard < 0.3):
# nur der Titel-Kern-Generator bringt das Paar ans Panel
a = db.insert("atome", topic=topic, titel="VERTEX COVER Problem", typ="begriff",
definition="Entscheidungsproblem, ob ein Graph ein Vertex Cover"
" der Größe höchstens k enthält.",
status="neu", braucht=db.j([]))
b = db.insert("atome", topic=topic, titel="VERTEX COVER Problem", typ="begriff",
definition="Gefragt wird nach einer Knotenmenge, die jede Kante"
" abdeckt und maximal k Elemente hat.",
status="neu", braucht=db.j([]))
ctx = llm.Kontext(run, topic, "minimax")
ctx.ebene = "inventar"
await inventar._judge_dedup(ctx)
stati = {r["id"]: r["status"] for r in db.query(
"SELECT id, status FROM atome WHERE topic=?", (topic,))}
assert sorted(stati.values()) == ["gemerged", "neu"]