// E2E tests for /ajustements endpoints — handler + real DB import { assertEquals, assertExists } from "@std/assert"; import { makeContextWithAffiliation, makeEmployeeContext, makeGetRequest, makeJsonRequest, } from "../helpers/handler.ts"; import { seedAjustements, seedPromotions, seedStudents, seedUes, truncateAll, } from "../helpers/db_integration.ts"; import { handler as ajustementsHandler } from "$apps/notes/api/ajustements.ts"; import { handler as ajustementHandler } from "$apps/notes/api/ajustements/[numEtud]/[idUE].ts"; import { ajustements as ajustementsTable } from "$root/databases/schema.ts"; import { testDb } from "../helpers/db_integration.ts"; // --- GET /ajustements --- Deno.test({ name: "e2e ajustements: GET /ajustements returns all", async fn() { await truncateAll(); await seedPromotions([{ id: "P1" }]); const [s] = await seedStudents([{ nom: "Dupont", prenom: "Jean", idPromo: "P1", }]); const [ue] = await seedUes([{ nom: "UE Info" }]); await seedAjustements([{ numEtud: s.numEtud, idUE: ue.id, valeur: 13.0 }]); const res = await ajustementsHandler.GET!( makeGetRequest("/ajustements"), makeEmployeeContext(), ); assertEquals(res.status, 200); const body = await res.json(); assertEquals(body.length, 1); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: "e2e ajustements: GET /ajustements?numEtud filters by student", async fn() { await truncateAll(); await seedPromotions([{ id: "P1" }]); const [s1] = await seedStudents([{ nom: "Dupont", prenom: "Jean", idPromo: "P1", }]); const [s2] = await seedStudents([{ nom: "Martin", prenom: "Alice", idPromo: "P1", }]); const [ue] = await seedUes([{ nom: "UE Info" }]); await seedAjustements([ { numEtud: s1.numEtud, idUE: ue.id, valeur: 13.0 }, { numEtud: s2.numEtud, idUE: ue.id, valeur: 15.0 }, ]); const res = await ajustementsHandler.GET!( makeGetRequest("/ajustements", { numEtud: String(s1.numEtud) }), makeEmployeeContext(), ); assertEquals(res.status, 200); const body = await res.json(); assertEquals(body.length, 1); assertEquals(body[0].numEtud, s1.numEtud); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: "e2e ajustements: GET /ajustements?numEtud=NaN returns 400", async fn() { await truncateAll(); const res = await ajustementsHandler.GET!( makeGetRequest("/ajustements", { numEtud: "abc" }), makeEmployeeContext(), ); assertEquals(res.status, 400); }, sanitizeResources: false, sanitizeOps: false, }); // --- POST /ajustements --- Deno.test({ name: "e2e ajustements: POST /ajustements creates ajustement (201) as employee", async fn() { await truncateAll(); await seedPromotions([{ id: "P1" }]); const [s] = await seedStudents([{ nom: "Leroy", prenom: "Paul", idPromo: "P1", }]); const [ue] = await seedUes([{ nom: "UE Info" }]); const res = await ajustementsHandler.POST!( makeJsonRequest("/ajustements", "POST", { numEtud: s.numEtud, idUE: ue.id, valeur: 14.5, }), makeEmployeeContext(), ); assertEquals(res.status, 201); const body = await res.json(); assertExists(body.numEtud); assertEquals(body.valeur, 14.5); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: "e2e ajustements: POST /ajustements 403 for non-employee", async fn() { await truncateAll(); const res = await ajustementsHandler.POST!( makeJsonRequest("/ajustements", "POST", { numEtud: 1, idUE: 1, valeur: 10.0, }), makeContextWithAffiliation("student"), ); assertEquals(res.status, 403); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: "e2e ajustements: POST /ajustements 400 on missing fields", async fn() { await truncateAll(); const res = await ajustementsHandler.POST!( makeJsonRequest("/ajustements", "POST", { numEtud: 12345 }), makeEmployeeContext(), ); assertEquals(res.status, 400); }, sanitizeResources: false, sanitizeOps: false, }); // --- GET /ajustements/:numEtud/:idUE --- Deno.test({ name: "e2e ajustements: GET /ajustements/:numEtud/:idUE returns correct ajustement (employee)", async fn() { await truncateAll(); await seedPromotions([{ id: "P1" }]); const [s1, s2] = await seedStudents([ { nom: "Bernard", prenom: "Lucie", idPromo: "P1" }, { nom: "Dupont", prenom: "Jean", idPromo: "P1" }, ]); const [ue1, ue2] = await seedUes([{ nom: "UE Maths" }, { nom: "UE Info" }]); // Plusieurs lignes partageant numEtud=s1 — le handler doit discriminer par idUE await seedAjustements([ { numEtud: s1.numEtud, idUE: ue1.id, valeur: 16.0 }, { numEtud: s1.numEtud, idUE: ue2.id, valeur: 8.0 }, { numEtud: s2.numEtud, idUE: ue1.id, valeur: 12.0 }, ]); const res = await ajustementHandler.GET!( makeGetRequest(`/ajustements/${s1.numEtud}/${ue1.id}`), makeEmployeeContext({ numEtud: String(s1.numEtud), idUE: String(ue1.id), }), ); assertEquals(res.status, 200); const body = await res.json(); assertEquals(body.valeur, 16.0); assertEquals(body.numEtud, s1.numEtud); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: "e2e ajustements: GET /ajustements/:numEtud/:idUE 403 for non-employee", async fn() { await truncateAll(); const res = await ajustementHandler.GET!( makeGetRequest("/ajustements/1/1"), makeContextWithAffiliation("student", { numEtud: "1", idUE: "1" }), ); assertEquals(res.status, 403); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: "e2e ajustements: GET /ajustements/:numEtud/:idUE 404 when not found", async fn() { await truncateAll(); const res = await ajustementHandler.GET!( makeGetRequest("/ajustements/99999/99"), makeEmployeeContext({ numEtud: "99999", idUE: "99" }), ); assertEquals(res.status, 404); }, sanitizeResources: false, sanitizeOps: false, }); // --- PUT /ajustements/:numEtud/:idUE --- Deno.test({ name: "e2e ajustements: PUT /ajustements/:numEtud/:idUE updates only targeted row (employee)", async fn() { await truncateAll(); await seedPromotions([{ id: "P1" }]); const [s] = await seedStudents([{ nom: "Thomas", prenom: "Eva", idPromo: "P1", }]); const [ue1, ue2] = await seedUes([{ nom: "UE Physique" }, { nom: "UE Chimie", }]); // Deux ajustements pour le même étudiant — seul ue1 doit être modifié await seedAjustements([ { numEtud: s.numEtud, idUE: ue1.id, valeur: 10.0 }, { numEtud: s.numEtud, idUE: ue2.id, valeur: 7.0 }, ]); const res = await ajustementHandler.PUT!( makeJsonRequest(`/ajustements/${s.numEtud}/${ue1.id}`, "PUT", { valeur: 19.0, }), makeEmployeeContext({ numEtud: String(s.numEtud), idUE: String(ue1.id) }), ); assertEquals(res.status, 200); const body = await res.json(); assertEquals(body.valeur, 19.0); // ue2 doit rester intact const unchanged = await testDb.select().from(ajustementsTable); const ue2Row = unchanged.find((a) => a.idUE === ue2.id); assertEquals(ue2Row?.valeur, 7.0); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: "e2e ajustements: PUT /ajustements/:numEtud/:idUE 403 for non-employee", async fn() { await truncateAll(); const res = await ajustementHandler.PUT!( makeJsonRequest("/ajustements/1/1", "PUT", { valeur: 10.0 }), makeContextWithAffiliation("student", { numEtud: "1", idUE: "1" }), ); assertEquals(res.status, 403); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: "e2e ajustements: PUT /ajustements/:numEtud/:idUE 404 when not found", async fn() { await truncateAll(); const res = await ajustementHandler.PUT!( makeJsonRequest("/ajustements/99999/99", "PUT", { valeur: 10.0 }), makeEmployeeContext({ numEtud: "99999", idUE: "99" }), ); assertEquals(res.status, 404); }, sanitizeResources: false, sanitizeOps: false, }); // --- DELETE /ajustements/:numEtud/:idUE --- Deno.test({ name: "e2e ajustements: DELETE /ajustements/:numEtud/:idUE deletes only targeted row (employee)", async fn() { await truncateAll(); await seedPromotions([{ id: "P1" }]); const [s] = await seedStudents([{ nom: "Petit", prenom: "Hugo", idPromo: "P1", }]); const [ue1, ue2] = await seedUes([{ nom: "UE Chimie" }, { nom: "UE Bio" }]); // Deux ajustements pour le même étudiant — seul ue1 doit être supprimé await seedAjustements([ { numEtud: s.numEtud, idUE: ue1.id, valeur: 11.0 }, { numEtud: s.numEtud, idUE: ue2.id, valeur: 14.0 }, ]); const res = await ajustementHandler.DELETE!( makeGetRequest(`/ajustements/${s.numEtud}/${ue1.id}`), makeEmployeeContext({ numEtud: String(s.numEtud), idUE: String(ue1.id) }), ); assertEquals(res.status, 204); // ue2 doit toujours exister const remaining = await testDb.select().from(ajustementsTable); assertEquals(remaining.length, 1); assertEquals(remaining[0].idUE, ue2.id); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: "e2e ajustements: DELETE /ajustements/:numEtud/:idUE 403 for non-employee", async fn() { await truncateAll(); const res = await ajustementHandler.DELETE!( makeGetRequest("/ajustements/1/1"), makeContextWithAffiliation("student", { numEtud: "1", idUE: "1" }), ); assertEquals(res.status, 403); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: "e2e ajustements: DELETE /ajustements/:numEtud/:idUE 404 when not found", async fn() { await truncateAll(); const res = await ajustementHandler.DELETE!( makeGetRequest("/ajustements/99999/99"), makeEmployeeContext({ numEtud: "99999", idUE: "99" }), ); assertEquals(res.status, 404); }, sanitizeResources: false, sanitizeOps: false, });