const request = require("supertest"); const app = require("../src/app"); const prisma = require("../src/config/prisma"); const jwt = require("jsonwebtoken"); const bcrypt = require("bcrypt"); jest.mock("../src/config/mailer", () => ({ sendMail: jest.fn().mockResolvedValue(true) })); const emails = { adm: "v-adm@test.local", emp: "v-emp@test.local", a: "v-a@test.local", b: "v-b@test.local" }; const CODES = ["VERIFTICK1", "VERIFTICK2", "VERIFTICK3", "VERIFTICK4", "VERIFTICK5"]; const DAY = 86400000; let ctx = {}; const log = (...a) => console.log(" ", ...a); async function cleanup() { const us = await prisma.user.findMany({ where: { email: { in: [...Object.values(emails), "v-r1@test.local", "v-r2@test.local"] } } }); const ids = us.map((u) => u.id); const ps = await prisma.participation.findMany({ where: { userId: { in: ids } } }); await prisma.claim.deleteMany({ where: { participationId: { in: ps.map((p) => p.id) } } }); await prisma.participation.deleteMany({ where: { userId: { in: ids } } }); await prisma.grandPrizeWinner.deleteMany({ where: { userId: { in: ids } } }); await prisma.ticket.deleteMany({ where: { code: { in: CODES } } }); await prisma.user.deleteMany({ where: { id: { in: ids } } }); } const setStart = (d) => prisma.gameSettings.update({ where: { id: ctx.settings.id }, data: { startDate: new Date(Date.now() - d * DAY) } }); // Un tirage reel peut exister en base : la table GrandPrizeWinner est unique // pour tout le jeu. On la met de cote le temps du test, puis on la restaure. async function mettreDeCoteLeGagnant() { const existant = await prisma.grandPrizeWinner.findFirst(); if (existant) await prisma.grandPrizeWinner.delete({ where: { id: existant.id } }); return existant; } async function restaurerLeGagnant(sauvegarde) { if (!sauvegarde) return; await prisma.grandPrizeWinner.deleteMany({}); await prisma.grandPrizeWinner.create({ data: { id: sauvegarde.id, userId: sauvegarde.userId, prize: sauvegarde.prize, drawnAt: sauvegarde.drawnAt, }, }); } beforeAll(async () => { ctx.gagnantSauvegarde = await mettreDeCoteLeGagnant(); await cleanup(); ctx.settings = await prisma.gameSettings.findFirst({ orderBy: { id: "asc" } }); ctx.orig = ctx.settings.startDate; const lot = await prisma.lot.findFirst({ orderBy: { id: "asc" } }); for (const c of CODES) await prisma.ticket.create({ data: { code: c, lotId: lot.id } }); const h = await bcrypt.hash("Password123", 10); const mk = (e, r) => prisma.user.create({ data: { firstname: "V", lastname: r, email: e, password: h, role: r, isEmailVerified: true } }); ctx.adm = await mk(emails.adm, "ADMIN"); ctx.emp = await mk(emails.emp, "EMPLOYEE"); ctx.a = await mk(emails.a, "CLIENT"); ctx.b = await mk(emails.b, "CLIENT"); const tk = (u) => jwt.sign({ id: u.id, role: u.role }, process.env.JWT_SECRET, { expiresIn: "1h" }); ctx.tadm = tk(ctx.adm); ctx.temp = tk(ctx.emp); ctx.ta = tk(ctx.a); ctx.tb = tk(ctx.b); }); afterAll(async () => { await restaurerLeGagnant(ctx.gagnantSauvegarde); await prisma.gameSettings.update({ where: { id: ctx.settings.id }, data: { startDate: ctx.orig } }); await cleanup(); await prisma.$disconnect(); }); describe("VERIFICATION DES 7 CORRECTIFS", () => { test("#1 ADMIN peut lancer le jeu", async () => { const futur = new Date(Date.now() + 5 * DAY).toISOString(); const r = await request(app).put("/api/admin/game-settings").set("Authorization", `Bearer ${ctx.tadm}`).send({ startDate: futur }); log("PUT game-settings ->", r.status, r.body.message); expect(r.status).toBe(200); const g = await request(app).get("/api/admin/game-settings").set("Authorization", `Bearer ${ctx.tadm}`).expect(200); log("GET game-settings -> participation ouverte :", g.body.gameWindow.isParticipationOpen, "(jeu pas encore commence)"); const bad = await request(app).put("/api/admin/game-settings").set("Authorization", `Bearer ${ctx.tadm}`).send({ startDate: "pas-une-date" }); log("date invalide ->", bad.status, bad.body.message); expect(bad.status).toBe(400); const nonAdmin = await request(app).put("/api/admin/game-settings").set("Authorization", `Bearer ${ctx.ta}`).send({ startDate: futur }); log("client tente de lancer le jeu ->", nonAdmin.status); expect(nonAdmin.status).toBe(403); }); test("#2 participation refusee apres 30 jours", async () => { await setStart(45); const r = await request(app).post("/api/game/participate").set("Authorization", `Bearer ${ctx.ta}`).send({ code: CODES[0] }); log("jour 45 ->", r.status, r.body.message); expect(r.status).toBe(410); await setStart(1); const ok = await request(app).post("/api/game/participate").set("Authorization", `Bearer ${ctx.ta}`).send({ code: CODES[0] }); log("jour 1 ->", ok.status, ok.body.message || "accepte"); expect(ok.status).toBe(201); ctx.claimId = ok.body.claim.id; }); test("#3 expiration derivee et reversible", async () => { await setStart(70); const g1 = await request(app).get("/api/game/my-gains").set("Authorization", `Bearer ${ctx.ta}`).expect(200); log("jour 70, statut affiche :", g1.body.gains.map((g) => g.claimStatus).join(",")); expect(g1.body.gains[0].claimStatus).toBe("CANCELLED"); const enBase = await prisma.claim.findUnique({ where: { id: ctx.claimId } }); log("statut REEL en base :", enBase.status, "(non ecrase)"); expect(enBase.status).toBe("PENDING"); await setStart(1); const g2 = await request(app).get("/api/game/my-gains").set("Authorization", `Bearer ${ctx.ta}`).expect(200); log("retour jour 1, statut :", g2.body.gains.map((g) => g.claimStatus).join(","), "(reversible)"); expect(g2.body.gains[0].claimStatus).toBe("PENDING"); }); test("#4 tirage refuse pendant le jeu", async () => { await setStart(1); const r = await request(app).post("/api/admin/draw").set("Authorization", `Bearer ${ctx.tadm}`); log("tirage pendant le jeu ->", r.status, r.body.message); expect(r.status).toBe(409); await setStart(35); const r2 = await request(app).post("/api/admin/draw").set("Authorization", `Bearer ${ctx.tadm}`); log("tirage apres cloture ->", r2.status, r2.body.message, r2.body.totalEligibleParticipants ? `(${r2.body.totalEligibleParticipants} eligibles)` : ""); expect(r2.status).toBe(201); await prisma.grandPrizeWinner.deleteMany({ where: { userId: { in: [ctx.a.id, ctx.b.id] } } }); await setStart(1); }); test("#5 admin et employe ne peuvent plus jouer", async () => { const r1 = await request(app).post("/api/game/participate").set("Authorization", `Bearer ${ctx.tadm}`).send({ code: CODES[1] }); const r2 = await request(app).post("/api/game/participate").set("Authorization", `Bearer ${ctx.temp}`).send({ code: CODES[1] }); log("ADMIN ->", r1.status, r1.body.message); log("EMPLOYE ->", r2.status, r2.body.message); expect(r1.status).toBe(403); expect(r2.status).toBe(403); const t = await prisma.ticket.findUnique({ where: { code: CODES[1] } }); log("le ticket reste vierge :", t.isUsed === false); expect(t.isUsed).toBe(false); }); test("#6 suppression de compte ne remet pas le ticket en jeu", async () => { await request(app).post("/api/game/participate").set("Authorization", `Bearer ${ctx.tb}`).send({ code: CODES[2] }).expect(201); await request(app).delete("/api/auth/account").set("Authorization", `Bearer ${ctx.tb}`).expect(200); const t = await prisma.ticket.findUnique({ where: { code: CODES[2] } }); log("apres suppression : isUsed =", t.isUsed, "| userId =", t.userId); expect(t.isUsed).toBe(true); const rejoue = await request(app).post("/api/game/participate").set("Authorization", `Bearer ${ctx.ta}`).send({ code: CODES[2] }); log("tentative de rejouer le code ->", rejoue.status, rejoue.body.message); expect(rejoue.status).toBe(409); }); test("#7 course sur un meme code", async () => { const h = await bcrypt.hash("Password123", 10); const c1 = await prisma.user.create({ data: { firstname: "R", lastname: "1", email: "v-r1@test.local", password: h, role: "CLIENT", isEmailVerified: true } }); const c2 = await prisma.user.create({ data: { firstname: "R", lastname: "2", email: "v-r2@test.local", password: h, role: "CLIENT", isEmailVerified: true } }); const t1 = jwt.sign({ id: c1.id, role: "CLIENT" }, process.env.JWT_SECRET, { expiresIn: "1h" }); const t2 = jwt.sign({ id: c2.id, role: "CLIENT" }, process.env.JWT_SECRET, { expiresIn: "1h" }); const [r1, r2] = await Promise.all([ request(app).post("/api/game/participate").set("Authorization", `Bearer ${t1}`).send({ code: CODES[3] }), request(app).post("/api/game/participate").set("Authorization", `Bearer ${t2}`).send({ code: CODES[3] }), ]); const codes = [r1.status, r2.status].sort(); log("statuts obtenus :", codes.join(" + "), "(201 + 409 attendu, aucun 500)"); expect(codes).toEqual([201, 409]); const n = await prisma.participation.count({ where: { ticket: { code: CODES[3] } } }); log("participations pour ce ticket :", n); expect(n).toBe(1); }); });