Files
card-game/SOP.md

12 KiB
Raw Permalink Blame History

SOP.md — Prototipo Gioco Carte (Server Docker + Client Test)

Obiettivo: realizzare un prototipo end-to-end dove:

  • Utente si registra/login con email+password
  • Ha una valuta (Gold) con cui apre bauli
  • Aprendo un baule ottiene carte con rarità
  • La collezione è "unique-only": la carta viene salvata solo se non già posseduta
    • Se la carta è già posseduta: conversione in Gold (o altra regola definita)
  • I giocatori possono vedere la collezione pubblica di altri giocatori
  • Server e DB girano su Docker
  • Esiste un client temporaneo testabile (CLI o Postman) per test E2E

0) Decisioni bloccanti (DA DEFINIRE PRIMA DI CODIFICARE)

Compilare questa sezione PRIMA di procedere.

  • STACK_BACKEND: TODO (FastAPI / NestJS)
  • DB: PostgreSQL (default)
  • ORM: TODO (SQLAlchemy+Alembic / Prisma / TypeORM)
  • AUTH: TODO (JWT access + refresh / solo access)
  • CLIENT_TEST: TODO (CLI Python / Postman / entrambi)
  • CHEST:
    • gold_initial: 1000
    • chest_cost: 100
    • cards_per_open: 3
    • rarities: Common 70%, Rare 25%, Legendary 5%
    • duplicate_conversion_gold: 20
  • PRIVACY:
    • collection_public_default: true

Regola: se un valore è “TODO” non procedere oltre senza definirlo.


1) Regole Architetturali (NON negoziabili)

  • Tutta la logica RNG e assegnazione premi sta nel backend (mai nel client).
  • Il backend è lunica sorgente di verità. Il client visualizza e chiama API.
  • Lapertura baule deve essere:
    • atomica (transazione DB)
    • idempotente (retry rete non deve duplicare premi né spesa)
  • La collezione non deve avere duplicati:
    • vincolo DB: PRIMARY KEY (user_id, card_id)

2) Convenzioni Repo e Commit

2.1 Struttura repository

Creare repo con questa struttura:

card-game/

  • backend/
  • client-test/ (CLI o Postman)
  • infra/
  • spec/
  • docker-compose.yml
  • .env.example
  • README.md
  • SOP.md

2.2 Regole commit

  • Ogni step sotto corrisponde a 1 commit (o più commit piccoli) con un messaggio chiaro:
    • feat: ...
    • test: ...
    • chore: ...
  • Ogni commit deve avere un “Gate” (test di verifica) che deve passare.

3) Stack locale con Docker (Backend + DB)

3.1 File obbligatori

  • docker-compose.yml
  • backend/Dockerfile
  • backend/.dockerignore
  • .env.example

3.2 Variabili ambiente minime (.env)

Definire:

  • APP_ENV=local
  • DB_HOST=db
  • DB_PORT=5432
  • DB_NAME=cardgame
  • DB_USER=cardgame
  • DB_PASSWORD=cardgame
  • JWT_SECRET=change-me
  • JWT_ACCESS_TTL_MIN=15
  • JWT_REFRESH_TTL_DAYS=30

Non committare mai .env vero. Solo .env.example.


STEPS (commit-based)

STEP 01 — Bootstrap repo + Docker “Hello World”

Obiettivo

  • docker compose up --build avvia tutto
  • backend espone GET /health{ "status": "ok" }

Azioni

  1. Creare docker-compose.yml con:
    • service db (postgres)
    • service api (backend)
  2. Implementare endpoint /health
  3. Documentare README con comandi minimi

Output

  • docker-compose.yml
  • backend/Dockerfile
  • backend/app/main.(py|ts)
  • README.md

Commit

  • feat: bootstrap docker compose and health endpoint

Gate / Test

  • Comando: docker compose up --build
  • Verifica:
    • curl http://localhost:8000/health
    • Risposta deve essere {"status":"ok"}

STEP 02 — Connessione DB + endpoint db-check

Obiettivo

Backend connesso realmente a Postgres.

Azioni

  1. Aggiungere driver DB e configurazione.
  2. Implementare GET /db-check che esegue SELECT 1.

Commit

  • feat: add database connection and db-check endpoint

Gate / Test

  • curl http://localhost:8000/db-check
  • Risposta: { "db": "ok" }

STEP 03 — Schema DB v1 (tabelle core)

Obiettivo

Creare schema DB minimo completo per il prototipo.

Tabelle richieste (v1)

users

  • id (uuid / serial)
  • email (unique, not null)
  • password_hash
  • created_at

user_profiles

  • user_id (PK/FK users.id)
  • nickname (unique, not null)
  • is_collection_public (bool, default true)
  • created_at

wallets

  • user_id (PK/FK)
  • balance (int, not null)

wallet_transactions

  • id
  • user_id
  • type (CREDIT/DEBIT)
  • amount
  • reason (REGISTER_BONUS, CHEST_OPEN, DUPLICATE_CONVERT, etc.)
  • reference_id (nullable, e.g. chest_open_id)
  • created_at
  • id (string or uuid)
  • name
  • rarity (COMMON/RARE/LEGENDARY)
  • created_at
  • id
  • name
  • cost_gold
  • cards_per_open
  • created_at

user_cards (collezione unique-only)

  • user_id
  • card_id
  • obtained_at
  • PRIMARY KEY (user_id, card_id)

chest_opens (audit)

  • id
  • user_id
  • chest_id
  • spent_gold
  • created_at

chest_open_items (audit dettagli)

  • id
  • chest_open_id
  • card_id (nullable se conversion-only)
  • rarity
  • outcome_type (NEW / CONVERTED)
  • converted_gold (nullable)
  • created_at

idempotency_keys

  • key (string)
  • user_id
  • endpoint (e.g. "POST:/chests/{id}/open")
  • response_body (json/text)
  • created_at
  • UNIQUE (key, user_id, endpoint)

Azioni

  • Implementare migrazioni (consigliato) o create_all (solo prototipo).
  • Assicurare vincoli unici e FK.

Commit

  • feat: add database schema v1

Gate / Test

  • Avviare stack e verificare che tabelle esistano (psql o query).
  • Verificare vincolo UNIQUE email e nickname.
  • Verificare PK composita su user_cards.

STEP 04 — Seed catalogo (cards + chests)

Obiettivo

Popolare catalogo con dati minimi.

Azioni

  • Inserire almeno:
    • 10 cards (mix rarità)
    • 1 chest base (cost=100, cards_per_open=3)
  • Seed deve essere ripetibile (non duplicare se rilanciato).

Commit

  • feat: seed initial catalog (cards, chests)

Gate / Test

  • GET /catalog/cards ritorna lista > 0
  • GET /catalog/chests ritorna lista > 0

STEP 05 — Auth: register + login (email/password)

Obiettivo

Utente può registrarsi e loggarsi.

Azioni

Implementare:

  • POST /auth/register:
    • valida email e password (minimo)
    • hash password
    • crea user
    • crea profile con nickname (TODO: decidere quando si setta nickname)
    • crea wallet con gold_initial
    • crea wallet_transaction REGISTER_BONUS
  • POST /auth/login:
    • verifica credenziali
    • emette token (access + refresh se previsto)

Nota: nickname

  • Opzione A: richiesto in register (consigliato per social immediato)
  • Opzione B: scelto dopo login (richiede endpoint /me/profile update)

Commit

  • feat: implement register and login

Gate / Test (manuale)

  • Register nuovo: 201
  • Register email già usata: 409
  • Login corretto: 200 + token
  • Login password errata: 401

STEP 06 — Endpoint me: wallet + collection + profile

Obiettivo

Utente autenticato può vedere:

  • saldo
  • collezione
  • profilo

Azioni

Implementare:

  • GET /me/wallet
  • GET /me/collection
  • GET /me/profile

Commit

  • feat: add me endpoints (wallet, collection, profile)

Gate / Test

  • Dopo register/login:
    • wallet = gold_initial
    • collection = []
    • profile contiene nickname

STEP 07 — Core: Open Chest (transazione + unique-only + conversione)

Obiettivo

Endpoint che apre il baule con logica corretta e audit completo.

Endpoint

  • POST /chests/{chestId}/open Header:
  • Authorization: Bearer <access_token>
  • Idempotency-Key:

Algoritmo (obbligatorio)

Dentro una transazione DB:

  1. Validare chestId
  2. Verificare saldo >= cost
  3. Debit saldo (wallet)
  4. Per i=1..cards_per_open: 4.1) Roll rarità secondo pesi 4.2) Selezionare una carta casuale tra quelle di quella rarità NON possedute dallutente - Se pool vuoto: - outcome = CONVERTED (converted_gold = duplicate_conversion_gold) - credit wallet - wallet_transaction DUPLICATE_CONVERT - Se pool non vuoto: - inserire in user_cards (PK impedisce dup) - outcome = NEW
  5. Salvare chest_opens + chest_open_items
  6. Commit

Idempotenza

  • Se la stessa Idempotency-Key per lo stesso user+endpoint è già stata usata:
    • ritornare ESATTAMENTE la response salvata
    • NON scalare saldo
    • NON creare nuovi record

Commit

  • feat: implement open chest with transaction, unique-only, conversion, audit

Gate / Test (manuale)

Caso A (happy path):

  • wallet iniziale 1000
  • open chest
  • wallet diminuisce di 100 (+ eventuali conversioni)
  • collection cresce con nuove carte
  • chest_opens creato
  • chest_open_items creati = cards_per_open

Caso B (saldo insufficiente):

  • set wallet a 50 (o creare user con meno gold)
  • open chest → 403

Caso C (idempotenza):

  • inviare due volte la stessa request con stessa Idempotency-Key
  • wallet deve scalare una volta sola
  • response identica

Caso D (no duplicates):

  • aprire fino a completare tutte le carte (o tutte della rarità)
  • ulteriori aperture producono conversioni, senza violare vincoli DB

STEP 08 — Social: profilo pubblico + collezione altrui

Obiettivo

Chiunque autenticato può vedere collezioni altrui (se pubbliche).

Endpoint

  • GET /users/{nickname}
  • GET /users/{nickname}/collection

Regole privacy

  • se is_collection_public=false → 403 (o response “private” definita)

Commit

  • feat: add public user profile and collection endpoints

Gate / Test

  • Utente A apre carte
  • Utente B fa GET su collezione A → vede lista
  • Se A setta privacy false (TODO se implementare endpoint) → B riceve 403

STEP 09 — Test automatici backend (minimo indispensabile)

Obiettivo

Test ripetibili per le feature core.

Test richiesti

  • register/login
  • wallet iniziale
  • open chest happy path
  • open chest saldo insufficiente
  • open chest idempotenza
  • unique-only: non si possono inserire duplicati

Commit

  • test: add backend tests for core flows

Gate / Test

  • pytest (o test runner equivalente) verde

STEP 10 — Client di test temporaneo (CLI o Postman)

Obiettivo

Avere un modo semplice per fare E2E senza Unity.

Opzione 1: CLI (consigliata)

  • script che:
    • register
    • login
    • open chest con Idempotency-Key
    • stampa wallet e collection
    • view other collection

Opzione 2: Postman collection

  • file esportato con env (base_url, tokens)

Commit

  • feat: add test client (cli/postman) for end-to-end runs

Gate / Test

  • Eseguire scenario:
    1. crea userA e userB
    2. userA open chest 3 volte
    3. userB legge collezione userA
    4. verificare che non ci siano duplicati in userA

STEP 11 — Hardening minimo (prototipo, ma non fragile)

Obiettivo

Ridurre bug e abusi evidenti.

Azioni

  • rate limit su login e open chest
  • logging strutturato per chest_open
  • gestione errori uniforme (JSON: code, message)

Commit

  • chore: add basic rate limiting and structured logs

Gate / Test

  • aprire 20 volte in 1 secondo → deve limitare o rispondere coerentemente
  • log contiene open_id e user_id

4) Definition of Done (DoD) Prototipo

Il prototipo è “DONE” quando:

  • docker compose up avvia stack
  • register/login funzionano
  • wallet non scende mai sotto zero
  • open chest è transazionale e idempotente
  • user_cards non contiene duplicati (vincolo DB)
  • collezione pubblica di altri utenti è consultabile
  • esistono test automatici minimi
  • esiste client di test per E2E

5) Prompt template per agente IA (uso consigliato)

Prompt generale per ogni step

“In questo step implementa SOLO ciò che è richiesto, senza inventare funzionalità extra. Rispetta i Gate/Test e aggiorna README se cambiano comandi. Dopo ogni modifica, elenca i file creati/modificati.”

Prompt per open chest (molto importante)

“Implementa POST /chests/{chestId}/open ESATTAMENTE secondo SOP. Vincoli: transazione DB, idempotenza con Idempotency-Key, unique-only, conversione duplicati, audit. Non cambiare comportamento e non introdurre logiche non esplicitate.”


6) Domande aperte (non procedere se non risolte)

  • STACK_BACKEND = ?
  • AUTH (refresh token sì/no) = ?
  • CLIENT_TEST = ?
  • Conversione duplicati: sempre 20 gold o dipende da rarità? (se dipende, definire tabella)
  • Nickname: in register o endpoint separato? (definire)