# 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