Il badge Sherpa dei palloni conta solo i turni confermati, non le proposte
rosa.ts calcolava Giocatore.palloni su completaTurni() (turni salvati + proposte automatiche di rotazione non ancora confermate da nessuno): un giocatore poteva avanzare nel badge senza aver mai confermato un turno, solo perché l'algoritmo lo proponeva per un evento passato. Ora passa a conteggioTurni() solo turniSalvati (i turni davvero confermati in turni_palloni); completaTurni() resta in uso solo per la UI di rotazione (TurnoPalloni.tsx, PromemoriaPalloni.tsx). Corregge anche la StatTile "quante volte hai portato i palloni" nel profilo, che leggeva lo stesso valore. Aggiornato palloni-badge.test.ts per verificare il nuovo comportamento (un evento non confermato non conta più per nessuno) invece del vecchio. docs/modules/ badge.md e palloni.md aggiornati di conseguenza, spostando la voce da "Limiti noti" a "risolto". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,24 +3,26 @@
|
||||
* `bun test/integration/palloni-badge.test.ts`.
|
||||
*
|
||||
* I test unitari (`test/unit/palloni-core.test.ts`, `test/unit/badges.test.ts`) verificano
|
||||
* `completaTurni()`/`conteggioTurni()` e `statoBadge()` come funzioni pure, con eventi e turni
|
||||
* costruiti a mano. Qui invece si scrivono eventi e turni veri su `eventi_app`/`turni_palloni`,
|
||||
* si rileggono via REST con la stessa forma di `fetchTurni()`/`daRiga()`, e si passa il
|
||||
* risultato attraverso `completaTurni()` → `conteggioTurni()` fino a `statoBadge()` sul badge
|
||||
* `palloni`: se una colonna cambia nome o la mappatura si rompe, qui il grado del badge torna
|
||||
* sbagliato anche se i test unitari restano verdi, perché quelli non toccano mai il database.
|
||||
* `conteggioTurni()` e `statoBadge()` come funzioni pure, con eventi e turni costruiti a mano.
|
||||
* Qui invece si scrivono eventi e turni veri su `eventi_app`/`turni_palloni`, si rileggono via
|
||||
* REST con la stessa forma di `fetchTurni()`/`daRiga()`, e si passa il risultato attraverso
|
||||
* `conteggioTurni()` fino a `statoBadge()` sul badge `palloni`: se una colonna cambia nome o la
|
||||
* mappatura si rompe, qui il grado del badge torna sbagliato anche se i test unitari restano
|
||||
* verdi, perché quelli non toccano mai il database.
|
||||
*
|
||||
* Copre in particolare un comportamento **documentato ma non ovvio** (`docs/modules/
|
||||
* palloni.md` § Limiti noti): il conteggio usato dal badge include anche le proposte
|
||||
* automatiche non ancora confermate da nessuno, non solo i turni salvati esplicitamente. Qui
|
||||
* lo si dimostra con dati veri, non solo con l'affermazione in doc.
|
||||
* Verifica in particolare che il badge conti **solo i turni confermati**
|
||||
* (`Giocatore.palloni` in `rosa.ts` usa `turniSalvati`, non l'output di `completaTurni()`):
|
||||
* un evento passato senza turno confermato non deve avanzare il badge di nessuno, anche se
|
||||
* `completaTurni()` (usato altrove, per la UI di rotazione) lo proporrebbe automaticamente a
|
||||
* qualcuno. Prima di questo fix il badge includeva anche le proposte non confermate — vedi
|
||||
* `docs/modules/badge.md` § Limiti noti (risolto) per il dettaglio del bug e della correzione.
|
||||
*
|
||||
* Gira solo sullo stack locale (`npx supabase start`) e cancella le proprie righe alla fine:
|
||||
* usa id con il prefisso `test-palloni-badge`, che nessun dato vero può avere.
|
||||
*/
|
||||
import assert from "node:assert/strict";
|
||||
import { badgeDefs, statoBadge } from "@/lib/badges";
|
||||
import { completaTurni, conteggioTurni, type CandidatoTurno } from "@/lib/palloni-core";
|
||||
import { conteggioTurni } from "@/lib/palloni-core";
|
||||
import { daRiga, type RigaEvento, type Evento } from "@/lib/eventi";
|
||||
import { giocatori, type Giocatore } from "@/lib/crapp-data";
|
||||
import { statoLocale } from "../helpers/locale";
|
||||
@@ -41,11 +43,6 @@ if (!locale) {
|
||||
// così restano "passati" (e quindi contati) a prescindere da quando gira il test.
|
||||
const OGGI = "2099-01-01";
|
||||
|
||||
const ROSA: CandidatoTurno[] = [
|
||||
{ id: "pv1", nome: "Uno" },
|
||||
{ id: "pv2", nome: "Due" },
|
||||
];
|
||||
|
||||
const rest = (percorso: string, init?: RequestInit) =>
|
||||
fetch(`${URL_BASE}/rest/v1/${percorso}`, {
|
||||
...init,
|
||||
@@ -86,6 +83,7 @@ if (!locale) {
|
||||
return righe.map(daRiga);
|
||||
}
|
||||
|
||||
/** Rilegge esattamente come `fetchTurni()`: solo i turni confermati, nessuna proposta. */
|
||||
async function leggiTurniSalvati(): Promise<Record<string, string>> {
|
||||
const res = await rest(
|
||||
`turni_palloni?evento_id=like.${PREFISSO}*&select=evento_id,giocatore_id`,
|
||||
@@ -103,12 +101,11 @@ if (!locale) {
|
||||
|
||||
try {
|
||||
await prova(
|
||||
"il badge palloni passa da bronzo ad argento con turni reali, incluse le proposte non confermate",
|
||||
"il badge palloni passa da bronzo ad argento con turni reali, ignorando gli eventi mai confermati",
|
||||
async () => {
|
||||
// m1-m3: turno confermato esplicitamente a pv1 (3 turni -> soglia bronzo).
|
||||
// m4: NESSUN turno salvato -> completaTurni() deve proporlo in automatico a chi ha
|
||||
// fatto meno turni (pv2, a zero) -> conta comunque per il badge di pv2, pur non
|
||||
// essendo mai stato confermato da nessuno (comportamento documentato in palloni.md).
|
||||
// m4: NESSUN turno confermato -> non deve contare per nessuno, anche se un algoritmo
|
||||
// di rotazione (usato altrove per la UI) proporrebbe qualcuno in automatico.
|
||||
// m5-m7: confermati di nuovo a pv1 (6 turni totali -> soglia argento).
|
||||
const eventi = [
|
||||
[`${PREFISSO}-m1`, "2020-01-01", "pv1"],
|
||||
@@ -129,36 +126,28 @@ if (!locale) {
|
||||
const salvati = await leggiTurniSalvati();
|
||||
assert.equal(Object.keys(salvati).length, 6, "6 turni confermati esplicitamente, m4 no");
|
||||
|
||||
const turniCompleti = completaTurni(salvati, eventiLetti, ROSA);
|
||||
assert.equal(
|
||||
turniCompleti[`${PREFISSO}-m4`],
|
||||
"pv2",
|
||||
"m4 senza turno salvato: la proposta automatica sceglie pv2 (meno turni)",
|
||||
);
|
||||
|
||||
const conteggio = conteggioTurni(turniCompleti, eventiLetti, OGGI);
|
||||
const conteggio = conteggioTurni(salvati, eventiLetti, OGGI);
|
||||
assert.equal(conteggio["pv1"], 6, "6 turni confermati per pv1");
|
||||
assert.equal(
|
||||
conteggio["pv2"],
|
||||
1,
|
||||
"pv2 non ha mai confermato nulla, ma la proposta per m4 conta comunque",
|
||||
conteggio["pv2"] ?? 0,
|
||||
0,
|
||||
"pv2 non ha mai confermato nulla: m4 senza conferma non conta per nessuno",
|
||||
);
|
||||
|
||||
const badgePv1 = statoBadge(palloniDef, giocatoreAzzerato(conteggio["pv1"]!));
|
||||
assert.equal(badgePv1.grado, "argento", "6 turni: soglia argento raggiunta");
|
||||
|
||||
const badgePv2 = statoBadge(palloniDef, giocatoreAzzerato(conteggio["pv2"]!));
|
||||
assert.equal(badgePv2.grado, null, "1 turno (solo proposto): sotto la soglia bronzo (3)");
|
||||
const badgePv2 = statoBadge(palloniDef, giocatoreAzzerato(conteggio["pv2"] ?? 0));
|
||||
assert.equal(badgePv2.grado, null, "zero turni confermati: nessun badge");
|
||||
},
|
||||
);
|
||||
|
||||
await prova("un evento futuro non riceve conteggio, anche se già assegnato", async () => {
|
||||
// Stesso scenario di sopra ma con "oggi" prima di tutti gli eventi: nessuno dei turni,
|
||||
// confermati o proposti, deve ancora contare per il badge.
|
||||
await prova("un evento futuro non riceve conteggio, anche se già confermato", async () => {
|
||||
// Stesso scenario di sopra ma con "oggi" prima di tutti gli eventi: nessun turno
|
||||
// confermato deve ancora contare per il badge.
|
||||
const eventiLetti = await leggiEventi();
|
||||
const salvati = await leggiTurniSalvati();
|
||||
const turniCompleti = completaTurni(salvati, eventiLetti, ROSA);
|
||||
const conteggio = conteggioTurni(turniCompleti, eventiLetti, "2019-01-01");
|
||||
const conteggio = conteggioTurni(salvati, eventiLetti, "2019-01-01");
|
||||
assert.deepEqual(conteggio, {}, "tutti gli eventi sono nel futuro rispetto a 'oggi'");
|
||||
});
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user