diff --git a/docs/SMOKE_TEST.md b/docs/SMOKE_TEST.md new file mode 100644 index 0000000..e1d6317 --- /dev/null +++ b/docs/SMOKE_TEST.md @@ -0,0 +1,111 @@ +# Smoke-Test Runbook + +Reproduzierbarer End-to-End-Lauf für `cards-api` ohne Frontend. +Validiert: Drizzle-Schema-Push, Auth-Gate, CRUD, FSRS-Berechnung, +Cascade-Delete. + +## Voraussetzungen + +- Docker läuft +- pnpm 9.x installiert +- Bun installiert (`brew install bun` oder via nvm) +- `pnpm install` einmalig im Repo-Root durchgelaufen + +## Ablauf + +```bash +# 1. Postgres-Container starten (auf :5435, kollidiert nicht mit Mana-Plattform-:5432) +pnpm docker:up + +# 2. Drizzle-Schema pushen (--force, weil non-interactive) +cd apps/api +DATABASE_URL='postgresql://cards:cards@localhost:5435/cards' pnpm exec drizzle-kit push --force +# Erwartet: 8 Tabellen + 11 Indizes + 6 Foreign-Keys in Schema `cards` + +# 3. cards-api starten (auf :3081) +DATABASE_URL='postgresql://cards:cards@localhost:5435/cards' CARDS_API_PORT=3081 bun run src/index.ts & +APIPID=$! + +# Warten bis ready +until curl -s http://localhost:3081/healthz > /dev/null; do sleep 0.5; done +``` + +## Verifizierte Endpoints + +```bash +# Plattform-Endpunkte +curl http://localhost:3081/healthz +# → {"status":"ok"} + +curl http://localhost:3081/version +# → {"app":"cards","version":"0.0.0","build":"dev"} + +curl http://localhost:3081/.well-known/mana-app.json | jq '.id' +# → "cards" + +# Auth-Gate +curl -i http://localhost:3081/api/v1/decks +# → HTTP/1.1 401 Unauthorized +# → {"error":"unauthenticated","detail":"X-User-Id header missing (dev stub)"} + +# Deck CRUD +curl -X POST http://localhost:3081/api/v1/decks \ + -H 'X-User-Id: u-test-1' \ + -H 'Content-Type: application/json' \ + -d '{"name":"Deck-Name","color":"#ff8800"}' +# → {"id":"","user_id":"u-test-1","name":"Deck-Name",...} + +# Card mit basic-reverse → automatisch 2 Reviews initialisiert +curl -X POST http://localhost:3081/api/v1/cards \ + -H 'X-User-Id: u-test-1' \ + -H 'Content-Type: application/json' \ + -d '{"deck_id":"","type":"basic-reverse","fields":{"front":"Q","back":"A"}}' +# → {"id":"",...} + +# Reviews fällig (zwei sub_indices) +curl -H 'X-User-Id: u-test-1' http://localhost:3081/api/v1/reviews/due +# → {"reviews":[{...sub_index:0,state:"new"},{...sub_index:1,state:"new"}],"total":2} + +# Grade triggert FSRS-Berechnung +curl -X POST http://localhost:3081/api/v1/reviews//0/grade \ + -H 'X-User-Id: u-test-1' \ + -H 'Content-Type: application/json' \ + -d '{"rating":"good"}' +# → state:"learning", stability:2.3..., learning_steps:1, due: + +# Cross-User-Schutz +curl -X POST http://localhost:3081/api/v1/cards \ + -H 'X-User-Id: u-attacker' \ + -H 'Content-Type: application/json' \ + -d '{"deck_id":"","type":"basic","fields":{"front":"Q","back":"A"}}' +# → {"error":"deck_not_owned"} (HTTP 403) + +# Cascade-Delete (DELETE deck → Cards weg → Reviews weg) +curl -X DELETE http://localhost:3081/api/v1/decks/ -H 'X-User-Id: u-test-1' +# → {"deleted":""} + +# Verifikation per SQL +docker exec cards-postgres psql -U cards -d cards -c "SELECT count(*) FROM cards.decks, cards.cards, cards.reviews;" +# → alle 0 (Cascade hat alles entfernt) +``` + +## Aufräumen + +```bash +kill $APIPID +pnpm docker:down +# Lokale DB-Daten in infrastructure/.volumes/cards-postgres bleiben. +# Komplett zurücksetzen: rm -rf infrastructure/.volumes/cards-postgres +``` + +## Letzter erfolgreicher Lauf + +**2026-05-08, Phase 3-Verifikation:** +- pnpm install: 136 packages, 8s +- type-check: 4/4 packages grün (svelte-check 0 errors) +- vitest: 46 Tests grün +- drizzle push: 8 Tabellen, 11 Indizes, 6 FKs in `cards`-Schema +- E2E-Flow: Deck-Create → Card-Create (basic-reverse, 2 Reviews) → + Reviews-Due (2 Einträge) → Grade rating=good (state new→learning, + stability 0→2.3, learning_steps 0→1) → 1 Review fällig → Cascade- + Delete leert alle Tabellen