cards/docs/SMOKE_TEST.md
Till e3b3a2b478 docs: SMOKE_TEST.md — verifizierter E2E-Lauf gegen lokale Postgres
Phase-3-Verifikation 2026-05-08:
- Drizzle push erfolgreich: 8 Tabellen, 11 Indizes, 6 FKs in
  Schema `cards`
- E2E-Flow durchgespielt: Deck-Create, Card-Create (basic-reverse,
  2 Auto-Reviews), Reviews-Due, FSRS-Grade (state new→learning,
  stability 0→2.3, learning_steps 0→1), Cross-User-Schutz (403),
  Cascade-Delete (Deck→Cards→Reviews alle 0)
- Type-Check + 46 Vitest-Tests grün

Runbook für reproduzierbaren Lauf in docs/SMOKE_TEST.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 16:43:07 +02:00

111 lines
3.7 KiB
Markdown

# 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":"<ULID>","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":"<DECK_ID>","type":"basic-reverse","fields":{"front":"Q","back":"A"}}'
# → {"id":"<CARD_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/<CARD_ID>/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:<now+10min>
# 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":"<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/<DECK_ID> -H 'X-User-Id: u-test-1'
# → {"deleted":"<DECK_ID>"}
# 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