Files
CRAPP/test/integration/notifiche-attive-route.test.ts
T
davideandClaude Sonnet 5 457a0fe23d Aggiunge copertura test e documentazione per Notifiche attive in admin
Estrae la deduplica degli id giocatore in idsConNotificheAttive
(src/lib/notifiche-attive.server.ts), testata a livello unit. Aggiunge
un test di integrazione sui permessi della route (401/403/200), sullo
stesso schema di permessi-route.test.ts. Documenta il nuovo endpoint
in notifiche.md e la dashboard admin a tab in profilo-giocatore.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 14:32:11 +02:00

107 lines
4.1 KiB
TypeScript

/**
* Chi può leggere l'elenco dei giocatori con notifiche push attive:
* `bun test/integration/notifiche-attive-route.test.ts`.
*
* `/api/public/notifiche-attive` legge `push_subscriptions` con la service role e
* salta la RLS (come le route che mandano notifiche, DD-024): il permesso deve stare
* nella route. Serve un database vero per provare token di un giocatore normale e di
* un amministratore, quindi solo stack locale.
*/
import assert from "node:assert/strict";
import { statoLocale } from "../helpers/locale";
import { avviaServer, json } from "../helpers/server";
import { prova, riepilogo, salta } from "../helpers/prova";
const locale = statoLocale();
if (!locale) {
salta("notifiche attive: permessi route", "stack locale non attivo (npx supabase start)");
riepilogo("notifiche-attive-route");
} else {
const { url: SUPABASE, anon: ANON, servizio: SERVIZIO } = locale;
// Il server di sviluppo eredita queste: le route leggono i nomi senza prefisso.
process.env["SUPABASE_URL"] = SUPABASE;
process.env["SUPABASE_PUBLISHABLE_KEY"] = ANON;
process.env["SUPABASE_SERVICE_ROLE_KEY"] = SERVIZIO;
const PASSWORD = "prova-notifiche-123";
const idUtenti: string[] = [];
const authAdmin = { apikey: SERVIZIO, Authorization: `Bearer ${SERVIZIO}` };
async function creaUtente(email: string): Promise<string> {
const res = await fetch(`${SUPABASE}/auth/v1/admin/users`, {
method: "POST",
headers: { ...authAdmin, "content-type": "application/json" },
body: JSON.stringify({ email, password: PASSWORD, email_confirm: true }),
});
const corpo = (await res.json()) as { id?: string };
if (!corpo.id) throw new Error(`creazione utente fallita: ${JSON.stringify(corpo)}`);
idUtenti.push(corpo.id);
return corpo.id;
}
async function accedi(email: string): Promise<string> {
const res = await fetch(`${SUPABASE}/auth/v1/token?grant_type=password`, {
method: "POST",
headers: { apikey: ANON, "content-type": "application/json" },
body: JSON.stringify({ email, password: PASSWORD }),
});
const corpo = (await res.json()) as { access_token?: string };
if (!corpo.access_token) throw new Error(`accesso fallito: ${JSON.stringify(corpo)}`);
return corpo.access_token;
}
const emailGiocatore = `test-notifiche-giocatore-${Date.now()}@example.test`;
const emailAdmin = `test-notifiche-admin-${Date.now()}@example.test`;
await creaUtente(emailGiocatore);
const idAdmin = await creaUtente(emailAdmin);
await fetch(`${SUPABASE}/rest/v1/user_roles`, {
method: "POST",
headers: { ...authAdmin, "content-type": "application/json" },
body: JSON.stringify({ user_id: idAdmin, role: "admin" }),
});
const tokenGiocatore = await accedi(emailGiocatore);
const tokenAdmin = await accedi(emailAdmin);
const server = await avviaServer();
console.log(`notifiche-attive-route su ${server.baseUrl} (database ${SUPABASE})`);
const PERCORSO = "/api/public/notifiche-attive";
const chiama = (intestazioni: Record<string, string> = {}) =>
fetch(`${server.baseUrl}${PERCORSO}`, { headers: intestazioni });
try {
await prova("senza token la route non risponde", async () => {
assert.equal((await chiama()).status, 401);
});
await prova("un giocatore autenticato non è admin", async () => {
const res = await chiama({ authorization: `Bearer ${tokenGiocatore}` });
assert.equal(res.status, 403);
});
await prova("un amministratore riceve l'elenco degli id", async () => {
const res = await chiama({ authorization: `Bearer ${tokenAdmin}` });
assert.equal(res.status, 200);
const corpo = (await json(res)) as { giocatoreIds: unknown };
assert.ok(Array.isArray(corpo.giocatoreIds), "giocatoreIds è un array");
});
} finally {
server.stop();
for (const id of idUtenti) {
await fetch(`${SUPABASE}/rest/v1/user_roles?user_id=eq.${id}`, {
method: "DELETE",
headers: authAdmin,
});
await fetch(`${SUPABASE}/auth/v1/admin/users/${id}`, {
method: "DELETE",
headers: authAdmin,
});
}
riepilogo("notifiche-attive-route");
}
}