12 KiB
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 è l’unica sorgente di verità. Il client visualizza e chiama API.
- L’apertura 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
.envvero. Solo.env.example.
STEPS (commit-based)
STEP 01 — Bootstrap repo + Docker “Hello World”
Obiettivo
docker compose up --buildavvia tutto- backend espone
GET /health→{ "status": "ok" }
Azioni
- Creare docker-compose.yml con:
- service
db(postgres) - service
api(backend)
- service
- Implementare endpoint
/health - 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
- Aggiungere driver DB e configurazione.
- Implementare
GET /db-checkche esegueSELECT 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
cards (catalogo)
- id (string or uuid)
- name
- rarity (COMMON/RARE/LEGENDARY)
- created_at
chests (catalogo)
- 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/cardsritorna lista > 0GET /catalog/chestsritorna 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/profileupdate)
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/walletGET /me/collectionGET /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}/openHeader:- Authorization: Bearer <access_token>
- Idempotency-Key:
Algoritmo (obbligatorio)
Dentro una transazione DB:
- Validare chestId
- Verificare saldo >= cost
- Debit saldo (wallet)
- 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 dall’utente - 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
- Salvare chest_opens + chest_open_items
- 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:
- crea userA e userB
- userA open chest 3 volte
- userB legge collezione userA
- 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)