Sécurisation de l'API et refonte des tests backend

- helmet et express-rate-limit sur les routes d'authentification
- CORS restreint aux origines de FRONTEND_URL
- CSP désactivée uniquement sur /api-docs pour Swagger UI
- ajout du test de tirage et simplification du test e2e
- suppression de tests/setup.js devenu inutile

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Emma781227
2026-08-17 00:22:54 +02:00
parent a2543cf4ce
commit 20c865a071
10 changed files with 219 additions and 132 deletions

View File

@@ -10,125 +10,88 @@ jest.mock("../src/config/mailer", () => ({
}));
// ------------------------
// Réinitialisation DB avant tests
// Ce test tourne sur la même base que le reste du dev (500 000 tickets
// réels du jeu-concours). On ne touche donc JAMAIS les tables globales
// (pas de deleteMany sur claim/participation/ticket/user/lot) : tout ce
// que ce fichier crée est identifiable de façon unique et nettoyé dans
// afterAll, rien d'autre.
// ------------------------
const testEmail = "e2e-flow-test@test.com";
const ticketCode = "ZZE2ETEST1";
let token;
let createdTicketId;
beforeAll(async () => {
await prisma.claim.deleteMany();
await prisma.participation.deleteMany();
await prisma.ticket.deleteMany();
await prisma.user.deleteMany();
await prisma.user.deleteMany({ where: { email: testEmail } });
await prisma.ticket.deleteMany({ where: { code: ticketCode } });
});
afterAll(async () => {
const user = await prisma.user.findUnique({ where: { email: testEmail } });
if (user) {
await prisma.claim.deleteMany({ where: { participation: { userId: user.id } } });
await prisma.participation.deleteMany({ where: { userId: user.id } });
}
if (createdTicketId) {
await prisma.ticket.delete({ where: { id: createdTicketId } }).catch(() => {});
}
if (user) {
await prisma.user.delete({ where: { id: user.id } });
}
await prisma.$disconnect();
});
describe("THE TIP TOP - FULL E2E FLOW", () => {
let token;
let ticketCode = "ABC123";
// ------------------------
// 1. REGISTER
// ------------------------
test("Register user", async () => {
const res = await request(app)
.post("/api/auth/register")
.send({
firstname: "Test",
lastname: "User",
email: "test@test.com",
password: "Password123",
});
const res = await request(app).post("/api/auth/register").send({
firstname: "E2E",
lastname: "Test",
email: testEmail,
password: "Password123",
});
expect([201, 409]).toContain(res.statusCode);
expect(res.statusCode).toBe(201);
});
// ------------------------
// 2. LOGIN
// ------------------------
test("Login user", async () => {
// Validation du user pour bypasser vérification email
await prisma.user.update({
where: { email: "test@test.com" },
where: { email: testEmail },
data: { isEmailVerified: true },
});
const res = await request(app)
.post("/api/auth/login")
.send({
email: "test@test.com",
password: "Password123",
});
const res = await request(app).post("/api/auth/login").send({
email: testEmail,
password: "Password123",
});
expect(res.statusCode).toBe(200);
expect(res.body.token).toBeDefined();
token = res.body.token;
});
// ------------------------
// 3. PARTICIPATE GAME
// ------------------------
test("Participate to game", async () => {
const res = await request(app)
.post("/api/game/participate")
.set("Authorization", `Bearer ${token}`)
.send({ code: ticketCode });
expect([200, 201, 404, 409, 400]).toContain(res.statusCode);
console.log("Ticket in DB before participate:", ticketCode);
});
// ------------------------
// 4. MY GAINS
// ------------------------
test("Get my gains", async () => {
const res = await request(app)
.get("/api/game/my-gains")
.set("Authorization", `Bearer ${token}`);
expect(res.statusCode).toBe(200);
expect(res.body.gains).toBeDefined();
});
// ------------------------
// 3. GENERER UN TICKET
// ------------------------
test("Create a ticket for testing", async () => {
const lot = await prisma.lot.create({
data: {
name: "Lot Test",
description: "Lot pour test E2E",
value: 10,
percentage: 100
},
});
test("Create a dedicated ticket for this test", async () => {
const lot = await prisma.lot.findFirst();
expect(lot).not.toBeNull();
const ticket = await prisma.ticket.create({
data: {
code: ticketCode,
lotId: lot.id
}
data: { code: ticketCode, lotId: lot.id },
});
createdTicketId = ticket.id;
expect(ticket).toBeDefined();
expect(ticket.code).toBe(ticketCode);
});
// ------------------------
// 4. PARTICIPATE GAME
// ------------------------
test("Participate to game", async () => {
const res = await request(app)
.post("/api/game/participate")
.set("Authorization", `Bearer ${token}`)
.send({ code: ticketCode });
expect([200, 201, 404, 409, 400]).toContain(res.statusCode);
expect(res.statusCode).toBe(201);
expect(res.body.gain).toBeDefined();
});
// ------------------------
// 5. MY GAINS
// ------------------------
test("Get my gains", async () => {
const res = await request(app)
.get("/api/game/my-gains")
@@ -136,11 +99,9 @@ describe("THE TIP TOP - FULL E2E FLOW", () => {
expect(res.statusCode).toBe(200);
expect(res.body.gains).toBeDefined();
expect(res.body.gains.some((g) => g.ticketCode === ticketCode)).toBe(true);
});
// ------------------------
// 6. ADMIN STATS (CLIENT access denied)
// ------------------------
test("Admin stats - CLIENT access denied", async () => {
const res = await request(app)
.get("/api/admin/stats")
@@ -149,4 +110,4 @@ describe("THE TIP TOP - FULL E2E FLOW", () => {
expect(res.statusCode).toBe(403);
expect(res.body.message || res.body).toBeDefined();
});
});
});