init
This commit is contained in:
85
tests/test_engine.py
Normal file
85
tests/test_engine.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""Engine-Kern: atomarer Claim, Zombie-Reset, Idempotenz, Kanten-Validierung."""
|
||||
import pytest
|
||||
|
||||
from backend import db, engine, graph
|
||||
from .conftest import topic_anlegen
|
||||
|
||||
|
||||
def test_claim_atomar():
|
||||
topic = topic_anlegen()
|
||||
run_id = db.insert("runs", topic=topic, status="running")
|
||||
tid = db.insert("tasks", run_id=run_id, topic=topic, knoten="suche", item="x")
|
||||
assert db.execute("UPDATE tasks SET status='laufend' WHERE id=? AND "
|
||||
"status='offen'", tid) == 1
|
||||
# zweiter Claim verliert
|
||||
assert db.execute("UPDATE tasks SET status='laufend' WHERE id=? AND "
|
||||
"status='offen'", tid) == 0
|
||||
|
||||
|
||||
def test_task_unique_idempotent():
|
||||
topic = topic_anlegen()
|
||||
run_id = db.insert("runs", topic=topic, status="running")
|
||||
a = db.insert("tasks", ignore=True, run_id=run_id, topic=topic,
|
||||
knoten="suche", item="r1:q:abc")
|
||||
b = db.insert("tasks", ignore=True, run_id=run_id, topic=topic,
|
||||
knoten="suche", item="r1:q:abc")
|
||||
assert a is not None and b is None
|
||||
# Runde im Item-Schlüssel: 2. Runde blockiert nicht
|
||||
c = db.insert("tasks", ignore=True, run_id=run_id, topic=topic,
|
||||
knoten="suche", item="r2:q:abc")
|
||||
assert c is not None
|
||||
|
||||
|
||||
async def test_zombie_reset_beim_start():
|
||||
import asyncio
|
||||
topic = topic_anlegen()
|
||||
run_id = db.insert("runs", topic=topic, status="stopped")
|
||||
db.insert("tasks", run_id=run_id, topic=topic, knoten="montage",
|
||||
item="montage", status="laufend")
|
||||
run_id2 = engine.lauf_starten(topic)
|
||||
await asyncio.sleep(0.1)
|
||||
t = db.one("SELECT status, fehler FROM tasks WHERE topic=? AND item='montage'",
|
||||
topic)
|
||||
assert t["status"] != "laufend" or t["fehler"] != "" # Zombie zurückgesetzt
|
||||
engine.lauf_stoppen(topic)
|
||||
with __import__("contextlib").suppress(asyncio.CancelledError):
|
||||
await engine._laeufe[topic]
|
||||
assert run_id2 != run_id
|
||||
|
||||
|
||||
def test_kanten_validierung():
|
||||
g = graph.get()
|
||||
kn = g.knoten["suche"]
|
||||
t = {"id": 1, "run_id": 1, "topic": "x", "knoten": "suche", "item": "i",
|
||||
"runde": 1}
|
||||
erg = engine.Ergebnis(neue_tasks=[{"knoten": "montage", "item": "m"}])
|
||||
with pytest.raises(graph.GraphFehler):
|
||||
engine._abschluss_schreiben(g, t, kn, erg)
|
||||
|
||||
|
||||
async def test_infra_zaehlt_nicht_gegen_versuch():
|
||||
"""LaufPause aus einem Worker setzt den Task zurück auf offen (kein versuch+1)
|
||||
und pausiert den Run."""
|
||||
import asyncio
|
||||
|
||||
async def kaputt(task):
|
||||
from backend.llm import LaufPause
|
||||
raise LaufPause("test")
|
||||
|
||||
topic = topic_anlegen()
|
||||
run_id = db.insert("runs", topic=topic, status="running")
|
||||
tid = db.insert("tasks", run_id=run_id, topic=topic, knoten="suche",
|
||||
item="x", status="laufend", versuch=0)
|
||||
g = graph.get()
|
||||
alt = engine.WORKER["suche.suchen"]
|
||||
engine.WORKER["suche.suchen"] = kaputt
|
||||
try:
|
||||
t = dict(db.one("SELECT * FROM tasks WHERE id=?", tid))
|
||||
await engine._ausfuehren(g, t, g.knoten["suche"], asyncio.Semaphore(1),
|
||||
asyncio.Semaphore(1), asyncio.Event())
|
||||
finally:
|
||||
engine.WORKER["suche.suchen"] = alt
|
||||
task = db.one("SELECT * FROM tasks WHERE id=?", tid)
|
||||
assert task["status"] == "offen"
|
||||
assert task["versuch"] == 0 # Infra zählt nicht als Inhalts-Versuch
|
||||
assert db.one("SELECT status FROM runs WHERE id=?", run_id)["status"] == "paused"
|
||||
Reference in New Issue
Block a user