Zum Inhalt springen

Tests und Teststatus

Stand: 2026-10-20. Dieses Dokument trennt klar zwischen automatisiert getestet, live gegen die laufende Docker-Instanz geprĂŒft, mit echter Hardware geprĂŒft und nicht getestet.

Suite Ort Braucht Ergebnis
Unit backend/tests/unit nichts 142 bestanden
Audio mit echten Modellen backend/tests/audio Modelle (JUSTAI_TEST_MODELS_DIR) 9 bestanden; test_speaker.py schlÀgt in etwa 1 von 4 LÀufen fehl (Piper-Synthese ist zufÀllig, fremde Stimme dann 0,49 statt < 0,45)
Datenbank backend/tests/db PostgreSQL + pgvector (JUSTAI_TEST_DATABASE_URL) 3 bestanden
End-to-End backend/tests/e2e laufende Instanz (JUSTAI_E2E_URL, JUSTAI_E2E_PASSWORD), Gemini 13 bestanden (siehe Hinweis)
Beispiel-Plugin plugins/example-weather/tests nichts 6 bestanden
Frontend frontend Node tsc und ESLint fehlerfrei
Statische Analyse ganzes Repo – ruff fehlerfrei, mypy --strict fehlerfrei (Backend, SDK, Sandbox)
Terminal-Fenster
uv run pytest backend/tests/unit
uv run python scripts/fetch_models.py .models
JUSTAI_TEST_MODELS_DIR=$PWD/.models uv run pytest backend/tests/audio
JUSTAI_TEST_DATABASE_URL=postgresql+asyncpg://justai:pw@127.0.0.1:55432/justai_test uv run pytest backend/tests/db
JUSTAI_E2E_URL=http://localhost:8080 JUSTAI_E2E_PASSWORD=
 uv run pytest backend/tests/e2e
uv run pytest plugins/example-weather/tests # getrennt aufrufen (eigene pytest-Konfiguration)
cd frontend && npm run typecheck && npm run lint

Eine Test-Datenbank startet man z. B. mit docker run -d --name j5-testdb -e POSTGRES_USER=justai -e POSTGRES_PASSWORD=pw -e POSTGRES_DB=justai_test -p 127.0.0.1:55432:5432 pgvector/pgvector:0.8.1-pg17.

  • Unit: Event-Bus (Isolation, Duplikate, Hop-Limit), Output Router (alle Regeln), Tool-Executor (Rechte, BestĂ€tigung, Timeout, Fehler), Display-Komponenten, Echo-Bausteine (Referenzpuffer, GCC-PHAT, Tracker), Wakeword-Arbitrierung (inkl. spĂ€ter Duplikate), Kalibrierung (Onset), KI-Router (Fallback, hĂ€ngender Provider, Cooldown, Rollen-Autokonfiguration), GesprĂ€chs-Zustandsautomat, Vision-Logik, Automationsmodell (inkl. ErgĂ€nzung fehlender Typen), PTZ-Regelung, statische PrĂŒfung generierten Codes, Prompts der Plugin-Erzeugung, FakePluginContext (Jobs, Events, HTTP-Routen), fehlende Plugin-Einstellungen, Sprachsteuerung (PrivatsphĂ€re-Modus, Zuhörpause mit Wiedereinschalten), Login-Drosselung, Discovery-ZusammenfĂŒhrung.
  • Audio: SelbstgesprĂ€ch mit echten Modellen in einem simulierten Raum (AUDIO.md).
  • DB: dauerhaftes Vergessen im GedĂ€chtnis (MEMORY.md).
  • E2E: zwei simulierte RĂ€ume mit akustischem Echo, echte lokale STT/TTS/Wakeword, Gemini als LLM: GesprĂ€ch, Raum-Routing, kein Echo, Timeout, doppelte Wakewords, parallele GesprĂ€che, Display-Kopplung mit BestĂ€tigung, proaktive Automation, GesprĂ€ch fortsetzen (gleiche Stimme 0,82 → fortgesetzt, RĂŒckfrage „wie viele Einwohner hat sie?“ richtig auf Rom bezogen; andere Stimme 0,26 → neues GesprĂ€ch), Sprech-Automation startet einen Dialog, Stimmprofil: aus echter GesprĂ€chssprache angelegt (3 Proben), beim nĂ€chsten „Hey Jarvis“ mit gleicher Stimme erkannt (Ähnlichkeit 0,75), andere Stimme nicht. Der Ablauf „im GesprĂ€ch anlegen lassen“ hat beim ersten Lauf funktioniert; Wiederholungen scheiterten am erschöpften Gemini-Kontingent (429), nicht an der Funktion. GerĂ€te-App: ein skriptgesteuertes Browser-GerĂ€t koppelt sich mit Mikrofon, Lautsprecher und Kamera, bekommt automatisch einen Endpoint mit 4 Rollen, liefert Snapshots; „Hey Jarvis“ und eine Frage ĂŒber das WebSocket-Mikrofon starten ein GesprĂ€ch, die Antwort kommt als Audio beim GerĂ€t an.

Hinweis zu E2E: Die Suite nutzt das konfigurierte Cloud-LLM (Gemini). Am 2026-10-15 lieferte Gemini zeitweise gleichzeitig 429 (Kontingent erschöpft) fĂŒr gemini-3.8-flash und 503 (Überlastung) fĂŒr den Fallback. In diesem Fall antwortet der Assistent korrekt mit einer Fehlermeldung, und drei Tests schlugen fehl. Die direkte Wiederholung bestand 5/5, obwohl im selben Zeitraum 8 weitere 429/503-Fehler ĂŒber die Fallback-Kette abgefangen wurden. FĂŒr stabile LĂ€ufe ein lokales LLM als zusĂ€tzlichen Fallback eintragen.

Legende: ✅ nachgewiesen · 🟡 teilweise (was fehlt, steht dabei) · ⏳ offen.

# Kriterium Status Nachweis
1 GerĂ€teerkennung ✅ Netzwerksuche findet die Tapo C200 in 6 s, schlĂ€gt den Tapo-Treiber mit Modell „C200“ vor und erkennt sie als bereits angelegt. Eingerichtet zeigt sie die gemessenen FĂ€higkeiten (Video, Audio rein/raus, Snapshot). PTZ fehlt in der Liste, weil dafĂŒr das Cloud-Passwort fehlt. Das ist korrekt ermittelt, nicht geraten. Die C410 (Akku) war meist im Schlaf.
2 Raumzuordnung ✅ Echte Hardware: C200 (Kamera, Mikrofon, Lautsprecher) als Endpoint „Poolhaus Kamera“ im Raum Poolhaus. E2E: kombinierte GerĂ€te pro Raum.
3 SprachgesprĂ€ch ✅ Echte Hardware: „Hey Jarvis“ an der C200 → Session → Antwort (Gemini Live) ĂŒber den Lautsprecher der C200. E2E: Pipeline-Modus mit Antwort im selben Raum.
4 Keine Echo-Schleife ✅ Echte Hardware (C200): keine Selbstauslösung. Audio-Tests: Antwort enthĂ€lt absichtlich das Wakeword → kein Wake, kein durchgelassenes Echo. E2E: keine Echo-Turns im Transkript.
5 Timeout ✅ Echte Hardware und E2E: Ende durch InaktivitĂ€t (end_reason = inactivity), danach ist wieder ein Wakeword nötig.
6 Keine falsche Raumausgabe ✅ E2E: KĂŒchenlautsprecher bekommt kein einziges Frame, wenn im Wohnzimmer gefragt wird. Unit: Output-Router-Regeln.
7 Doppelte Wakewords ✅ E2E: dasselbe Wakeword in zwei RĂ€umen (leiser in der KĂŒche) → genau eine Session, im lauteren Raum. Unit: Arbitrierung inkl. verspĂ€teter Duplikate. Mit zwei echten Mikrofonen nicht getestet (C410 nicht dauerhaft erreichbar).
8 Parallele GesprĂ€che ✅ E2E: zwei RĂ€ume in getrennten Arbitrierungsgruppen fragen gleichzeitig, jeder bekommt seine Antwort.
9 Personenregistrierung 🟡 Vorstellen per Sprache (identity.introduce), Einwilligung mit Nachweis und Registrierung mit QualitĂ€tsprĂŒfung sind implementiert; identity.register_face verlangt eine ausdrĂŒckliche BestĂ€tigung (Mechanismus unit-getestet). Offen: der Ablauf mit einer echten Person vor der Kamera.
10 Wiedererkennung 🟡 YuNet/SFace sind im Container geladen (Plugin vision-opencv lĂ€uft, GesichtsprĂŒfung in der Vision-Pipeline aktiv); Abstimmung ĂŒber starke und schwache Treffer ist implementiert. Offen: Test mit echter Person.
11 PTZ-Tracking 🟡 Regelung unit-getestet (Totzone, Schrittweite, Rate, Zielbindung, Heimkehr, manuelle Übersteuerung). Echte C200: manuelles Schwenken funktioniert (rechts und zurĂŒck, per Bildvergleich bestĂ€tigt). Offen: Verfolgen einer Person.
12 Erinnerung ✅ Live: „Merk dir 
“ in Session A, richtige Antwort in neuer Session B. Vergessen mit RĂŒckfrage; dauerhaft (DB-Test und live).
13 Automation 🟡 Live: der Satz aus dem Kriterium (mit Personenname) legt eine korrekte Automation an. E2E: das Erkennungs-Event startet im richtigen Raum eine proaktive BegrĂŒĂŸung, once greift. Offen: Auslösung durch eine echte Gesichtserkennung (hĂ€ngt an 9/10).
14 Display-Ausgabe ✅ E2E: Display im Wohnzimmer gekoppelt und gewĂ€hlt. Echter Browser: Dashboard mit Kurs, Chart und Tabelle gerendert.
15 Display-BestĂ€tigung ✅ E2E: das Tool wartet nachweislich auf das ack. Echter Browser: ack nach dem Zeichnen; Hintergrundseite meldet sofort „nicht sichtbar“, statt Erfolg vorzutĂ€uschen.
16 Provider-Wechsel ✅ Live: Rolle conversation nur per Konfiguration auf einen neuen OpenAI-kompatiblen Endpoint umgestellt (Gemini ĂŒber dessen OpenAI-Schnittstelle); die Antwort kam laut Nutzungsprotokoll von oai:gemini-oai. Keine CodeĂ€nderung. Fallback bei Überlastung (503/429) im Betrieb beobachtet.
17 Plugin-Installation ✅ Live: Zip hochgeladen → als untrusted in der Sandbox → Start ohne Rechte abgelehnt → Recht gewĂ€hrt → aktiv, Tool erscheint, der Assistent nutzt es → deaktiviert → Tool verschwindet.
18 Tool-Entwicklung ✅ Live: „Kalenderwoche und Wochentag zu einem Datum“ → Spezifikation, Code, Tests → statische PrĂŒfung → Tests in der Sandbox (2 bestanden) → Sicherheitsreview → Freigabe → der Assistent beantwortet „KW von 24.12.2026?“ korrekt mit dem neuen Tool → Deaktivierung. Live (2026-10-20): „ein Plugin, das alle 5 Minuten prĂŒft, ob es in Bozen zu regnen anfĂ€ngt, und Bescheid sagt“ → Plugin mit Tool, Hintergrundjob und Event-Typ; einmal ĂŒberarbeitet; freigegeben → lĂ€uft in der Sandbox, der Event-Typ plugin.gen-rain-monitor.rain_started steht im Katalog und wird fĂŒr Automationen angeboten. Offen: ein echter Regenbeginn, der eine Automation auslöst.
19 Neustart ✅ docker compose down + up: RĂ€ume, GerĂ€te, Endpoints, Plugins (aktiv), Personen, Erinnerungen, Automationen, KI- und Systemeinstellungen identisch. ZusĂ€tzlich Backup in ein getrenntes Projekt wiederhergestellt.
20 Fehlerbehandlung ✅ Live: die C410 ist meist offline, die C200 arbeitet unbeeinflusst. Neustarts einzelner Dienste: Medienserver → Kameras nach 2 s wieder da; Datenbank → API nach 3 s; Sandbox → Sandbox-Plugins nach 44 s automatisch neu gestartet. Ein fehlschlagendes Plugin (fehlende Rechte) wird failed markiert, alles andere lĂ€uft weiter. Gemini-Überlastung: Fallback statt Ausfall. Unit: Bus-Fehlerisolation, hĂ€ngende Provider.
  • PrivatsphĂ€re-Modus per Sprache (Textchat an die C200): „schalte die Kamera aus“ → PrivatsphĂ€re-Modus an, direkt an der Kamera mit pytapo geprĂŒft; „wieder an“ → aus.
  • Zuhörpause: „hör hier eine Minute nicht zu“ → Wakeword am Endpoint aus, eine einmalige Automation hat es nach 60 s wieder eingeschaltet und lief genau einmal.
  • E2E mit englischem System-Prompt: 13/13 bestanden (Fragen auf Deutsch und Italienisch).
  • 9/10/13 mit echter Person: vor die C200 stellen, „Ich bin 
“ sagen, der Gesichtsregistrierung zustimmen. SpĂ€ter erneut vor die Kamera gehen; die BegrĂŒĂŸungs-Automation sollte dann auslösen.
  • 11: Im GerĂ€t Tapo C200 das Tapo-Cloud-Passwort oder ein Kamerakonto (Tapo-App → Kamera → Erweitert → Kamerakonto) eintragen, dann ptz.follow.
  • Barge-in mit echter Hardware: Die C200 schaltet ihr Mikrofon wĂ€hrend der Wiedergabe stumm, damit ist Unterbrechen physikalisch unmöglich. FĂŒr einen Test braucht es ein GerĂ€t mit echtem Full-Duplex (z. B. USB-Freisprecheinrichtung).
  • Display auf einem Fernseher: bisher nur im Desktop-Browser geprĂŒft.
  • Installationsskript (scripts/install.sh, 2026-10-16, als eigenes Compose-Projekt neben der laufenden Instanz): per curl 
 | bash in leerem Ordner (klont von GitHub, Ports 8080/8443 belegt → 8081/8444, LAN-IP erkannt, zufĂ€lliges Passwort angezeigt und Anmeldung damit erfolgreich); interaktiv in einem Pseudo-Terminal (eigenes Passwort, Anmeldung erfolgreich, .env mit Rechten 600); erneuter Aufruf (behĂ€lt .env, baut neu). HTTPS ĂŒber die LAN-IP mit dem CA-Zertifikat geprĂŒft. Nicht getestet: Docker-Neuinstallation auf einem Rechner ohne Docker.
  • GerĂ€te-App auf echten Handys/Tablets: Setup-Seite, Kopplung und Einstellungskarte im Desktop-Browser geprĂŒft, der Medienweg per E2E-Test. Headless-Chrome mit simulierter Kamera/Mikrofon und strenger Autoplay-Regel: nach dem Koppeln und nach einem Neuladen ohne BerĂŒhrung kommen Kamerabilder und Mikrofon an; das Livebild (MJPEG) lĂ€uft in der Admin-UI. Echo- UnterdrĂŒckung des Browsers, Hintergrundverhalten mobiler Browser und die Zertifikatsinstallation auf Android/iOS sind noch nicht auf echten GerĂ€ten getestet.

Live-PrĂŒfungen gegen die laufende Instanz haben Fehler gefunden, die keine Unit-Tests abgedeckt hatten:

Fehler Behebung
Vergessenes kam durch die GedĂ€chtnis-Extraktion der alten Session zurĂŒck Grabsteine, DB-Test
Dashboard stĂŒrzte nach dem Login ab (_cooldowns als Rolle in /api/ai/status) eigenes Feld, defensive UI
„BegrĂŒĂŸe X das nĂ€chste Mal“: Modell ließ type weg und nutzte den Namen statt der ID; Antwort blieb leer Typen ergĂ€nzen, Namen auflösen, Validierungsdetails an das Modell, Ersatzantwort
Sandbox-Testlauf je nach Zufalls-Verzeichnisname nicht importierbar fester Pfad, pythonpath
Generierter Klassenname umbenannt, Tests fanden ihn nicht Alias statt Umbenennen
Zweites Tool-Projekt mit gleichem Namen brach ab (Unique-Constraint) eindeutige Plugin-IDs
„Wo ist das nĂ€chste McDonald’s?“: der Assistent behauptete, kein Internet zu haben, und lehnte auch auf zweimalige Bitte ab, ein Plugin zu bauen; der System-Prompt erwĂ€hnte tools.develop nicht, eine Websuche gab es nicht Google-Suche von Gemini neben den eigenen Tools (Realtime und Pipeline), Standort des Hauses in den Einstellungen und im Prompt, Prompt-Regel, tools.develop anzubieten und zu starten; Unit-Tests, E2E per Sprache (Realtime), live per Textchat
Plugin-Erzeugung brach ab („Replacement index 0 out of range“) nach der Übersetzung des Prompts geschweifte Klammern im Prompt maskiert, Unit-Test
Tests generierter Plugins schlugen fehl („‘dict’ object is not callable“): das Modell ĂŒbergab JSON direkt als HTTP-Route FakePluginContext akzeptiert dict/list/str/Funktionen als Routen, Unit-Test
Deaktivieren der C200 hing ĂŒber 2 Minuten (GerĂ€t als deaktiviert markiert, Verbindungen blieben) Supervisor.stop wartete unbegrenzt auf Tasks, die auf Abbruch nicht reagieren, und verschluckte dabei den eigenen Abbruch. Jetzt: höchstens 5 s, dann Warnung mit der Stelle, an der der Task hĂ€ngt; jeder Schritt beim Stoppen eines GerĂ€ts (Audio, Vision, Treiber, go2rtc) höchstens 10 s, langsame Schritte werden mit Namen geloggt. Live: Deaktivieren 0,2–0,3 s
Kameralautsprecher (C200) stumm, obwohl alle Bytes an der Kamera ankamen; auch nach go2rtc- und Kamera-Neustart. Eine frische zweite Verbindung und die Tapo-App waren hörbar eigene, pro GesprĂ€ch neu geöffnete Lautsprecher-Verbindung (<stream>_talk), Schließen nach 90 s Stille; ZustellprĂŒfung erkennt jetzt auch Kameraverbindungen, die go2rtc als Consumer fĂŒhrt (AUDIO.md). Testton danach technisch zugestellt (Login 4,1 s)
Erinnerung „in einer Viertelstunde“ wiederholte sich alle 5 s (18× in 1,5 min, dann erst durch das Limit von 20 LĂ€ufen pro Stunde gestoppt) Der Zeitplaner prĂŒfte bei at-Triggern run_count an seiner zwischengespeicherten Kopie, die nur die Datenbank hochzĂ€hlte. Jetzt: einmalig ĂŒber den Auslösezeitpunkt, at-Automationen werden nach dem Lauf deaktiviert, Cache nach jedem Lauf neu geladen. Unit-Test und live geprĂŒft (1 Lauf, danach deaktiviert)
„Hey Jarvis, wie spĂ€t ist es?“ ohne Pause blieb unbeantwortet: Audio zwischen Wakeword und bereiter Session (1,4–1,7 s) wurde verworfen behoben: Puffer pro Endpoint, Wiedergabe ab dem Wakeword mit VAD-Entscheidung Pause/keine Pause, Pipeline ignoriert Kurz-Äußerungen; E2E in beiden Modi (AUDIO.md)
Installation: mit geĂ€ndertem JUSTAI_HTTPS_PORT (z. B. 8444, weil 8443 belegt) lauschte der Core im Container auf 8444, Docker leitete aber auf 8443 weiter – HTTPS nicht erreichbar behoben: Container-Port fest 8443, JUSTAI_HTTPS_PORT nur noch Host-Port und Anzeige
GerĂ€te-App: Livebild in der Admin-UI „nicht verfĂŒgbar“ (fragte MP4 von go2rtc an, App-Kameras haben keinen Stream); nach dem automatischen Neuladen bei Updates keine Kamera/kein Mikrofon mehr, bis jemand tippt behoben: MJPEG-Livebild, Selbststart, Ton per BerĂŒhrung nachholbar; im Browser geprĂŒft
Display: doppelte Achsenbeschriftung, „GesprĂ€ch“-Anzeige durch Text-Chats, unklare Fehler bei unsichtbarer Seite, kein Neuladen nach Updates behoben, im Browser geprĂŒft
Zeitzone der Einstellungen wurde von Automationen ignoriert; Schalter ohne Funktion in den Einstellungen verdrahtet bzw. entfernt
Kein Schutz gegen Passwort-Raten, keine Security-Header Drosselung, CSP u. a.
Discovery schlug fĂŒr Tapo den ONVIF-Treiber vor (ohne Lautsprecher) herstellerspezifischer Treiber hat Vorrang
Nach einem Neustart von go2rtc blieben alle Kameras unerreichbar (Streams nur im Speicher) Watchdog stellt Streams wieder her; live: 2 s
Erste Ausgabe nach einer Pause unhörbar (Kanal nach 2 min geschlossen, Neuaufbau zu langsam) Kanal bleibt offen, Sprache wartet auf Bereitschaft
Ton blieb nach Kamera-Reconnect dauerhaft stumm (go2rtc holte ab, lieferte aber nicht an die Kamera) ZustellungsprĂŒfung an go2rtc, automatischer Neuaufbau

Die beiden Full-Duplex-Tests in backend/tests/audio sind nicht deterministisch, weil Piper die Testsprache bei jedem Lauf leicht anders erzeugt. Im Mittel schlĂ€gt etwa einer von drei bis sechs LĂ€ufen fehl: mal wird eine echte Unterbrechung zu spĂ€t erkannt, mal (seltener seit der Toleranz um die geschĂ€tzte Echoverzögerung) Echo als Unterbrechung gewertet. Half Duplex, der Modus der C200, ist davon nicht betroffen. Offen: reproduzierbare Testsprache und eine Abstimmung ĂŒber viele Varianten.

  • Konsolidierung des GedĂ€chtnisses (ZusammenfĂŒhren, WidersprĂŒche)
  • Realtime-Modus (Gemini Live) nur manuell mit der C200, nicht in der E2E-Suite
  • Plugin-AbhĂ€ngigkeiten und Updates hochgeladener Plugins
  • Retention-Job
  • Lastverhalten mit vielen Kameras