test: add full test coverage for notes, ues, ue-modules, ajustements, enseignements, users
- Unit tests (mock DB + API) for all missing endpoints - Integration tests (Drizzle direct) for all missing entities - E2E tests (handler + real DB) for all missing endpoints - Robustness tests: invalid inputs, SQL injection, type errors, business rule violations - Seed helpers: seedNotes, seedUeModules, seedEnseignements, seedAjustements - Add test:coverage and test:coverage:html tasks to deno.json Tests expose known handler bugs (marked [BUG] in test names): - ajustements PUT/DELETE: .where() without and() modifies all rows for student - Missing try/catch in modules, users, enseignements handlers - Whitespace accepted as valid string values - No type or business rule validation (note bounds, coeff >= 0)
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
// E2E tests for /enseignements endpoints — handler + real DB
|
||||
|
||||
import { assertEquals, assertExists } from "@std/assert";
|
||||
import {
|
||||
makeContextWithAffiliation,
|
||||
makeEmployeeContext,
|
||||
makeGetRequest,
|
||||
makeJsonRequest,
|
||||
} from "../helpers/handler.ts";
|
||||
import {
|
||||
seedEnseignements,
|
||||
seedModules,
|
||||
seedPromotions,
|
||||
seedUsers,
|
||||
truncateAll,
|
||||
} from "../helpers/db_integration.ts";
|
||||
import { handler as enseignementsHandler } from "$apps/admin/api/enseignements.ts";
|
||||
import { handler as enseignementHandler } from "$apps/admin/api/enseignements/[idProf]/[idModule]/[idPromo].ts";
|
||||
|
||||
// --- POST /enseignements ---
|
||||
|
||||
Deno.test({
|
||||
name: "e2e enseignements: POST /enseignements creates enseignement (201) as employee",
|
||||
async fn() {
|
||||
await truncateAll();
|
||||
await seedUsers([{ id: "prof.dupont", nom: "Dupont", prenom: "Jean" }]);
|
||||
await seedModules([{ id: "M1", nom: "Mod A" }]);
|
||||
await seedPromotions([{ id: "P1" }]);
|
||||
const res = await enseignementsHandler.POST!(
|
||||
makeJsonRequest("/enseignements", "POST", { idProf: "prof.dupont", idModule: "M1", idPromo: "P1" }),
|
||||
makeEmployeeContext(),
|
||||
);
|
||||
assertEquals(res.status, 201);
|
||||
const body = await res.json();
|
||||
assertExists(body.idProf);
|
||||
assertEquals(body.idModule, "M1");
|
||||
},
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "e2e enseignements: POST /enseignements 403 for non-employee",
|
||||
async fn() {
|
||||
await truncateAll();
|
||||
const res = await enseignementsHandler.POST!(
|
||||
makeJsonRequest("/enseignements", "POST", { idProf: "p", idModule: "M1", idPromo: "P1" }),
|
||||
makeContextWithAffiliation("student"),
|
||||
);
|
||||
assertEquals(res.status, 403);
|
||||
},
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "e2e enseignements: POST /enseignements 400 on missing fields",
|
||||
async fn() {
|
||||
await truncateAll();
|
||||
const res = await enseignementsHandler.POST!(
|
||||
makeJsonRequest("/enseignements", "POST", { idProf: "prof.dupont" }),
|
||||
makeEmployeeContext(),
|
||||
);
|
||||
assertEquals(res.status, 400);
|
||||
},
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "e2e enseignements: POST /enseignements 409 on duplicate",
|
||||
async fn() {
|
||||
await truncateAll();
|
||||
await seedUsers([{ id: "prof.dupont", nom: "Dupont", prenom: "Jean" }]);
|
||||
await seedModules([{ id: "M1", nom: "Mod A" }]);
|
||||
await seedPromotions([{ id: "P1" }]);
|
||||
await seedEnseignements([{ idProf: "prof.dupont", idModule: "M1", idPromo: "P1" }]);
|
||||
const res = await enseignementsHandler.POST!(
|
||||
makeJsonRequest("/enseignements", "POST", { idProf: "prof.dupont", idModule: "M1", idPromo: "P1" }),
|
||||
makeEmployeeContext(),
|
||||
);
|
||||
assertEquals(res.status, 409);
|
||||
},
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
});
|
||||
|
||||
// --- GET /enseignements/:idProf/:idModule/:idPromo ---
|
||||
|
||||
Deno.test({
|
||||
name: "e2e enseignements: GET /enseignements/:idProf/:idModule/:idPromo returns enseignement (employee)",
|
||||
async fn() {
|
||||
await truncateAll();
|
||||
await seedUsers([{ id: "prof.dupont", nom: "Dupont", prenom: "Jean" }]);
|
||||
await seedModules([{ id: "M1", nom: "Mod A" }]);
|
||||
await seedPromotions([{ id: "P1" }]);
|
||||
await seedEnseignements([{ idProf: "prof.dupont", idModule: "M1", idPromo: "P1" }]);
|
||||
const res = await enseignementHandler.GET!(
|
||||
makeGetRequest("/enseignements/prof.dupont/M1/P1"),
|
||||
makeEmployeeContext({ idProf: "prof.dupont", idModule: "M1", idPromo: "P1" }),
|
||||
);
|
||||
assertEquals(res.status, 200);
|
||||
const body = await res.json();
|
||||
assertEquals(body.idProf, "prof.dupont");
|
||||
assertEquals(body.idModule, "M1");
|
||||
},
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "e2e enseignements: GET /enseignements/:idProf/:idModule/:idPromo 403 for non-employee",
|
||||
async fn() {
|
||||
await truncateAll();
|
||||
const res = await enseignementHandler.GET!(
|
||||
makeGetRequest("/enseignements/p/M1/P1"),
|
||||
makeContextWithAffiliation("student", { idProf: "p", idModule: "M1", idPromo: "P1" }),
|
||||
);
|
||||
assertEquals(res.status, 403);
|
||||
},
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "e2e enseignements: GET /enseignements/:idProf/:idModule/:idPromo 404 when not found",
|
||||
async fn() {
|
||||
await truncateAll();
|
||||
const res = await enseignementHandler.GET!(
|
||||
makeGetRequest("/enseignements/ghost/GHOST/GHOST"),
|
||||
makeEmployeeContext({ idProf: "ghost", idModule: "GHOST", idPromo: "GHOST" }),
|
||||
);
|
||||
assertEquals(res.status, 404);
|
||||
},
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
});
|
||||
|
||||
// --- DELETE /enseignements/:idProf/:idModule/:idPromo ---
|
||||
|
||||
Deno.test({
|
||||
name: "e2e enseignements: DELETE /enseignements/:idProf/:idModule/:idPromo returns 204 (employee)",
|
||||
async fn() {
|
||||
await truncateAll();
|
||||
await seedUsers([{ id: "prof.dupont", nom: "Dupont", prenom: "Jean" }]);
|
||||
await seedModules([{ id: "M1", nom: "Mod A" }]);
|
||||
await seedPromotions([{ id: "P1" }]);
|
||||
await seedEnseignements([{ idProf: "prof.dupont", idModule: "M1", idPromo: "P1" }]);
|
||||
const res = await enseignementHandler.DELETE!(
|
||||
makeGetRequest("/enseignements/prof.dupont/M1/P1"),
|
||||
makeEmployeeContext({ idProf: "prof.dupont", idModule: "M1", idPromo: "P1" }),
|
||||
);
|
||||
assertEquals(res.status, 204);
|
||||
},
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "e2e enseignements: DELETE /enseignements/:idProf/:idModule/:idPromo 403 for non-employee",
|
||||
async fn() {
|
||||
await truncateAll();
|
||||
const res = await enseignementHandler.DELETE!(
|
||||
makeGetRequest("/enseignements/p/M1/P1"),
|
||||
makeContextWithAffiliation("student", { idProf: "p", idModule: "M1", idPromo: "P1" }),
|
||||
);
|
||||
assertEquals(res.status, 403);
|
||||
},
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "e2e enseignements: DELETE /enseignements/:idProf/:idModule/:idPromo 404 when not found",
|
||||
async fn() {
|
||||
await truncateAll();
|
||||
const res = await enseignementHandler.DELETE!(
|
||||
makeGetRequest("/enseignements/ghost/GHOST/GHOST"),
|
||||
makeEmployeeContext({ idProf: "ghost", idModule: "GHOST", idPromo: "GHOST" }),
|
||||
);
|
||||
assertEquals(res.status, 404);
|
||||
},
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
});
|
||||
Reference in New Issue
Block a user