Completa la copertura test del badge Risposta lampo e ne documenta il comportamento

Analisi della logica di serieConferme() senza bug trovati; aggiunte le soglie
esplicite 3/8/15 e i rami mancanti (convocati ristretti, tipo misto, bordo
esatto delle 24h, evento futuro), più un integration test end-to-end che
verifica la mappatura creato_il/risposto_il con dati reali.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-08 15:07:59 +02:00
co-authored by Claude Sonnet 5
parent 8a692bfe2f
commit a451d4187f
5 changed files with 366 additions and 50 deletions
+10
View File
@@ -125,6 +125,16 @@ assert.equal(gradoRaggiunto(serieAllenamentiDef, 9), "argento");
assert.equal(gradoRaggiunto(serieAllenamentiDef, 10), "oro");
assert.equal(gradoRaggiunto(serieAllenamentiDef, 50), "oro", "oltre l'oro resta oro");
// --- serie-conferme: soglie 3 / 8 / 15 ----------------------------------------
const serieConfermeDef = badgeDefs.find((b) => b.id === "serie-conferme")!;
assert.equal(gradoRaggiunto(serieConfermeDef, 2), null, "sotto la prima soglia nessun grado");
assert.equal(gradoRaggiunto(serieConfermeDef, 3), "bronzo", "la soglia è inclusiva");
assert.equal(gradoRaggiunto(serieConfermeDef, 7), "bronzo");
assert.equal(gradoRaggiunto(serieConfermeDef, 8), "argento");
assert.equal(gradoRaggiunto(serieConfermeDef, 14), "argento");
assert.equal(gradoRaggiunto(serieConfermeDef, 15), "oro");
assert.equal(gradoRaggiunto(serieConfermeDef, 50), "oro", "oltre l'oro resta oro");
// --- badgeGiocatore ----------------------------------------------------------
assert.equal(badgeGiocatore(g()).length, badgeDefs.length, "i badge normali sono sempre tutti");
assert.ok(
+48
View File
@@ -176,6 +176,54 @@ assert.equal(
);
assert.equal(serieConferme("g2", conConvocazione, tempi, OGGI), 0, "chi non risponde è a zero");
// Chi non è convocato non spezza la serie conferme di nessun altro (stesso filtro condiviso
// con `serieConsecutiva`, vedi sopra).
const c6Ristretto: Evento = {
...convocati("c6", "2026-08-08", "2026-08-03T10:00:00Z"),
convocati: ["g9"],
};
assert.equal(
serieConferme("g1", [conConvocazione[0]!, c6Ristretto], tempi, OGGI),
1,
"un evento convocato solo per un altro giocatore resta fuori, la serie di g1 non ne risente",
);
// Le conferme contano partite e allenamenti insieme: nessuna versione per tipo.
const mistiTipo: Evento[] = [
convocati("m1", "2026-08-06", "2026-08-01T10:00:00Z"),
{ ...convocati("m2", "2026-08-13", "2026-08-08T10:00:00Z"), tipo: "partita" },
];
const tempiMisti: MappaTempiRisposta = {
m1: { g1: "2026-08-01T11:00:00Z" }, // un'ora dopo
m2: { g1: "2026-08-08T11:00:00Z" }, // un'ora dopo, ma è una partita
};
assert.equal(
serieConferme("g1", mistiTipo, tempiMisti, OGGI),
2,
"partite e allenamenti si sommano nella stessa serie",
);
// Il confronto con le 24h è inclusivo: esattamente al bordo conta, un secondo oltre azzera.
const bordo: Evento[] = [convocati("b1", "2026-08-06", "2026-08-01T10:00:00Z")];
assert.equal(
serieConferme("g1", bordo, { b1: { g1: "2026-08-02T10:00:00Z" } }, OGGI),
1,
"esattamente 24h dopo: il confronto è <=, quindi conta",
);
assert.equal(
serieConferme("g1", bordo, { b1: { g1: "2026-08-02T10:00:01Z" } }, OGGI),
0,
"un secondo oltre le 24h azzera",
);
// Un evento futuro non conta ancora, anche con una risposta rapidissima già registrata.
const futuro: Evento[] = [convocati("f1", "2026-09-10", "2026-09-05T10:00:00Z")];
assert.equal(
serieConferme("g1", futuro, { f1: { g1: "2026-09-05T10:05:00Z" } }, OGGI),
0,
"l'evento di domani non è ancora passato, non entra nel calcolo",
);
// --- cache locale dopo una risposta: il cronometro non riparte ----------------
// Stessa regola del database: `risposto_il` è la PRIMA risposta e un trigger la congela.
// Qui la cache deve imitarla, altrimenti la serie "Conferme 24h" mente fino al refresh.