Rende dinamica anche la scadenza di "Tutti rispondono alle convocazioni"
La scadenza era una data fissa (2026-09-30), stesso problema già risolto per "presenze del mese" e "evento di squadra al mese": ora segue l'ultimo giorno del mese corrente. Aggiunti unit test e integration test end-to-end contro il database locale (isolando gli eventi di test, perché questo obiettivo aggrega su tutti gli eventi indipendentemente dal mese). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,8 +59,14 @@ smart (`notifiche-smart.ts`).
|
|||||||
"90% presenze del mese" e "1 evento di squadra al mese" si azzerano automaticamente a ogni
|
"90% presenze del mese" e "1 evento di squadra al mese" si azzerano automaticamente a ogni
|
||||||
cambio mese: il mese di riferimento è calcolato dalla data corrente (fuso Europe/Rome), non più
|
cambio mese: il mese di riferimento è calcolato dalla data corrente (fuso Europe/Rome), non più
|
||||||
una costante fissa. Per il primo, titolo ("90% di presenze ad agosto" / "a settembre" / ...) e
|
una costante fissa. Per il primo, titolo ("90% di presenze ad agosto" / "a settembre" / ...) e
|
||||||
scadenza (ultimo giorno del mese) seguono di conseguenza. Per i test, `obiettiviSquadra`/
|
scadenza (ultimo giorno del mese) seguono di conseguenza.
|
||||||
`obiettiviOrdinati` accettano un terzo parametro opzionale `oggi: Date` per iniettare una data
|
|
||||||
deterministica. Coperti sia da unit test (`test/unit/obiettivi.test.ts`, funzione pura) sia da
|
"Tutti rispondono alle convocazioni" non si azzera (aggrega su tutti gli eventi in programma,
|
||||||
un integration test end-to-end (`test/integration/obiettivi.test.ts`, scrive/rilegge righe vere
|
non solo quelli del mese corrente), ma la sua `scadenza` mostrata in interfaccia era anch'essa
|
||||||
su `eventi_app` e `risposte_presenze` sullo stack Supabase locale).
|
una data fissa (`"2026-09-30"`): ora è anch'essa l'ultimo giorno del mese corrente.
|
||||||
|
|
||||||
|
Per i test, `obiettiviSquadra`/`obiettiviOrdinati` accettano un terzo parametro opzionale
|
||||||
|
`oggi: Date` per iniettare una data deterministica. Coperti sia da unit test
|
||||||
|
(`test/unit/obiettivi.test.ts`, funzione pura) sia da un integration test end-to-end
|
||||||
|
(`test/integration/obiettivi.test.ts`, scrive/rilegge righe vere su `eventi_app` e
|
||||||
|
`risposte_presenze` sullo stack Supabase locale).
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export function obiettiviSquadra(
|
|||||||
valore: percentualeRisposte(ctx, rosa.length),
|
valore: percentualeRisposte(ctx, rosa.length),
|
||||||
target: 90,
|
target: 90,
|
||||||
unita: "%",
|
unita: "%",
|
||||||
scadenza: "2026-09-30",
|
scadenza: fineMese(oggi),
|
||||||
emoji: "⚡",
|
emoji: "⚡",
|
||||||
impatto: "Bastano pochi tap per far quadrare i conti a chi organizza.",
|
impatto: "Bastano pochi tap per far quadrare i conti a chi organizza.",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Obiettivi mensili di squadra ("presenze del mese" e "evento di squadra al mese")
|
* Obiettivi di squadra con scadenza/mese dinamici ("presenze del mese", "evento di squadra al
|
||||||
* end-to-end contro il database locale: `bun test/integration/obiettivi.test.ts`.
|
* mese" e "tutti rispondono alle convocazioni") end-to-end contro il database locale:
|
||||||
|
* `bun test/integration/obiettivi.test.ts`.
|
||||||
*
|
*
|
||||||
* I test unitari (`test/unit/obiettivi.test.ts`) verificano `obiettiviSquadra()` come funzione
|
* I test unitari (`test/unit/obiettivi.test.ts`) verificano `obiettiviSquadra()` come funzione
|
||||||
* pura, con un `ContestoObiettivi` costruito a mano. Qui invece si scrivono righe vere su
|
* pura, con un `ContestoObiettivi` costruito a mano. Qui invece si scrivono righe vere su
|
||||||
@@ -207,6 +208,77 @@ if (!locale) {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await prova(
|
||||||
|
"o2 legge dal database risposte reali ed esclude i compleanni, con scadenza dinamica",
|
||||||
|
async () => {
|
||||||
|
// o2 (percentualeRisposte) aggrega su TUTTI gli eventi non-compleanno, senza filtro di
|
||||||
|
// mese: per un'asserzione deterministica isoliamo dal risultato reale solo i due eventi
|
||||||
|
// di questo test, invece di dipendere dal numero di eventi già presenti nel database
|
||||||
|
// (seed incluso).
|
||||||
|
const partitaId = `${PREFISSO}-o2-partita`;
|
||||||
|
const compleannoId = `${PREFISSO}-o2-compleanno`;
|
||||||
|
|
||||||
|
const inseritaPartita = await rest("eventi_app", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: partitaId,
|
||||||
|
tipo: "partita",
|
||||||
|
titolo: "Test obiettivi o2",
|
||||||
|
data: `${MESE_TEST}-05`,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!inseritaPartita.ok) {
|
||||||
|
throw new Error(`inserimento evento fallito: ${await inseritaPartita.text()}`);
|
||||||
|
}
|
||||||
|
const inseritoCompleanno = await rest("eventi_app", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: compleannoId,
|
||||||
|
tipo: "compleanno",
|
||||||
|
titolo: "Test obiettivi o2 - compleanno",
|
||||||
|
data: `${MESE_TEST}-06`,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!inseritoCompleanno.ok) {
|
||||||
|
throw new Error(`inserimento evento fallito: ${await inseritoCompleanno.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solo 12 giocatori su tutta la rosa rispondono alla partita; nessuno "risponde" al
|
||||||
|
// compleanno, perché non richiede risposta.
|
||||||
|
const rispondenti = giocatori.slice(0, 12);
|
||||||
|
const righe = rispondenti.map((g, i) => ({
|
||||||
|
evento_id: partitaId,
|
||||||
|
giocatore_id: g.id,
|
||||||
|
stato: i % 2 === 0 ? "presente" : "forse",
|
||||||
|
}));
|
||||||
|
const inseriteRisposte = await rest("risposte_presenze", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(righe),
|
||||||
|
});
|
||||||
|
if (!inseriteRisposte.ok) {
|
||||||
|
throw new Error(`inserimento presenze fallito: ${await inseriteRisposte.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventiReali = (await leggiEventi()).filter(
|
||||||
|
(e) => e.id === partitaId || e.id === compleannoId,
|
||||||
|
);
|
||||||
|
const presenzeReali = await leggiPresenze(partitaId);
|
||||||
|
|
||||||
|
const o2 = obiettiviSquadra(
|
||||||
|
giocatori,
|
||||||
|
{ eventi: eventiReali, presenze: presenzeReali, pagelle: [] },
|
||||||
|
OGGI,
|
||||||
|
).find((o) => o.id === "o2")!;
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
o2.valore,
|
||||||
|
Math.round((12 / giocatori.length) * 100),
|
||||||
|
"12 risposte reali su un solo evento che le richiede (il compleanno è escluso)",
|
||||||
|
);
|
||||||
|
assert.equal(o2.scadenza, "2099-03-31", "scadenza o2 = ultimo giorno del mese iniettato");
|
||||||
|
},
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
await rest(`risposte_presenze?evento_id=like.${PREFISSO}*`, { method: "DELETE" });
|
await rest(`risposte_presenze?evento_id=like.${PREFISSO}*`, { method: "DELETE" });
|
||||||
await rest(`eventi_app?id=like.${PREFISSO}*`, { method: "DELETE" });
|
await rest(`eventi_app?id=like.${PREFISSO}*`, { method: "DELETE" });
|
||||||
|
|||||||
@@ -127,6 +127,13 @@ assert.equal(trova(obiettiviSquadra(giocatori, soloCompleanni, OGGI_AGOSTO), "o2
|
|||||||
const o1Settembre = trova(obiettiviSquadra(giocatori, contestoVuoto, OGGI_SETTEMBRE), "o1");
|
const o1Settembre = trova(obiettiviSquadra(giocatori, contestoVuoto, OGGI_SETTEMBRE), "o1");
|
||||||
assert.ok(o1Settembre.titolo.includes("settembre"), "titolo o1 riflette il mese iniettato (settembre)");
|
assert.ok(o1Settembre.titolo.includes("settembre"), "titolo o1 riflette il mese iniettato (settembre)");
|
||||||
assert.equal(o1Settembre.scadenza, "2026-09-30", "scadenza = ultimo giorno di settembre (30 gg)");
|
assert.equal(o1Settembre.scadenza, "2026-09-30", "scadenza = ultimo giorno di settembre (30 gg)");
|
||||||
|
|
||||||
|
// La scadenza di o2 ("Tutti rispondono alle convocazioni") era una data fissa
|
||||||
|
// ("2026-09-30"): ora segue lo stesso mese dinamico di o1.
|
||||||
|
const o2Agosto = trova(obiettiviSquadra(giocatori, contestoVuoto, OGGI_AGOSTO), "o2");
|
||||||
|
assert.equal(o2Agosto.scadenza, "2026-08-31", "scadenza o2 = ultimo giorno del mese iniettato");
|
||||||
|
const o2Settembre = trova(obiettiviSquadra(giocatori, contestoVuoto, OGGI_SETTEMBRE), "o2");
|
||||||
|
assert.equal(o2Settembre.scadenza, "2026-09-30", "scadenza o2 cambia con il mese iniettato");
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- o6: evento di squadra al mese, si azzera come o1 ------------------------
|
// --- o6: evento di squadra al mese, si azzera come o1 ------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user