# Røgtest af erindringer-modulet med FastAPI TestClient, uden OIDC: sessionen sættes direkte. # Kør i CT151: PORTAL_DATA=/tmp/ef-test/data + env fra /etc/portal.env. Rører ikke prod-data. import io, json, os, sys from fastapi.testclient import TestClient import main, erindringer c = TestClient(main.app, base_url="https://weircon.dk") adm = {"username": "agw", "name": "Asger", "groups": [main.config.ADMIN_GROUP, "Familie"]} def login(client, user): # Starlette-session: skriv via en lille rute er omstændeligt; monkeypatch current_user i stedet. main.current_user = lambda request: user login(c, adm) r = c.get("/admin/erindringer"); assert r.status_code == 200 and "Erindringer" in r.text, r.status_code r = c.post("/admin/erindringer/projekt", data={"slug": "test-ep", "titel": "Testemne", "beskrivelse": "b", "kontekst": "Personen er Asger. Vi ved at han bor i Sønderlund.", "start": "Hvad husker du fra Rørbæk?\nHvem var din morfar?"}, follow_redirects=False) assert r.status_code == 303, r.text assert erindringer.projekt("test-ep")["start"][0].startswith("Hvad husker") r = c.post("/admin/erindringer/gaest", data={"navn": "Tina Test", "projekter": ["test-ep"]}, follow_redirects=False) assert r.status_code == 303 pid = [p for p in erindringer.alle_personer() if p["navn"] == "Tina Test"][0]["pid"]; noegle = erindringer.person(pid)["noegle"] print("gæst", pid) # API som gæst (uden session) login(c, None) r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": None, "noegle": noegle}); assert r.status_code == 200, r.text q1 = r.json(); print("Q1", q1["spoergsmaal"], "| lyd:", bool(q1["lyd_url"])) if q1["lyd_url"]: assert c.get(q1["lyd_url"]).status_code == 200 r = c.post("/erindringer/api/svar", data={"projekt": "test-ep", "samtale": q1["samtale"], "spoergsmaal": q1["spoergsmaal"], "noegle": noegle, "tekst": "Jeg husker min morfar Gustav i Villa Monika i Hannerup. Han havde en stor have og en motorcykel, som han aldrig kørte på."}) assert r.status_code == 200, r.text; s1 = r.json(); print("svar1", s1["id"]) r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": q1["samtale"], "noegle": noegle}); assert r.status_code == 200, r.text q2 = r.json(); print("Q2 (Claude)", q2["spoergsmaal"], "| talt:", q2["talt"], "| kilde:", q2["kilde"], "| afslut:", q2["afslut"]) assert "[" not in q2["spoergsmaal"] and q2["lyd_sha"] # spring over: det sprungne må ikke komme igen r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": q1["samtale"], "noegle": noegle, "sprunget": [q2["spoergsmaal"]]}); assert r.status_code == 200 assert r.json()["spoergsmaal"] != q2["spoergsmaal"]; print("Q3 efter spring", r.json()["spoergsmaal"], "| kilde:", r.json()["kilde"]) # lydsvar: 2 s tone som mp3 lavet med ffmpeg os.system("ffmpeg -y -v error -f lavfi -i 'sine=frequency=440:duration=2' -c:a libmp3lame /tmp/ef-test/tone.mp3") r = c.post("/erindringer/api/svar", data={"projekt": "test-ep", "samtale": q1["samtale"], "spoergsmaal": q2["spoergsmaal"], "spoergsmaal_talt": q2["talt"], "lyd_sha": q2["lyd_sha"], "kilde": q2["kilde"], "noegle": noegle}, files={"lyd": ("svar.mp3", open("/tmp/ef-test/tone.mp3", "rb"), "audio/mpeg")}) assert r.status_code == 200, r.text; s2 = r.json(); print("svar2 lyd", s2["id"], "varighed", s2["varighed"], "tekst:", repr(s2["tekst"])[:60]) r = c.get(f"/erindringer/g/{noegle}/klip/{s2['id']}.mp3"); assert r.status_code == 200 and r.headers["content-type"].startswith("audio") r = c.get(f"/erindringer/g/{noegle}/klip/{s2['id']}-sp.mp3"); assert r.status_code == 200, "spørgsmålets lyd mangler" k2 = erindringer.klip(pid, s2["id"]); assert k2["spoergsmaal_lyd"] and k2["kilde"] == q2["kilde"] and k2["spoergsmaal_talt"] == q2["talt"] r = c.post("/erindringer/api/ret", json={"id": s1["id"], "tekst": "Rettet tekst.", "noegle": noegle}); assert r.status_code == 200 # kladde-flow: optagelsen sendes op med det samme, afskrift i baggrunden, så bekræftes den import time as _t r = c.post("/erindringer/api/svar", data={"projekt": "test-ep", "samtale": q1["samtale"], "spoergsmaal": "Hvad lavede du i haven?", "kladde": "1", "noegle": noegle}, files={"lyd": ("svar.mp3", open("/tmp/ef-test/tone.mp3", "rb"), "audio/mpeg")}) assert r.status_code == 200, r.text; s3 = r.json(); print("kladde", s3["id"], s3["status"], "svartid ok") assert s3["kladde"] is True and not [k for k in erindringer.klip_for(pid) if k["id"] == s3["id"]], "kladde må ikke tælle med" r = c.post("/erindringer/api/bekraeft", json={"id": s3["id"], "noegle": noegle}); assert r.status_code == 200 for _ in range(40): st = c.post("/erindringer/api/status", json={"id": s3["id"], "noegle": noegle}).json() if st["status"] != "transskriberes": break _t.sleep(1) print("status efter bekræft:", st["status"], repr(st["tekst"])[:40]) assert [k for k in erindringer.klip_for(pid) if k["id"] == s3["id"]], "bekræftet klip skal tælle med" _t.sleep(8) # baggrunden forbereder næste spørgsmål t0 = _t.time(); r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": q1["samtale"], "noegle": noegle}); dt = _t.time() - t0 assert r.status_code == 200; print("næste efter kladde: %.1fs" % dt, r.json()["spoergsmaal"][:60], "| lyd:", bool(r.json()["lyd_url"])) # slet kladde r = c.post("/erindringer/api/svar", data={"projekt": "test-ep", "samtale": q1["samtale"], "spoergsmaal": "x", "kladde": "1", "noegle": noegle}, files={"lyd": ("svar.mp3", open("/tmp/ef-test/tone.mp3", "rb"), "audio/mpeg")}); s4 = r.json() r = c.post("/erindringer/api/slet", json={"id": s4["id"], "noegle": noegle}); assert r.status_code == 200 and erindringer.klip(pid, s4["id"]) is None # dagbog: samtale = dato login(c, adm) r = c.post("/erindringer/api/naeste", json={"projekt": "dagbog", "samtale": None, "noegle": "", "dato": "2026-10-05"}); assert r.status_code == 200 and r.json()["samtale"] == "20261005", r.text r = c.get("/erindringer/alle"); assert r.status_code == 200 and "Alle svar" in r.text login(c, None) assert erindringer.klip(pid, s1["id"])["tekst"] == "Rettet tekst." # forkert nøgle -> 404 r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": None, "noegle": "x" * 40}); assert r.status_code == 404 # indlogget familie: dagbog findes, admin-person-side, eksport login(c, adm) r = c.post("/erindringer/api/naeste", json={"projekt": "dagbog", "samtale": None, "noegle": ""}); assert r.status_code == 200, r.text print("dagbog Q", r.json()["spoergsmaal"]) r = c.get(f"/admin/erindringer/p/{pid}"); assert r.status_code == 200 and "Rettet tekst." in r.text r = c.get(f"/admin/erindringer/p/{pid}/eksport.zip"); assert r.status_code == 200 and r.headers["content-type"] == "application/zip" and len(r.content) > 1000 r = c.post(f"/admin/erindringer/p/{pid}/klip/{s1['id']}", data={"handling": "favorit", "favorit": "1"}, follow_redirects=False); assert r.status_code == 303 assert erindringer.klip(pid, s1["id"])["favorit"] is True r = c.post(f"/admin/erindringer/person/{pid}", data={"handling": "luk"}, follow_redirects=False); assert r.status_code == 303 login(c, None) r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": None, "noegle": noegle}); assert r.status_code == 404 print("ALT OK")