107 lines
5.0 KiB
Python
107 lines
5.0 KiB
Python
"""Lesemodus-Endpoints: Baustein-Karten (nicht faellig-gefiltert), Lernstand, fertig."""
|
||
|
||
import db
|
||
import main
|
||
from conftest import topic_anlegen
|
||
|
||
|
||
def _setup(topic):
|
||
b = db.insert("bausteine", topic=topic, ziel_id=1, titel="B", ord=0, status="neu")
|
||
a = db.insert("atome", topic=topic, titel="A", typ="begriff", definition="d",
|
||
status="neu", baustein_id=b, ord=0)
|
||
db.insert("artefakte", atom_id=a, typ="flashcard", status="verifiziert",
|
||
inhalt=db.j({"frage": "F1", "antwort": "A1"}))
|
||
db.insert("artefakte", atom_id=a, typ="flashcard", status="kandidat", # unverifiziert
|
||
inhalt=db.j({"frage": "F2", "antwort": "A2"}))
|
||
db.insert("artefakte", atom_id=a, typ="beispiel", status="verifiziert", # kein Flashcard
|
||
inhalt=db.j({"form": "text", "text": "bsp"}))
|
||
return b, a
|
||
|
||
|
||
def test_baustein_karten_nur_verifizierte_flashcards():
|
||
topic = topic_anlegen("lm1")
|
||
b, _ = _setup(topic)
|
||
karten = main.baustein_karten(topic, b)
|
||
assert len(karten) == 1 # nur die verifizierte Flashcard
|
||
assert karten[0]["inhalt"]["frage"] == "F1"
|
||
assert karten[0]["box"] == 1 # Default ohne Leitner-Eintrag
|
||
|
||
|
||
def test_baustein_karten_absolvierte_bleiben_draussen():
|
||
"""Richtig beantwortet (faellig in Zukunft) → nicht im Stapel; alle=True zeigt sie."""
|
||
topic = topic_anlegen("lm-leitner")
|
||
b, _ = _setup(topic)
|
||
kid = main.baustein_karten(topic, b)[0]["id"]
|
||
db.execute("INSERT INTO leitner(artefakt_id, box, faellig)"
|
||
" VALUES(?, 2, date('now', '+1 day'))", (kid,))
|
||
assert main.baustein_karten(topic, b) == []
|
||
assert len(main.baustein_karten(topic, b, alle=True)) == 1
|
||
# faellig heute (falsch beantwortet) → wieder im Stapel
|
||
db.execute("UPDATE leitner SET faellig=date('now') WHERE artefakt_id=?", (kid,))
|
||
assert len(main.baustein_karten(topic, b)) == 1
|
||
|
||
|
||
def _aussage(topic, atom_id, paar, wahr, text):
|
||
return db.insert("artefakte", atom_id=atom_id, typ="aussage", status="verifiziert",
|
||
inhalt=db.j({"text": text, "wahr": wahr, "paar": paar,
|
||
"erklaerung": "die wahre Fassung"}))
|
||
|
||
|
||
def test_pruefung_panels_anti_cheat_und_paar_regel():
|
||
topic = topic_anlegen("pf1")
|
||
b, a = _setup(topic)
|
||
for p in range(4): # 4 Paare = 8 Aussagen → mind. 1 volles Panel möglich
|
||
_aussage(topic, a, f"{a}-{p}", True, f"wahr {p}")
|
||
_aussage(topic, a, f"{a}-{p}", False, f"falsch {p}")
|
||
panels = main.pruefung_holen(topic, b)
|
||
assert panels and all(len(p["panel"]) == 4 for p in panels)
|
||
for p in panels:
|
||
for x in p["panel"]:
|
||
assert set(x) == {"id", "text"} # kein wahr/erklaerung (Anti-Cheat)
|
||
paare = [db.uj(db.one("SELECT inhalt FROM artefakte WHERE id=?", (x["id"],))
|
||
["inhalt"], {}).get("paar") for x in p["panel"]]
|
||
assert len(set(paare)) == 4 # kein Paar-Partner im selben Panel
|
||
|
||
|
||
def test_pruefung_scoring_exakte_teilmenge():
|
||
topic = topic_anlegen("pf2")
|
||
b, a = _setup(topic)
|
||
ids_wahr = [_aussage(topic, a, f"{a}-{p}", True, f"w{p}") for p in range(2)]
|
||
ids_falsch = [_aussage(topic, a, f"{a}-{p+2}", False, f"f{p}") for p in range(2)]
|
||
ids = ids_wahr + ids_falsch
|
||
r = main.pruefung_antwort(topic, main.PruefungAntwort(ids=ids, angekreuzt=ids_wahr))
|
||
assert r["richtig"] and r["xp"] == 1
|
||
assert all(set(x) == {"id", "wahr", "erklaerung"} for x in r["aufloesung"])
|
||
# eine falsche angekreuzt → −1, Boden 0
|
||
r = main.pruefung_antwort(topic, main.PruefungAntwort(
|
||
ids=ids, angekreuzt=ids_wahr + ids_falsch[:1]))
|
||
assert not r["richtig"] and r["xp"] == 0
|
||
# Schwelle → fertig + lernstand-Status
|
||
for _ in range(10):
|
||
r = main.pruefung_antwort(topic, main.PruefungAntwort(ids=ids, angekreuzt=ids_wahr))
|
||
assert r["fertig"]
|
||
stand = [s for s in main.lernstand_holen(topic) if s["baustein_id"] == b][0]
|
||
assert stand["status"] == "fertig"
|
||
|
||
|
||
def test_check_migration_erlaubt_aussage():
|
||
import sqlite3
|
||
con = sqlite3.connect(":memory:")
|
||
con.execute("CREATE TABLE artefakte(id INTEGER PRIMARY KEY, atom_id INTEGER NOT NULL,"
|
||
" typ TEXT NOT NULL CHECK(typ IN ('flashcard','beispiel')),"
|
||
" inhalt TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'kandidat')")
|
||
con.execute("INSERT INTO artefakte(atom_id, typ, inhalt) VALUES(1,'flashcard','{}')")
|
||
db._init_schema(con) # muss den CHECK erweitern und Daten erhalten
|
||
con.execute("INSERT INTO artefakte(atom_id, typ, inhalt) VALUES(1,'aussage','{}')")
|
||
assert con.execute("SELECT COUNT(*) FROM artefakte").fetchone()[0] == 2
|
||
|
||
|
||
def test_lernstand_und_fertig_idempotent():
|
||
topic = topic_anlegen("lm2")
|
||
b, _ = _setup(topic)
|
||
assert main.lernstand_holen(topic) == []
|
||
main.baustein_fertig(topic, b)
|
||
assert main.lernstand_holen(topic) == [{"baustein_id": b, "status": "fertig", "xp": 0}]
|
||
main.baustein_fertig(topic, b) # zweimal → kein Duplikat
|
||
assert len(main.lernstand_holen(topic)) == 1
|