🌟 Novità: Elementi decorativi romantici per un'esperienza visiva migliorata! Cuori fluttuanti, particelle animate, gradienti e effetti glassmorphism.
A modern card game application built with Event-Driven Architecture using React, ASP.NET Core, and RabbitMQ. Designed for couples to strengthen their relationship through meaningful conversation prompts.
- 🔐 Registrazione / Login con Password: Account locale con hashing PBKDF2 (salt univoco, nessuna password in chiaro)
- 🎯 Single Player Mode: Esperienza personale di pesca carte
- 👥 Couple Mode (richiesta / approvazione): Accoppiamento esplicito con auto‑start sessione
- ⚡ Partner Sync Immediato:
respond-joinora restituisce direttamentepartnerInfoevitando attese - 🎴 Card Sharing Sincronizzato: Stato carte condivise in snapshot (storico
sharedCards) - � Lavagna Collaborativa: Canvas interattivo con strumenti di disegno, sincronizzazione real-time tra partner
- �🎲 150+ Carte Conversazione: Prompt curati in italiano
- 💕 Elementi Decorativi: Cuori fluttuanti, particelle animate, gradienti romantici per un'atmosfera speciale
- 🔄 Eventi Real-time / Polling Resiliente: RabbitMQ (o polling snapshot come fallback)
- 🩺 Diagnostica Sync Partner: Evento
partnerSyncDelaydopo 3 poll se partner mancante - 🧪 Test Integrazione Automatizzati: Suite Vitest per flussi coppia e pesca carta
- 🎨 Modern UI con MUI + Fabric.js: Layout responsive, AppBar, Drawer log, canvas animato per carte
- 📱 Responsive Design: Mobile & Desktop
- 🏗️ Architettura Moderna: Separation of concerns, fallback sicuri
- 🌗 Dark Mode Toggle: Tema scuro persistente via localStorage
Le credenziali sono gestite solo lato browser (modalità prototipo):
| Aspetto | Implementazione |
|---|---|
| Hashing | PBKDF2 SHA-256 120k iterazioni |
| Salt | Generato per-account (16 byte) |
| Storage | localStorage (hash + salt + metadati utente) |
| Trasmissione | Nessun invio password al backend attuale |
Limitazioni attuali:
- Nessun recupero password / reset
- Nessun rate limiting locale
- Sessione legata al browser (no multi-device persistente)
Per produzione migrare a backend con Argon2id / scrypt, sessioni firmate e rotazione token.
Il toggle (icona sole/luna) consente di passare fra light e dark mode. Caratteristiche:
- Persistenza in
localStorage['complicity_color_mode'] - Palette ottimizzata per contrasto su sfumature viola/rosa
- Canvas Fabric rianima la carta mantenendo centratura in entrambi i temi
- Componenti MUI reattivi alla palette (background/paper/primary/secondary)
Event-Driven with RabbitMQ
- Frontend: React 18 + Vite + Tailwind CSS
- Backend: ASP.NET Core 8 Web API
- Database: SQLite with Entity Framework Core
- Events: RabbitMQ for real-time communication
- State Management: Event sourcing pattern
window.__apiService(singleton) esposto solo per scenari E2E quando build conVITE_E2E=1(TTL, metriche senza UI fragile). La precedente strumentazione multi‑istanza è stata rimossa.- Assert "soft" su formazione coppia / partner name: se il backend è lento non falliscono, ma loggano un messaggio informativo.
- Flag ambiente supportati:
E2E_VERBOSE=1abilita la stampa del frammento HTML post autenticazione.STRICT_COUPLE_ASSERT=1rende nuovamente obbligatorie le asserzioni sulla coppia/partner (usa helperassertStrict).VITE_E2E=1(build-time) abilita l'esposizione diwindow.__apiService.
Esempio esecuzione verbosa:
E2E_VERBOSE=1 npm run test:e2ePer esecuzione silenziosa (CI):
npm run test:e2eNota: l'esposizione di window.__apiService è pensata unicamente per test end‑to‑end; evitare di farvi affidamento nel codice di produzione.
CardApp/
├── 🎯 Core Application
│ ├── src/ # Clean, modern React frontend
│ │ ├── main.jsx # App entry point
│ │ ├── SimpleApp.jsx # Main application orchestrator
│ │ ├── SimpleAuth.jsx # User authentication
│ │ ├── SimpleCardGame.jsx # Single player game
│ │ ├── CoupleGame.jsx # Couple/partner game
│ │ ├── EventDrivenApiService.js # API communication layer
│ │ ├── expandedCards.js # Card deck data
│ │ └── familyCards.js # Family-friendly cards
│ │
│ ├── Backend/ComplicityGame.Api/ # ASP.NET Core Web API
│ │ ├── Controllers/ # REST API endpoints
│ │ │ └── EventDrivenGameController.cs
│ │ ├── Services/ # Business logic layer
│ │ │ ├── UserPresenceService.cs
│ │ │ ├── CoupleMatchingService.cs
│ │ │ ├── GameSessionService.cs
│ │ │ └── RabbitMQEventPublisher.cs
│ │ ├── Models/ # Data models and entities
│ │ ├── Events/ # RabbitMQ event system
│ │ └── Data/ # Database context (SQLite)
│ │
├── 🛠️ Development Tools
│ ├── start.sh # Start complete application
│ ├── stop.sh # Stop all services
│ ├── test-all.sh # Comprehensive test suite
│ ├── test-partner-matching.sh # Partner matching tests
│ │
├── 📦 Configuration
│ ├── package.json # Frontend dependencies
│ ├── vite.config.js # Vite build configuration
│ └── .github/copilot-instructions.md
│
└── 📚 Documentation
├── README.md # This file
├── SCRIPTS.md # Scripts documentation
└── archive/ # Legacy files (cleaned up)
- Node.js 20.19.0+ (consigliato via
.nvmrc/nvm use) - .NET 8 SDK
- SQLite
- (Opzionale) RabbitMQ se si abilita la messaggistica reale (il polling snapshot è fallback)
-
Clone and setup:
git clone <repository-url> cd CardApp npm install
-
Start the application:
./start.sh
-
Access the application:
- Frontend: http://localhost:5173
- Backend API: http://localhost:5000
- Open http://localhost:5173
- Enter your name and select "Gioco Singolo"
- Start drawing cards and enjoy!
Flusso moderno (request / approve) implementato per evitare accoppiamenti involontari:
- Entrambi gli utenti si connettono ("Gioco di Coppia").
- L'utente A preme "Richiedi" accanto al nome di B.
- B vede un badge "Richiesta per te" e i pulsanti
Accetta/Rifiuta. - Se B accetta:
- La richiesta viene rimossa da entrambi i lati.
- Si crea (o completa) la coppia.
- Se la coppia ha due membri il sistema avvia automaticamente una Game Session.
- Se B rifiuta: la richiesta scompare, nessuna coppia viene creata.
- A può anche
Annullaprima della risposta di B.
Note tecniche:
- Le richieste pendono per 10 minuti prima di scadere (expire) automaticamente.
- Optimistic UI: A vede subito lo stato "In attesa" senza attendere il polling.
- Se una richiesta viene approvata vengono ripulite eventuali richieste incrociate residue.
Per dettagli su caching locale, flag _optimistic e riconciliazione snapshot consultare il file JOIN_REQUESTS.md.
La nuova lavagna (Whiteboard.jsx) usa Fabric.js + Material UI con toolbar espandibile e sincronizzazione debounced.
- Disegno libero (brush) con spessore e colore
- Forme rapide: Rettangolo, Cerchio
- Testo editabile (Fabric IText)
- Undo / Redo (stack max 50 stati)
- Zoom incrementale 0.4x – 3x
- Cambio colore sfondo (persistito nello stato condiviso)
- Pulizia totale canvas
- Esportazione PNG (download + callback
onExport) - Modalità toggle Disegno / Oggetti
- Overlay di caricamento e stato disabilitato con opacità
Props principali:
<Whiteboard
value={{ json, bgColor, version }} // stato remoto ricevuto
onChange={(nextState, meta) => { /* publish */ }}
disabled={false}
loading={false}
height={360}
debounceMs={600}
sessionId={gameSession?.id}
userId={user.userId}
/>nextState struttura:
{
json: FabricJSON,
bgColor: string,
version: number // timestamp ms usato come semplice vettore
}meta struttura:
{ reason: 'draw'|'add'|'modify'|'remove'|'undo'|'redo'|'clear'|'background', localOps: number }Per ora la sincronizzazione avviene tramite BroadcastChannel (stub locale) nel service (syncLavagna). Questo permette collaborazione multi‑tab / multi‑finestra senza backend dedicato. Ogni emissione:
- Genera JSON completo del canvas (
fabric.Canvas.toJSON(['id'])). - Applica debounce (default 600ms) per ridurre traffico.
- Pubblica evento su canale e emette localmente
lavagnaSync. - Whiteboard ascolta via prop
valuee ricarica seversiondifferente da quello locale.
Endpoint pianificato: POST /api/lavagna/sync con payload:
{ "sessionId": "...", "diff": { /* patch ottimizzata */ }, "bgColor": "#fff", "version": 1739032459123 }Il backend pubblicherà evento RabbitMQ LavagnaUpdated sul routing key game.<sessionId>.lavagna.updated.
Fasi evolutive:
- Delta/Patch (operazioni incrementali anziché full JSON)
- Compressione (rimozione oggetti invariati oltre threshold)
- Persistenza snapshot per riapertura sessione
Test E2E lavagna-sync-test.spec.js verifica propagazione creando due contesti e simulando modifica (rectangle / broadcast diretto). Lo stato corrente è esposto a scopo test via window.__latestLavagnaState.
- Debounce evita spam eventi su ogni stroke.
- Limite history (50) previene crescita memoria eccessiva.
- Zoom e rendering delegati a Fabric (accelerazione canvas 2D).
- Contrasto toolbar (fondo bianco + blur) per leggibilità.
- Opacità canvas quando disabilitato; overlay di caricamento aria-friendly.
- Label italiane coerenti con il tema del progetto.
Per velocizzare e rendere più stabili i test di dominio è stato introdotto un progetto ComplicityGame.Core che contiene:
- Modelli minimi (
User,Couple,CoupleUser) eGameDbContextcon configurazione EF. - Eventi di base per la coppia (
CoupleCreated,CoupleCompleted,CoupleDisconnection). CoupleMatchingServicee relative interfacce semplificate.
I test unitari ora referenziano solo ComplicityGame.Core, evitando dipendenze runtime superflue (Swagger, RabbitMQ, SQLite native), con esecuzione più rapida e isolamento maggiore. L'API continua a poter evolvere (controller, presenza utenti, sessioni di gioco) senza appesantire il ciclo TDD sul servizio di matching.
| Livello | Strumento | Percorso | Cosa valida |
|---|---|---|---|
| Integrazione API (JS) | Vitest | tests/integration/*.test.js |
Coppia, sessione, sync partner, pesca carta |
| Shell integration | bash + curl + jq | tests/*.test.sh |
Flussi API legacy (approve / reject / cancel) |
| End‑to‑End UI | Playwright | tests/e2e/*.spec.js |
Interazioni reali browser (richiesta, accetta, rifiuta, annulla, reconnect) |
# Test unit frontend
npm run test:unit
# Test integrazione (avvia backend + frontend e lancia Vitest integration)
npm run test:integration
# Test shell (flussi base)
./test-all.sh
# Test E2E Playwright
npx playwright testPer generare il report HTML Playwright:
npx playwright show-reportSono stati introdotti data-testid in UserDirectory.jsx per ridurre la fragilità:
incoming-request-badgesend-requestaccept-requestreject-requestcancel-request
- Messaggio
Please upgrade your Node.js version: assicurati di usarenvm use(20.19.0+). - Se i test E2E trovano molti utenti "fantasma", l'endpoint
POST /api/admin/clear-userspuò pulire lo stato. - Flakiness ridotta aggiungendo polling con
expect.polle testids stabili.
POST /api/EventDrivenGame/connect- Connessione utentePOST /api/EventDrivenGame/reconnect- Riconnessione con auth tokenGET /api/EventDrivenGame/available-users/{userId}- Lista utenti disponibili (esclude self)POST /api/EventDrivenGame/request-join- Crea richiesta join (A->B)POST /api/EventDrivenGame/respond-join- Approvazione / rifiuto richiesta (B risponde) → ora ritorna anchepartnerInfoegameSessionPOST /api/EventDrivenGame/cancel-join- Annulla richiesta in pending (A)GET /api/EventDrivenGame/join-requests/{userId}- Incoming / outgoing requestsGET /api/EventDrivenGame/snapshot/{userId}- Snapshot aggregato (users + requests + stato + sessione)POST /api/EventDrivenGame/start-game- Avvio manuale game (fallback se non auto)POST /api/EventDrivenGame/draw-card- Pesca carta
POST /api/admin/clear-users- Pulisce utenti, coppie, sessioni (usato nei test)POST /api/admin/reset-system- Alias di reset completoPOST /api/admin/force-refresh- Segnale soft di refresh (no-op logico)POST /api/admin/seed-test-cards- Inserisce carte di testGET /api/admin/cards-status- Stato deck carteGET /api/health- Health check
Request:
POST /api/EventDrivenGame/connect
{ "name": "Anna", "gameType": "couple" }Response:
{
"success": true,
"status": { "userId": "f2e...", "isOnline": true, "coupleId": null },
"personalCode": "A1B2C3",
"authToken": "<guid>",
"userId": "f2e..."
}Campi notevoli:
personalCode: codice condivisibile (non segreto) per pairing legacyauthToken: usato perreconnect
POST /api/EventDrivenGame/request-join
{ "requestingUserId": "U1", "targetUserId": "U2" }Response:
{ "success": true, "requestId": "<guid>", "status": "Pending" }Rate limiting: max 5 richieste (A->B) per 30s (429 + header Retry-After).
POST /api/EventDrivenGame/respond-join
{ "requestId": "<guid>", "targetUserId": "U2", "approve": true }Response (approve):
{
"success": true,
"approved": true,
"coupleId": "<guid>",
"gameSession": { "id": "<guid>", "isActive": true, "createdAt": "2025-10-02T12:00:00Z" },
"partnerInfo": { "userId": "U1", "name": "Anna", "personalCode": "A1B2C3" }
}Response (reject):
{ "success": true, "approved": false }GET /api/EventDrivenGame/snapshot/U1Response (parziale):
{
"success": true,
"status": { "userId": "U1", "coupleId": "<guid>" },
"gameSession": { "id": "<guid>", "isActive": true },
"partnerInfo": { "userId": "U2", "name": "Bruno" },
"users": [ { "id": "U1", "name": "Anna" }, { "id": "U2", "name": "Bruno" } ],
"outgoingRequests": [],
"incomingRequests": [],
"expiresAfterMinutes": 10
}POST /api/EventDrivenGame/draw-card
{ "sessionId": "<guid>", "userId": "U1" }Response:
{
"success": true,
"card": { "id": 42, "gameType": "couple", "category": "dialogo", "content": "Domanda...", "level": 2 }
}Start (fallback manuale):
POST /api/EventDrivenGame/start-game
{ "coupleId": "<guid>" }Response:
{ "success": true, "gameSession": { "id": "<guid>", "isActive": true } }End:
POST /api/EventDrivenGame/end-game
{ "sessionId": "<guid>" }Response:
{ "success": true }Edge cases:
404/400secoupleIdosessionIdinesistenti o non autorizzatidraw-cardritorna400se mazzo esauritorequest-joinritorna stessorequestIdse richiesta pendente già esiste (idempotenza soft)
- Connect → User authentication and setup
- Select Game Type → Choose "Single Player"
- Draw Cards → Get conversation prompts
- Enjoy → Reflect on the prompts
- Both Connect → Authentication for both partners
- Partner Matching → Use personal codes to form a couple
- Auto Game Session → System creates shared game session
- Draw Cards Together → Take turns drawing cards
- Conversation → Discuss the prompts together
| Script | Purpose |
|---|---|
start.sh |
Start complete application (backend + frontend) |
start.sh --simple |
Quick start mode (minimal checks) |
start.sh --cleanup |
Clean up ports and processes only |
stop.sh |
Stop all services and clean up ports |
test-all.sh |
Run comprehensive test suite |
test-partner-matching.sh |
Test partner matching workflow |
# Standard start with full health checks
./start.sh
# Quick start for development
./start.sh --simple
# Clean up stuck processes/ports
./start.sh --cleanup
# Stop everything cleanly
./stop.shUsers - User accounts and authentication
Couples - Partner relationships
CoupleUsers - Many-to-many relationship for couples
GameSessions - Active game instances
Cards - Game card data (optional storage)
The application uses RabbitMQ for real-time events:
- UserConnected - User joins the system
- CoupleCreated - New couple formed
- CoupleCompleted - Couple has 2 members
- GameSessionStarted - New game begins
- CardDrawn - Card drawn by player
- Workflow richieste coppia (request / approve / reject / cancel) con auto-start game
- Risposta
respond-joinarricchita conpartnerInfo+gameSession - Fallback server-side partner (
[FallbackPartner]) per snapshot immediato del richiedente - Eventi frontend:
partnerUpdated,gameSessionStarted,sessionUpdated(carta pescata) - Diagnostica
partnerSyncDelaydopo 3 poll senza partner - Ottimistic UI per richieste (aggiornamento immediato)
- Snapshot endpoint aggregato
- Test integrazione Vitest (coppia, stabilità snapshot, pesca, partner immediato)
- Test shell (approve, reject, cancel) + E2E Playwright con
data-testid - Auto pulizia richieste incrociate dopo approvazione
- Avvio automatico Game Session
- Matrix CI (Node / OS) & caching ottimizzato
- Global Playwright setup (clear-users pre suite)
- Coverage combinata frontend+backend automatica (badge dinamico)
- Persistenza carte / progressi sessioni multiple
- i18n dinamico runtime
- WebSocket / SignalR per eliminare polling
- Rate limiting configurabile lato API (già esistente per join, estendere ad altre operazioni)
| Area | Aggiornamento |
|---|---|
| UI | Introduzione Material UI (MUI) con tema personalizzato + Fabric.js (canvas carte) + pulizia log |
| Join Workflow | respond-join ora include partnerInfo e gameSession |
| Partner Sync | Fallback server-side immediato + evento diagnostico partnerSyncDelay |
| Snapshot | Aggiunto fallback [FallbackPartner] e stabilità sessione verificata via test |
| Testing | Suite integrazione Vitest + stabilizzazione unit (mock interno & ordine fetch deterministico) |
| API | Migliorata risposta respond-join per ridurre latenze UI |
| Ottimistic Join | TTL configurabile + pruning con metriche & evento joinRequestExpired |
| Script | test:integration esegue backend+frontend+Vitest in modo automatizzato |
| Documentazione | README aggiornato con nuove sezioni e API arricchite |
Per i test unitari del frontend è disponibile un mock interno opzionale attivabile tramite variabile ambiente.
| Variabile | Valore | Effetto |
|---|---|---|
INTERNAL_API_TEST_MOCK |
1 |
Attiva handler in–memory dentro EventDrivenApiService (nessuna chiamata HTTP reale) |
ENABLE_POLL_IN_TEST |
1 |
(Opzionale) Riabilita il polling automatico anche in ambiente test |
Caratteristiche mock:
- Generazione deterministica di ID utente (
U1,U2, ...) - Aging artificiale delle richieste join per test pruning
- Simulazione failure: target contenente
FAILoTARGET2→ errore su/request-join;FAILsu/cancel-join - Primo snapshot può nascondere outgoing per validare conservazione ottimistica
- Nessun side effect esterno → test rapidi e stabili
Disabilitato di default: i test unitari usano fetch mock espliciti e il servizio in modalità "no polling" per mantenere l'ordine prevedibile delle chiamate.
Il frontend applica un pattern Optimistic UI alle richieste di coppia:
requestJoininserisce subito un record temporaneo{ _optimistic: true }nella cacheoutgoingcontemp-<timestamp>.- Se la risposta server contiene
requestIdil record viene aggiornato mantenendo il flag finché uno snapshot non lo conferma. - Snapshot vuoti preservano i record
_optimistic(evita flicker). - Pruning: se un record resta
_optimisticoltreoptimisticJoinTTLviene rimosso e vengono emessi:joinRequestExpired(payload{ request })- Incremento metrica
prunedJoinCount(+ eventometricsUpdated)
Parametri:
| Chiave | Descrizione | Default |
|---|---|---|
optimisticJoinTTL |
Tempo massimo (ms) prima di pruning | 30000 |
minOptimisticTTL |
Soglia minima forzata | 500 |
prunedJoinCount |
Contatore persistito (localStorage) | 0 |
Persistenza: localStorage['complicity_join_settings'] memorizza TTL e contatore pruning.
Il servizio accumula eventi interni in un buffer (flush a 20 eventi o al teardown):
Tipi principali:
metricIncrement(es. pruning)settingsUpdated(cambio TTL)telemetryBatch(emesso con{ events, at }al flush)
Uso suggerito: collegare un listener a telemetryBatch per invio futuro a backend / analytics.
| Evento | Payload | Trigger |
|---|---|---|
usersUpdated |
{ users, incoming, outgoing } |
Cambi snapshot utenti / richieste |
joinRequestsUpdated |
{ incoming, outgoing } |
Cache richieste aggiornata |
joinRequestExpired |
{ request } |
Pruning richiesta ottimistica |
metricsUpdated |
{ prunedJoinCount } |
Aggiornamento metriche |
settingsUpdated |
{ optimisticJoinTTL } |
Modifica TTL ottimistico |
coupleJoined |
{ coupleId, partner } |
Coppia formata / approvazione |
partnerUpdated |
{ userId, name, personalCode } |
Aggiornamento/rilevazione partner |
gameSessionStarted |
{ sessionId } |
Sessione avviata |
Per migliorare stabilità e osservabilità dei test end‑to‑end sono stati introdotti alcuni flag ambiente:
| Variabile | Scope | Effetto |
|---|---|---|
VITE_E2E=1 |
Build (vite) | Espone il singleton window.__apiService per test Playwright (metriche, TTL) – NON usare in produzione |
STRICT_COUPLE_ASSERT=1 |
Runtime (Playwright) | Le asserzioni sulla formazione coppia/partner tornano hard‑fail (usa helper assertStrict) |
E2E_VERBOSE=1 |
Runtime (Playwright) | Logga snippet HTML post‑auth per debug flussi di login/registrazione |
Esempi:
# Esecuzione completa in modalità strict e con log diagnostici
STRICT_COUPLE_ASSERT=1 E2E_VERBOSE=1 npm run test:e2e
# Costruire il frontend esponendo apiService per E2E
VITE_E2E=1 npm run devNel workflow CI E2E (.github/workflows/ci-e2e.yml) i flag NON sono abilitati di default per mantenere il comportamento standard e evitare di dipendere da API non pubbliche. Abilitare VITE_E2E solo se si introducono nuovi test che richiedono accesso diretto alle metriche.
Per eseguire rapidamente i test Playwright in headless partendo da un report pulito:
npm run test:e2e:clean
Questo script rimuove la cartella playwright-report/ precedente e usa il reporter list per output compatto (utile in CI locale). Per il report HTML completo continua a usare npm run test:e2e e poi npm run test:e2e:report.
Le asserzioni su coppia e partner sono soft per ridurre flakiness dovuta a latenze backend. Abilitare STRICT_COUPLE_ASSERT nelle esecuzioni locali quando si vuole intercettare regressioni early.
Per ridurre il rumore in console durante l'uso normale, i log dettagliati dell'EventDrivenApiService ora passano tramite src/utils/logger.js.
Livelli disponibili:
logger.debug(mostrato solo se abilitato)logger.infologger.warnlogger.error
Abilitazione debug (tutti i log):
# Solo runtime (vite):
DEBUG_API=1 npm run dev
# Oppure build-time + runtime
VITE_DEBUG_API=1 npm run devNota: VITE_DEBUG_API viene iniettata nel bundle, mentre DEBUG_API è letta a runtime (utile nei test).
Con VITE_E2E=1 il singleton window.__apiService viene esposto solo in ambiente di test. Gli helper forniscono astrazioni stabili:
| Helper | Scopo |
|---|---|
getJoinMetrics(page) |
Ritorna metriche TTL join |
forceExpireOptimistic(page) |
Forza expire richieste ottimistiche |
setOptimisticTTL(page, ms) |
Modifica TTL locale per test |
drawCard(page) |
Esegue una drawCard sulla sessione corrente |
waitForCardDrawEvent(page,{timeoutMs}) |
Attende evento sessionUpdated tipo cardDrawn |
getClientState(page) |
Snapshot rapido dello stato client |
Esempio uso in test:
import { drawCard, waitForCardDrawEvent } from './lib/serviceHelpers';
const p = waitForCardDrawEvent(page);
await drawCard(page);
const evt = await p;
expect(evt.success).toBeTruthy();File: tests/e2e/card-draw-smoke.spec.js
Verifica rapidamente:
- Due utenti si collegano
- Formano coppia usando personal code
- Sessione disponibile
- Pesca carta → evento
sessionUpdatedricevuto
Questo test viene eseguito nello step Smoke del workflow verify.
Creare (o verificare) i seguenti file per approfondimenti:
JOIN_REQUESTS.md– Dettaglio lifecycle, esempi timing, casi edge (approve simultaneo, cancel tardivo).env.example– Porta frontend/backend + flag testdocs/FRONTEND_EVENTS.md– Lista versionata degli eventi con schema payload
In casi rari di latenza, il frontend emette una voce log: ⏱️ Ritardo nella sincronizzazione del partner... (diagnostica) dopo ~6s (3 poll). Il backend espone un fallback interno che ricostruisce partnerInfo direttamente dal DB; il log [FallbackPartner] indica che il meccanismo è entrato in azione.
Se questo evento appare di frequente:
- Verificare carico DB / latenza I/O
- Controllare eventuali lock o ritardi EF nelle navigation
- Considerare l'abilitazione di un canale WebSocket per push immediato
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
./test-all.sh - Submit a pull request
This project is private and proprietary.
CardApp - Bringing couples closer through meaningful conversation 💕