Files
neural-hunt/TESTING.md
groot 7fb7960608
Some checks failed
release-tag / release-image (push) Failing after 1m14s
RC-2
2026-08-10 06:30:40 +02:00

335 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```text
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. `/admin`**TASK** → 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
```bash
go run ./cmd/client -url http://127.0.0.1:8080
```
Prüfen:
```text
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
```bash
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:
```bash
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:
```bash
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:
```bash
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:
```bash
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:
```text
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
```bash
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
```env
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:
```text
/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:
```env
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:
```env
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
```bash
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:
```bash
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`.