/** Check dei voti MVP: `bun test/unit/mvp-voti.test.ts`. */ import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { conteggioPartita, elencoNomi, mioVoto, mvpPartita, mvpVintiPerGiocatore, DURATA_VOTO_MVP_ORE, ORE_ATTESA_MVP, vincitoriMvp, votoMvpAperto, votoMvpChiuso, votoMvpInCorso, VOTI_MINIMI_MVP, type VotoMvp, } from "@/lib/mvp-voti"; const v = ( match_id: string, votante_id: string, votato_id: string, votato_nome: string, ): VotoMvp => ({ match_id, votante_id, votato_id, votato_nome, }); // --- conteggioPartita -------------------------------------------------------- const partita = [ v("m1", "g1", "g2", "Bruno"), v("m1", "g3", "g2", "Bruno"), v("m1", "g4", "g5", "Anna"), v("m2", "g1", "g5", "Anna"), v("m2", "g3", "g5", "Anna"), ]; assert.deepEqual(conteggioPartita(partita, "m1"), [ { id: "g2", nome: "Bruno", voti: 2 }, { id: "g5", nome: "Anna", voti: 1 }, ]); assert.deepEqual(conteggioPartita(partita, "inesistente"), []); // A parità di voti l'ordine è alfabetico, così la UI è stabile. const pari = [v("m3", "g1", "g9", "Zeno"), v("m3", "g2", "g8", "Anna")]; assert.deepEqual( conteggioPartita(pari, "m3").map((c) => c.nome), ["Anna", "Zeno"], ); // --- vincitoriMvp: la parità in testa assegna l'MVP a tutti i pari merito -------------------------- assert.deepEqual(vincitoriMvp(partita), { m1: "Bruno", m2: "Anna" }); assert.deepEqual( vincitoriMvp(pari), { m3: "Anna e Zeno" }, "due voti pari in testa: sono MVP entrambi, a pari merito", ); assert.deepEqual(vincitoriMvp([]), {}); // --- vincitoriMvp: quorum minimo di voti ------------------------------------- assert.equal(VOTI_MINIMI_MVP, 2, "un solo voto non deve mai bastare da solo"); assert.deepEqual( vincitoriMvp([v("m4", "g1", "g2", "Solo")]), {}, "un solo votante, senza concorrenza: sotto quorum, nessun MVP", ); assert.deepEqual( vincitoriMvp([v("m4", "g1", "g2", "Solo"), v("m4", "g3", "g2", "Solo")]), { m4: "Solo" }, "due voti allo stesso candidato: quorum raggiunto", ); // Tre candidati: i primi due pari in testa, un terzo staccato. Deve restare senza MVP, // non basta che il terzo sia sotto: conta solo il confronto fra il primo e il secondo. const triplaPari = [ v("m6", "g1", "g9", "Zeno"), v("m6", "g2", "g8", "Anna"), v("m6", "g3", "g7", "Bea"), ]; assert.deepEqual( vincitoriMvp(triplaPari), { m6: "Anna, Bea e Zeno" }, "tre pari in testa: tre MVP, in ordine alfabetico", ); assert.deepEqual( mvpVintiPerGiocatore(triplaPari), { g9: 1, g8: 1, g7: 1 }, "ognuno dei pari merito conta un titolo", ); // Primo pari col secondo, terzo staccato: il terzo resta fuori. const duePariUnoSotto = [ v("m7", "g1", "g9", "Zeno"), v("m7", "g2", "g9", "Zeno"), v("m7", "g3", "g8", "Anna"), v("m7", "g4", "g8", "Anna"), v("m7", "g5", "g7", "Bea"), ]; assert.deepEqual( mvpPartita(duePariUnoSotto, "m7").map((c) => c.nome), ["Anna", "Zeno"], ); assert.deepEqual(mvpPartita([v("m8", "g1", "g2", "Solo")], "m8"), [], "sotto quorum: nessuno"); assert.deepEqual(mvpPartita([], "m8"), []); // --- elencoNomi --- assert.equal(elencoNomi([]), ""); assert.equal(elencoNomi(["Anna"]), "Anna"); assert.equal(elencoNomi(["Anna", "Bruno"]), "Anna e Bruno"); assert.equal(elencoNomi(["Anna", "Bruno", "Carla"]), "Anna, Bruno e Carla"); // --- mioVoto ----------------------------------------------------------------- assert.equal(mioVoto(partita, "m1", "g1")?.votato_nome, "Bruno"); assert.equal(mioVoto(partita, "m1", "g9"), null, "chi non ha votato non ha voto"); assert.equal(mioVoto(partita, "m9", "g1"), null); // --- mvpVintiPerGiocatore: una vittoria per partita, la parità non conta ----- assert.deepEqual( mvpVintiPerGiocatore(partita), { g2: 1, g5: 1 }, "m1 la vince g2, m2 g5: un titolo a testa", ); assert.deepEqual( mvpVintiPerGiocatore([...partita, ...pari]), { g2: 1, g5: 1, g9: 1, g8: 1 }, "m3 è pari: i due pari merito prendono un titolo ciascuno", ); assert.deepEqual( mvpVintiPerGiocatore([...partita, v("m5", "g1", "g2", "Bruno"), v("m5", "g3", "g2", "Bruno")]), { g2: 2, g5: 1 }, "i titoli si sommano su partite diverse", ); assert.deepEqual( mvpVintiPerGiocatore([...partita, v("m5", "g1", "g2", "Bruno")]), { g2: 1, g5: 1 }, "m5 ha un solo voto: sotto quorum, non conta", ); assert.deepEqual(mvpVintiPerGiocatore([]), {}); // --- votoMvpAperto: due ore dopo il fischio d'inizio ------------------------- const alle = (o: number, m = 0) => new Date(2026, 8, 5, o, m); assert.equal(votoMvpAperto("2026-09-05", "20:30", alle(22, 29)), false, "un minuto prima"); assert.equal(votoMvpAperto("2026-09-05", "20:30", alle(22, 30)), true, "due ore in punto"); assert.equal(votoMvpAperto("2026-09-05", "20:30", alle(23)), true); assert.equal(votoMvpAperto("2026-09-06", "10:00", alle(23)), false, "partita di domani"); assert.equal(votoMvpAperto("2026-09-04", "20:30", alle(0, 1)), true, "partita passata: aperto"); assert.equal(votoMvpAperto("2026-09-05", "", alle(1, 59)), false, "senza ora vale mezzanotte"); assert.equal(votoMvpAperto("2026-09-05", "", alle(2)), true); // --- votoMvpChiuso: 24 ore dopo l'apertura (2 ore dopo l'inizio) ----------------- assert.equal(DURATA_VOTO_MVP_ORE, 24); const giorno = (g: number, o: number, m = 0) => new Date(2026, 8, g, o, m); // partita del 5/9 alle 20:30: apre il 5 alle 22:30, chiude il 6 alle 22:30 assert.equal(votoMvpChiuso("2026-09-05", "20:30", giorno(6, 22, 29)), false, "un minuto prima"); assert.equal(votoMvpChiuso("2026-09-05", "20:30", giorno(6, 22, 30)), true, "24 ore in punto"); assert.equal(votoMvpChiuso("2026-09-05", "20:30", giorno(20, 12)), true, "partita vecchia"); assert.equal(votoMvpChiuso("2026-09-05", "20:30", giorno(5, 12)), false, "prima della partita"); assert.equal(votoMvpInCorso("2026-09-05", "20:30", giorno(5, 21)), false, "non ancora aperto"); assert.equal(votoMvpInCorso("2026-09-05", "20:30", giorno(5, 23)), true, "aperto"); assert.equal(votoMvpInCorso("2026-09-05", "20:30", giorno(7, 8)), false, "chiuso"); assert.equal(votoMvpChiuso("2026-09-05", "", giorno(6, 1, 59)), false, "senza ora vale mezzanotte"); assert.equal(votoMvpChiuso("2026-09-05", "", giorno(6, 2)), true); // --- l'app e il database (M36, M37) hanno la stessa finestra ----------------------- const m37 = readFileSync( "supabase/migrations/20261026170000_m37_voto_mvp_segreto_e_chiusura.sql", "utf8", ); assert.ok( m37.includes(`interval '${ORE_ATTESA_MVP} hours'`), "ORE_ATTESA_MVP e l'attesa di voto_mvp_consentito() devono coincidere", ); assert.ok( m37.includes(`interval '${DURATA_VOTO_MVP_ORE} hours'`), "DURATA_VOTO_MVP_ORE e la chiusura di voto_mvp_consentito() devono coincidere", ); console.log("mvp-voti: ok");