Files
neural-hunt/TESTING.md
2026-08-10 16:20:59 +02:00

18 KiB
Raw Blame History

Validation — V2.5.1 Reconnect / Presence / Heartbeat

Regression: regelmäßiger HTTP 409 nach ~90 Sekunden

  1. Browser-Client mit einem aktiven Task verbinden und mindestens 3 Minuten ohne F5 laufen lassen.
  2. Prüfen, dass Auto-Guesses über die frühere 90-Sekunden-Grenze hinweg weiterlaufen.
  3. DevTools → Network → WS beobachten: der Server sendet echte WebSocket-Ping-Control-Frames; Browser-Pongs verlängern die Read-Deadline.
  4. Netzwerk für einige Sekunden trennen und wieder aktivieren. Der Browser muss automatisch reconnecten und ohne F5 weitertippen.
  5. Während eines Task-Wechsels bzw. schnellen Reconnects darf ein verspätetes ReleasePresence der alten Verbindung die neue Presence nicht entfernen. Das wird zusätzlich durch TestPresenceReconnectOldReleaseCannotDeleteNewLease geprüft.
  6. Shell-Client ebenfalls mindestens 3 Minuten laufen lassen, Verbindung kurz unterbrechen und prüfen, dass [ws] wieder verbunden erscheint und Guessing fortgesetzt wird.

Maschinenlesbare 409-Codes:

presence_required
sequence_mismatch
task_config_changed
selection_conflict
task_inactive

V2.5 — Task-Auswahl, Vererbung und Shell-Client

Browser-Landing-Page

  1. Server mit mindestens zwei aktiven Tasks starten (ACTIVE_TASK_COUNT=2).
  2. / in einem neuen Browserprofil öffnen.
  3. Prüfen, dass vor der 3D-Ansicht die Task-Landing-Page erscheint.
  4. Einen Task wählen und prüfen, dass WebSocket/Auto-Guess starten.
  5. TASKS wählen, einen anderen Task anklicken und prüfen, dass keine identity already connected-Meldung entsteht.
  6. Browser neu laden: die serverseitig zuletzt gewählte Task-Karte muss als ausgewählt markiert sein.

Task-spezifisches NFT-Prompt + Admin-Draft

  1. /adminTASK → Task auswählen.
  2. Anzeigename, Beschreibung, NFT-Prompt-Anweisungen und Negative Prompt eintippen, noch nicht speichern.
  3. Mindestens einen Auto-Refresh-Zyklus (>3 s) abwarten und danach F5 drücken.
  4. Prüfen, dass die noch nicht gespeicherten Eingaben weiter im Formular stehen.
  5. TASK-KONFIG SPEICHERN verwenden und anschließend neu laden; nun müssen die Werte vom Server kommen.
  6. Task gewinnen oder über Admin close beenden.
  7. Folge-Task öffnen und prüfen, dass range_bits, task-spezifische Intervalle, Anzeigename, Beschreibung und beide Prompt-Felder identisch geerbt wurden.

Shell-Client interaktiv

go run ./cmd/client -url http://127.0.0.1:8080

Prüfen:

tasks
status
map 25
leaderboard 20
nfts 10
use 2
status

Der Shell-Client muss weiter automatisch Tipps senden. Nach use darf der Server die neue WebSocket-Verbindung nicht wegen der alten Presence ablehnen.

Shell-Client unbeaufsichtigt

go run ./cmd/client \
  -url http://127.0.0.1:8080 \
  -non-interactive \
  -task 1 \
  -max-nodes 250

Mindestens zwei Tippintervalle laufen lassen und im Admin prüfen, dass Guess-/Score-Aktivität vorhanden ist. Dann Ctrl+C; die Presence muss freigegeben werden.

Browser-/Shell-Identity-Portabilität

Im Browser eine Identity exportieren. Danach Browser-Tab schließen, damit die Single-Connection-Lease frei ist:

export NEURALHUNT_IDENTITY_PASSPHRASE='test-passphrase'
go run ./cmd/client \
  -import ./neuralhunt-identity-....json \
  -identity ./tmp-shell-identity.json

Die Client-ID muss mit der Browser-ID übereinstimmen.

Umgekehrt:

export NEURALHUNT_IDENTITY_PASSPHRASE='test-passphrase'
go run ./cmd/client \
  -identity ./tmp-shell-identity.json \
  -export ./tmp-browser-import.json

Diese Datei anschließend über Import in einem Browserprofil laden; die Client-ID muss identisch bleiben.

Automatische Tests

Zusätzlich zu den bisherigen Tests:

go test ./cmd/client

enthält PBKDF2-Testvektor sowie Raw-/verschlüsselten Identity-Roundtrip. internal/data/successor_test.go prüft mit einem echten SQLite-Treiber die Task-Vererbung, Idempotenz des Nachfolgers und das atomare Migrieren der Client-Auswahl.

In dieser Erstellungsumgebung erfolgreich ausgeführt:

node --check internal/webui/dist/app.js

gofmt -w cmd/client/*.go internal/data/store.go internal/server/server.go internal/artifact/worker.go

# kompletter Source-Compile mit lokalen API-Stubs für die drei nicht ladbaren Module
go test -run '^$' ./...

# echte Shell-Client-Krypto-/Identity-Tests; nur der WebSocket-Import ist dabei gestubbt
go test ./cmd/client

Zusätzlich wurde eine V2.4-artige SQLite-Datenbank mit Python/SQLite angelegt und die Upgrade-Reihenfolge aus OpenSQLite() reproduziert. Dabei wurden die fünf neuen Task-Spalten, client_task_selection und der erst nach parent_task_id angelegte tasks_parent_unique_idx erfolgreich erzeugt. Das ist relevant, weil der Index bei Bestandsdatenbanken nicht vor dem ALTER TABLE angelegt werden darf.

Ergebnis dieser ausführbaren Prüfungen: erfolgreich.

Statisch geprüft / implementiert

  • UI ist weiterhin vollständig unter internal/webui/dist/ eingebettet; kein npm/Vite-Build nötig.
  • /leaderboard ist eine SPA-Route und besitzt einen öffentlichen Live-WebSocket.
  • Mobile Mode ist für Client, Leaderboard und Admin implementiert; Admin bietet mobile MAP/TASKS/CONTROL-Navigation.
  • Das Leaderboard lädt Gewinner-Artefakte ausschließlich über /api/public/artifacts/{task}/preview.
  • Die öffentlichen Leaderboard-Daten enthalten NFT-Anzahl und letzte Wasserzeichen-Preview, aber keine Original-Artifact-URI.
  • Der frühere öffentliche /artifacts/*-File-Server ist entfernt; Original und Manifest benötigen ein Admin-JWT.
  • Raster-Previews werden serverseitig mit einem eingebrannten Bitmap-Wasserzeichen neu als PNG codiert; SVG-Previews erhalten eine wiederholte Wasserzeichenebene.
  • TARGET FIELD bildet Score monoton und direkt auf den Orbitalradius ab; Kameratiefe kann die wahrgenommene Nähe nicht mehr umkehren.
  • Score-Ringe: 0 / 25 / 50 / 75 / 90 / 95 / 99+; Vorder- und Rückhälfte werden zur Tiefenwahrnehmung unterschiedlich gerendert.
  • Pseudozufällige Client-zu-Client-Synapsen sind vollständig entfernt.
  • SIGNALWEGE verbinden ausschließlich Top-Kandidaten/eigenen Client mit dem Task-Core; Score-Verbesserungen erzeugen nur entlang dieses realen Pfads Signalimpulse.
  • Client lädt bei task_changed Range Bits, Seed, Revision, Intervalle und Punkte neu.
  • Client-Autosubmit verwendet keinen statischen setInterval mehr, sondern die jeweils aktuelle Task-Konfiguration.
  • Task-Aktionen werden in task_actions persistiert, jede Sekunde geprüft und mit Status/Audit gespeichert.
  • Alte SQLite-Datenbanken erhalten die neuen tasks-Spalten per ALTER TABLE-Migration.
  • set_range_bits unterstützt preserve und reroll.
  • Task-spezifische Tippintervalle überschreiben globale Runtime-Defaults.
  • OpenAI-, ComfyUI- und A1111-Provider speichern nur lokal erzeugte Ergebnisbytes + Manifest; kein S3 notwendig.
  • OpenAI-Provider validiert für gpt-image-2 die dokumentierten Dimensionsgrenzen, bevor ein Request gesendet wird.
  • Provider-Secrets/URLs werden nicht über die Runtime-Settings-API exponiert.
  • ComfyUI unterstützt Workflow-Platzhalter für Prompt/Negative Prompt/Seed/Größe/Steps/Modell.
  • A1111 unterstützt optional Basic Auth und Checkpoint-Override pro Request.

In dieser Umgebung nicht vollständig ausführbar

go test ./... und go mod tidy können hier nicht abgeschlossen werden, weil ausgehender DNS/Netzwerkzugriff auf proxy.golang.org gesperrt ist. Der Versuch scheitert beim Download dieser bereits im go.mod referenzierten Module:

modernc.org/sqlite
github.com/go-chi/chi/v5
github.com/gorilla/websocket

Dadurch konnten internal/data, internal/server, internal/ws und das komplette Binary hier nicht gegen frisch heruntergeladene Dependencies gebaut werden. Die direkt von ihnen unabhängigen geänderten Go-Packages wurden kompiliert/getestet.

Docker ist in der Erstellungsumgebung ebenfalls nicht verfügbar.

Empfohlene Tests auf deinem Rechner

go mod tidy
go test ./...
go run ./cmd/server

Dann:

  1. / öffnen und prüfen, dass TARGET FIELD standardmäßig aktiv ist. Einen Score-90-, Score-95- und Score-99-Punkt vergleichen: 99 muss unabhängig von Orbit/Neigung sichtbar auf dem kleinsten Ring liegen.
  2. ORBIT laufen lassen und die Ansicht ziehen: Die Reihenfolge der Ringradien darf sich durch Kamerabewegung nicht umkehren.
  3. SIGNALWEGE einschalten: Es dürfen keine Client→Client-Kanten erscheinen; nur ausgewählte Client→Task-Pfade sind erlaubt.
  4. Zwei unabhängige Browserprofile auf 127.0.0.1 verbinden.
  5. /leaderboard in einem dritten Tab öffnen; Punktänderungen sollen ohne manuelles Reload erscheinen.
  6. Im Admin-Bereich einen aktiven Task auswählen und pause sofort ausführen. Clients müssen PAUSE anzeigen und keine Tipps senden.
  7. resume ausführen; Autosubmit muss mit dem aktuellen Client-Intervall weiterlaufen.
  8. set_range_bits im Modus preserve erhöhen; Client muss neue Bitzahl ohne Reload verwenden.
  9. set_range_bits im Modus reroll ausführen; Punkte/Scores müssen auf 0 zurückfallen und neuer Seed/Revision aktiv werden.
  10. Eine Range-Bit-Änderung fünf Minuten in die Zukunft planen und anschließend abbrechen; Status cancelled prüfen.
  11. Zwei geplante Aktionen hintereinander setzen, z. B. 24 Bit und später 32 Bit, und Ausführung/Audit prüfen.
  12. Einen kleinen Zahlenraum verwenden, einen Task lösen und lokales SVG + Manifest prüfen.
  13. /leaderboard öffnen: das Gewinner-Artefakt muss in Galerie und Ranking nur als Wasserzeichen-Vorschau erscheinen.
  14. Direkter Aufruf der früheren /artifacts/...-URI muss 404 liefern; Original/Manifest aus dem Admin-Dashboard müssen weiterhin nach Authentifizierung öffnen.
  15. Browser auf <850 px setzen oder MOBILE aktivieren: Client-Details müssen einklappbar sein; im Admin müssen MAP/TASKS/CONTROL einzeln erreichbar sein.

OpenAI Provider

ARTIFACT_PROVIDER=openai
ARTIFACT_MODEL=gpt-image-2
OPENAI_API_KEY=...
ARTIFACT_WIDTH=1024
ARTIFACT_HEIGHT=1024
ARTIFACT_QUALITY=medium

Task lösen bzw. bei einem abgeschlossenen Task NFT-Bild neu erzeugen ausführen. Erwartet:

/data/artifacts/artifact_<id>/image.png
/data/artifacts/artifact_<id>/manifest.json

ComfyUI Provider

ComfyUI starten, API-Workflow exportieren und im Workflow mindestens {{PROMPT}} sowie optional die anderen Platzhalter verwenden. Danach:

ARTIFACT_PROVIDER=comfyui
COMFYUI_URL=http://127.0.0.1:8188
COMFYUI_WORKFLOW_PATH=/path/workflow-api.json
ARTIFACT_MODEL=checkpoint.safetensors

AUTOMATIC1111 Provider

A1111 mit --api starten:

ARTIFACT_PROVIDER=a1111
A1111_URL=http://127.0.0.1:7860
ARTIFACT_MODEL=checkpoint.safetensors

Bei --api-auth user:pass zusätzlich A1111_USER und A1111_PASSWORD setzen.

Regressionen aus früheren Fixes

  • Auth challenge/login toleriert optionale JWK-Metadaten (alg, use, kid etc.).
  • Mehrere Challenges derselben Identität überschreiben einander nicht.
  • Eine Browser-ID darf weiterhin nur eine aktive Client-WebSocket-Verbindung besitzen.
  • Leere Listen werden als [], nicht als null, an die UI geliefert.
  • Client-Punkt wird direkt beim WebSocket-Connect erzeugt.

Additional v2 checks

  • SQLite schema parsed successfully with Python sqlite3, including task_actions and the live task override columns.
  • The legacy migrations/001_init.sql mirror was synchronized with internal/data/schema.sql so manual schema inspection does not show an obsolete layout.
  • Responsive CSS keeps the Admin Task Actions / Artifact control plane visible on common laptop widths (8511280px).

V2.1 package-integrity regression

The V2 archive was missing internal/data/, which caused an old store.go to survive when users extracted over a previous checkout. That produced compile errors such as missing Task.GuessMinIntervalSec, Task.Paused, LiveLeaderboard, and TaskAction. V2.1 explicitly includes:

  • internal/data/store.go
  • internal/data/schema.sql
  • the matching V2 internal/server/server.go

Always test from a clean extraction directory.

V2.2 zusätzliche Validierung

  • node --check internal/webui/dist/app.js erfolgreich.
  • go test ./... wurde zusätzlich gegen lokale Compile-Stubs für chi, gorilla/websocket und modernc.org/sqlite ausgeführt, um alle Neural-Hunt-Packages inklusive internal/server/watermark_test.go ohne Netzwerkzugriff zu kompilieren.
  • watermark_test.go prüft Traversal-Abwehr, SVG-Wasserzeichen und Raster-Wasserzeichen.
  • Die erweiterten Leaderboard-/Artifact-SQL-Abfragen wurden gegen das reale schema.sql mit Python sqlite3 ausgeführt.

V2.4 Scale smoke test

node --check internal/webui/dist/app.js
go test ./internal/runtime ./internal/ws ./internal/server ./cmd/loadtest

# Server in Terminal 1
go run ./cmd/server

# Start small, then increase.
go run ./cmd/loadtest -url http://127.0.0.1:8080 -clients 250 -ramp 10s -duration 1m -max-nodes 100
go run ./cmd/loadtest -url http://127.0.0.1:8080 -clients 1000 -ramp 30s -duration 2m -max-nodes 100

Im Admin unter /admin sollten dabei Guess/s, Improve/s, SQLite W/s, WS Frames/s, WS MB/s, Drops/s, Goroutines und Heap MB live aktualisiert werden. Bei einem stabilen Test sollten Drops/s nahe 0 bleiben. Ein niedriger SQLite W/s relativ zu Guess/s bestätigt, dass verlierende Tipps den DB-Hotpath nicht mehr belasten.

V2.5.2 Admin form stability regression

The periodic 3-second admin telemetry refresh must never rebuild #settingfields. Only explicit task/tab changes or completed admin actions may refresh the control plane.

Manual regression:

  1. Open /admin and select ARTIFACT.
  2. Open the Provider or Quality native select and leave it open for more than 6 seconds.
  3. The dropdown must remain open while Overview/Performance counters continue updating.
  4. Type into Prompt-Zusatz for more than 6 seconds; caret position and textarea scroll position must not jump.
  5. Switch tabs and back; the draft value must still be restored.

Static/syntax checks used for this release:

node --check internal/webui/dist/app.js
go test ./internal/webui ./internal/core ./internal/auth ./internal/artifact ./internal/settings

RIFT Medium + OpenAI usage/cost telemetry

  • Start with ARTIFACT_PRESET=raccoon_full_art_v1 and no explicit ARTIFACT_QUALITY: the effective quality must be medium.
  • Generate one RIFT winner artifact and verify that the OpenAI request sends quality=medium.
  • Verify that artifact_api_usage receives one artifact row per successful winner-image API call and a character_anchor row when the canonical anchor is created. A successful call is logged even if the provider omits its optional usage block; in that case token counts remain zero and estimated_cost_usd is NULL.
  • When OpenAI returns usage data, verify that input_tokens, text/image input split, output_tokens, total_tokens, estimated_cost_usd, and pricing_basis are persisted and mirrored in the artifact manifest/provider metadata.
  • In Admin → Artifact, verify KOSTEN HEUTE, Ø KOSTEN PRO KARTE, KOSTEN PRO 1.000 KARTEN, and the recent-call table. Anchor cost belongs to today's total but must not affect the per-card average.
  • Verify GET /api/admin/artifact/usage?day_start_ms=<local-midnight-ms> returns the same aggregates.

RIFT V2.8 — per-task style references + manual anchor

  • In Admin → ARTIFACT, with OPENAI_API_KEY configured and no existing _collection/character_anchor.png, click RIFT-ANCHOR JETZT ERZEUGEN. Verify exactly one OpenAI image-generation request is made, the file is stored at data/artifacts/_collection/character_anchor.png, the usage row has kind=character_anchor, and the UI changes to LOCKED without an overwrite button.
  • Verify a second direct POST /api/admin/artifact/character-anchor returns HTTP 409 while the anchor exists.
  • In Admin → TASK ACTIONS upload a JPEG/PNG style reference. Verify it is stored content-addressed under data/artifacts/_styles/, tasks.nft_style_reference receives only the basename, and the admin preview shows the uploaded image.
  • Verify the public Task landing card displays /api/public/tasks/<id>/style-reference and marks a custom task as TASK STYLE; tasks without an upload display the bundled default reference.
  • Complete a task and verify its successor inherits nft_style_reference.
  • Generate a RIFT winner card and inspect the multipart OpenAI edit request: image[] must contain two files in this order: character_anchor.png, then the task style reference. Provider metadata should contain reference_mode=character-plus-task-style, the character-anchor hash and the style-reference hash.
  • Use AUF DEFAULT ZURÜCK and verify the task DB reference is empty and generation falls back to internal/artifact/assets/style_reference.jpg without deleting shared content-addressed style files.

V3.1 — Admin cleanup for stale non-winner profiles

  • In Admin → RUNTIME set e.g. 30 Tage and click PRÜFEN. Verify the preview reports only clients whose clients.last_seen is older than the cutoff, that are not currently connected, and that have never appeared as tasks.winner_client_id.
  • Keep an old client connected via WebSocket: it must be reported as aktuell verbunden geschützt and never be deleted.
  • Create an old winner identity: it must be reported as Gewinner geschützt and never be deleted, regardless of age.
  • Confirm deletion and verify the client row is removed together with cascading task_points, client_unlocks, and client_task_selection rows.
  • Verify a recent non-winner remains untouched.
  • Verify WebSocket connect and disconnect update clients.last_seen, so a long-running session starts its inactivity window at disconnect rather than at its original login.
  • The API rejects cleanup windows shorter than one hour.

Relevant automated tests: internal/data/profile_cleanup_test.go and internal/server/profile_cleanup_test.go.

V3.5 Random Guess Lottery

  1. Admin → RUNTIME: Lotterie-Zeitfenster (s)=20, Max. gezogene Tipps je Task/Fenster=2 setzen und speichern.
  2. Mindestens drei Clients mit demselben Task verbinden und innerhalb desselben Fensters je einen Tipp absenden lassen.
  3. Bis zum Fensterende müssen die Requests auf die Losziehung warten. Danach dürfen höchstens zwei Clients Tipp gezogen & geprüft sehen; übrige Clients sehen Tipp diesmal nicht gezogen.
  4. Admin-Telemetrie: Reject/s steigt für nicht gezogene Tipps; Guess/s zählt nur tatsächlich gezogene/ausgewertete Tipps.
  5. Mit Max. gezogene Tipps je Task/Fenster=0 speichern: Tipps müssen wieder ohne Lotterie-Verzögerung normal verarbeitet werden.
  6. Bei mehreren aktiven Tasks prüfen, dass jeder Task sein eigenes Kontingent erhält.