fix: correct handler bugs exposed by test suite
- ajustements [numEtud]/[idUE]: fix .where() missing and() — PUT/DELETE were applying only numEtud condition, modifying all rows for a student - modules/users/enseignements POST: add try/catch, return 500 on invalid JSON - modules/[idModule] PUT: add try/catch + type check on nom (string required) - modules POST: add .trim() check to reject whitespace-only id/nom - users POST: add .trim() check to reject whitespace-only id/nom/prenom - ues POST: add .trim() check to reject whitespace-only nom - notes POST: add type check (typeof number) and bounds check (0 ≤ note ≤ 20) - ue-modules POST: add coeff >= 0 validation Update robustness tests to reflect fixed behavior (remove [BUG] labels, replace assertRejects with status code assertions).
This commit is contained in:
@@ -26,11 +26,12 @@ export const handler: Handlers<null, AuthenticatedState> = {
|
|||||||
return FORBIDDEN;
|
return FORBIDDEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
const body: {
|
let body: { idProf: string; idModule: string; idPromo: string };
|
||||||
idProf: string;
|
try {
|
||||||
idModule: string;
|
body = await request.json();
|
||||||
idPromo: string;
|
} catch {
|
||||||
} = await request.json();
|
return new Response(null, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
if (!body.idProf || !body.idModule || !body.idPromo) {
|
if (!body.idProf || !body.idModule || !body.idPromo) {
|
||||||
return new Response(null, { status: 400 });
|
return new Response(null, { status: 400 });
|
||||||
|
|||||||
@@ -31,9 +31,14 @@ export const handler: Handlers<null, AuthenticatedState> = {
|
|||||||
return new Response(null, { status: 403 });
|
return new Response(null, { status: 403 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const body: { id: string; nom: string } = await request.json();
|
let body: { id: string; nom: string };
|
||||||
|
try {
|
||||||
|
body = await request.json();
|
||||||
|
} catch {
|
||||||
|
return new Response(null, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
if (!body.id || !body.nom) {
|
if (!body.id || !body.id.trim() || !body.nom || !body.nom.trim()) {
|
||||||
return new Response(null, { status: 400 });
|
return new Response(null, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,16 @@ export const handler: Handlers<null, AuthenticatedState> = {
|
|||||||
request: Request,
|
request: Request,
|
||||||
context: FreshContext<AuthenticatedState>,
|
context: FreshContext<AuthenticatedState>,
|
||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
const body: { nom: string } = await request.json();
|
let body: { nom: string };
|
||||||
|
try {
|
||||||
|
body = await request.json();
|
||||||
|
} catch {
|
||||||
|
return new Response(null, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof body.nom !== "string") {
|
||||||
|
return new Response(null, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
const [updated] = await db
|
const [updated] = await db
|
||||||
.update(modules)
|
.update(modules)
|
||||||
|
|||||||
@@ -27,10 +27,17 @@ export const handler: Handlers<null, AuthenticatedState> = {
|
|||||||
request: Request,
|
request: Request,
|
||||||
_context: FreshContext<AuthenticatedState>,
|
_context: FreshContext<AuthenticatedState>,
|
||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
const body: { id: string; nom: string; prenom: string; idRole: number } =
|
let body: { id: string; nom: string; prenom: string; idRole: number };
|
||||||
await request.json();
|
try {
|
||||||
|
body = await request.json();
|
||||||
|
} catch {
|
||||||
|
return new Response(null, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
if (!body.id || !body.nom || !body.prenom) {
|
if (
|
||||||
|
!body.id || !body.id.trim() || !body.nom || !body.nom.trim() ||
|
||||||
|
!body.prenom || !body.prenom.trim()
|
||||||
|
) {
|
||||||
return new Response(null, { status: 400 });
|
return new Response(null, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { FreshContext, Handlers } from "$fresh/server.ts";
|
|||||||
import { db } from "$root/databases/db.ts";
|
import { db } from "$root/databases/db.ts";
|
||||||
import { ajustements } from "$root/databases/schema.ts";
|
import { ajustements } from "$root/databases/schema.ts";
|
||||||
import { AuthenticatedState } from "$root/defaults/interfaces.ts";
|
import { AuthenticatedState } from "$root/defaults/interfaces.ts";
|
||||||
import { eq } from "npm:drizzle-orm@0.45.2";
|
import { and, eq } from "npm:drizzle-orm@0.45.2";
|
||||||
|
|
||||||
const NOT_FOUND = new Response(
|
const NOT_FOUND = new Response(
|
||||||
JSON.stringify({ error: "Ajustement introuvable" }),
|
JSON.stringify({ error: "Ajustement introuvable" }),
|
||||||
@@ -31,7 +31,7 @@ export const handler: Handlers<null, AuthenticatedState> = {
|
|||||||
const ajustement = await db
|
const ajustement = await db
|
||||||
.select()
|
.select()
|
||||||
.from(ajustements)
|
.from(ajustements)
|
||||||
.where(eq(ajustements.numEtud, numEtud), eq(ajustements.idUE, idUE))
|
.where(and(eq(ajustements.numEtud, numEtud), eq(ajustements.idUE, idUE)))
|
||||||
.then((rows) => rows[0] ?? null);
|
.then((rows) => rows[0] ?? null);
|
||||||
|
|
||||||
if (!ajustement) return NOT_FOUND;
|
if (!ajustement) return NOT_FOUND;
|
||||||
@@ -69,7 +69,7 @@ export const handler: Handlers<null, AuthenticatedState> = {
|
|||||||
const [updated] = await db
|
const [updated] = await db
|
||||||
.update(ajustements)
|
.update(ajustements)
|
||||||
.set({ valeur: body.valeur })
|
.set({ valeur: body.valeur })
|
||||||
.where(eq(ajustements.numEtud, numEtud), eq(ajustements.idUE, idUE))
|
.where(and(eq(ajustements.numEtud, numEtud), eq(ajustements.idUE, idUE)))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (!updated) return NOT_FOUND;
|
if (!updated) return NOT_FOUND;
|
||||||
@@ -97,7 +97,7 @@ export const handler: Handlers<null, AuthenticatedState> = {
|
|||||||
|
|
||||||
const [deleted] = await db
|
const [deleted] = await db
|
||||||
.delete(ajustements)
|
.delete(ajustements)
|
||||||
.where(eq(ajustements.numEtud, numEtud), eq(ajustements.idUE, idUE))
|
.where(and(eq(ajustements.numEtud, numEtud), eq(ajustements.idUE, idUE)))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
if (!deleted) return NOT_FOUND;
|
if (!deleted) return NOT_FOUND;
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ export const handler: Handlers = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof note !== "number" || note < 0 || note > 20) {
|
||||||
|
return new Response("Champ 'note' doit être un nombre entre 0 et 20", {
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const result = await db.insert(notes).values({ note, numEtud, idModule })
|
const result = await db.insert(notes).values({ note, numEtud, idModule })
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ export const handler: Handlers = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof coeff !== "number" || coeff < 0) {
|
||||||
|
return new Response("Champ 'coeff' doit être un nombre >= 0", {
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const result = await db.insert(ueModules).values({
|
const result = await db.insert(ueModules).values({
|
||||||
idModule,
|
idModule,
|
||||||
idUE,
|
idUE,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const handler: Handlers = {
|
|||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const { nom } = body;
|
const { nom } = body;
|
||||||
|
|
||||||
if (!nom) {
|
if (!nom || !nom.trim()) {
|
||||||
return new Response("Champ 'nom' manquant", { status: 400 });
|
return new Response("Champ 'nom' manquant", { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+116
-83
@@ -4,7 +4,7 @@
|
|||||||
// Les tests marqués [BUG] représentent le comportement ATTENDU — ils échouent
|
// Les tests marqués [BUG] représentent le comportement ATTENDU — ils échouent
|
||||||
// intentionnellement pour exposer un bug dans le handler ciblé.
|
// intentionnellement pour exposer un bug dans le handler ciblé.
|
||||||
|
|
||||||
import { assertEquals, assertRejects } from "@std/assert";
|
import { assertEquals } from "@std/assert";
|
||||||
import {
|
import {
|
||||||
makeContextWithAffiliation,
|
makeContextWithAffiliation,
|
||||||
makeEmployeeContext,
|
makeEmployeeContext,
|
||||||
@@ -90,7 +90,8 @@ Deno.test({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness: POST /ajustements malformed JSON → 500 (try/catch présent)",
|
name:
|
||||||
|
"robustness: POST /ajustements malformed JSON → 500 (try/catch présent)",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
const res = await ajustementsHandler.POST!(
|
const res = await ajustementsHandler.POST!(
|
||||||
@@ -103,49 +104,43 @@ Deno.test({
|
|||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handlers SANS try/catch — throwent au lieu de retourner 500
|
|
||||||
// [BUG] Ces handlers devraient retourner 500, pas throw
|
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /modules malformed JSON → throw (pas de try/catch)",
|
name: "robustness: POST /modules malformed JSON → 500",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await assertRejects(() =>
|
const res = await modulesHandler.POST!(
|
||||||
modulesHandler.POST!(
|
makeMalformedRequest("/modules"),
|
||||||
makeMalformedRequest("/modules"),
|
makeEmployeeContext(),
|
||||||
makeEmployeeContext(),
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
assertEquals(res.status, 500);
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /enseignements malformed JSON → throw (pas de try/catch)",
|
name: "robustness: POST /enseignements malformed JSON → 500",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await assertRejects(() =>
|
const res = await enseignementsHandler.POST!(
|
||||||
enseignementsHandler.POST!(
|
makeMalformedRequest("/enseignements"),
|
||||||
makeMalformedRequest("/enseignements"),
|
makeEmployeeContext(),
|
||||||
makeEmployeeContext(),
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
assertEquals(res.status, 500);
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /users malformed JSON → throw (pas de try/catch)",
|
name: "robustness: POST /users malformed JSON → 500",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await assertRejects(() =>
|
const res = await usersHandler.POST!(
|
||||||
usersHandler.POST!(
|
makeMalformedRequest("/users"),
|
||||||
makeMalformedRequest("/users"),
|
makeEmployeeContext(),
|
||||||
makeEmployeeContext(),
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
assertEquals(res.status, 500);
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
@@ -170,15 +165,14 @@ Deno.test({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /modules sans body → throw (pas de try/catch)",
|
name: "robustness: POST /modules sans body → 500",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await assertRejects(() =>
|
const res = await modulesHandler.POST!(
|
||||||
modulesHandler.POST!(
|
makeEmptyBodyRequest("/modules"),
|
||||||
makeEmptyBodyRequest("/modules"),
|
makeEmployeeContext(),
|
||||||
makeEmployeeContext(),
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
assertEquals(res.status, 500);
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
@@ -235,52 +229,42 @@ Deno.test({
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /modules id=espaces → devrait être 400, retourne 201",
|
name: "robustness: POST /modules id=espaces → 400",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
const res = await modulesHandler.POST!(
|
const res = await modulesHandler.POST!(
|
||||||
makeJsonRequest("/modules", "POST", { id: " ", nom: "Test" }),
|
makeJsonRequest("/modules", "POST", { id: " ", nom: "Test" }),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
// Le handler vérifie !body.id → " " est truthy → passe → s'insère
|
assertEquals(res.status, 400);
|
||||||
// Comportement attendu : 400
|
|
||||||
// Comportement réel : 201 (bug : pas de trim())
|
|
||||||
assertEquals(res.status, 400); // ← va échouer, expose le bug
|
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /ues nom=espaces → devrait être 400, retourne 201",
|
name: "robustness: POST /ues nom=espaces → 400",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
const res = await uesHandler.POST!(
|
const res = await uesHandler.POST!(
|
||||||
makeJsonRequest("/ues", "POST", { nom: " " }),
|
makeJsonRequest("/ues", "POST", { nom: " " }),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
assertEquals(res.status, 400); // ← va échouer, expose le bug
|
assertEquals(res.status, 400);
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /users id=espaces → devrait être 400, retourne 201",
|
name: "robustness: POST /users id=espaces → 400",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await assertRejects(
|
const res = await usersHandler.POST!(
|
||||||
// sans try/catch + whitespace id → s'insère (ou throw si DB rejette)
|
makeJsonRequest("/users", "POST", { id: " ", nom: "X", prenom: "Y" }),
|
||||||
// Dans tous les cas le handler ne valide pas correctement
|
makeEmployeeContext(),
|
||||||
async () => {
|
|
||||||
const res = await usersHandler.POST!(
|
|
||||||
makeJsonRequest("/users", "POST", { id: " ", nom: "X", prenom: "Y" }),
|
|
||||||
makeEmployeeContext(),
|
|
||||||
);
|
|
||||||
// Si pas de throw : le handler a inséré des espaces en DB
|
|
||||||
assertEquals(res.status, 400);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
assertEquals(res.status, 400);
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
@@ -291,36 +275,40 @@ Deno.test({
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /notes note=string → devrait être 400, retourne 500",
|
name: "robustness: POST /notes note=string → 400",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await seedPromotions([{ id: "P1" }]);
|
await seedPromotions([{ id: "P1" }]);
|
||||||
const [s] = await seedStudents([{ nom: "Test", prenom: "User", idPromo: "P1" }]);
|
const [s] = await seedStudents([{
|
||||||
|
nom: "Test",
|
||||||
|
prenom: "User",
|
||||||
|
idPromo: "P1",
|
||||||
|
}]);
|
||||||
await seedModules([{ id: "M1", nom: "Mod" }]);
|
await seedModules([{ id: "M1", nom: "Mod" }]);
|
||||||
const res = await notesHandler.POST!(
|
const res = await notesHandler.POST!(
|
||||||
makeJsonRequest("/notes", "POST", { note: "pas-un-nombre", numEtud: s.numEtud, idModule: "M1" }),
|
makeJsonRequest("/notes", "POST", {
|
||||||
|
note: "pas-un-nombre",
|
||||||
|
numEtud: s.numEtud,
|
||||||
|
idModule: "M1",
|
||||||
|
}),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
// "pas-un-nombre" !== undefined → passe la validation → DB rejette → 500
|
assertEquals(res.status, 400);
|
||||||
// Comportement attendu : 400 (validation de type)
|
|
||||||
// Comportement réel : 500
|
|
||||||
assertEquals(res.status, 400); // ← va échouer, expose le bug
|
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: PUT /modules/:id nom=number → devrait être 400, throw ou insère",
|
name: "robustness: PUT /modules/:id nom=number → 400",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await seedModules([{ id: "M1", nom: "Mod" }]);
|
await seedModules([{ id: "M1", nom: "Mod" }]);
|
||||||
await assertRejects(() =>
|
const res = await moduleHandler.PUT!(
|
||||||
moduleHandler.PUT!(
|
makeJsonRequest("/modules/M1", "PUT", { nom: 42 }),
|
||||||
makeJsonRequest("/modules/M1", "PUT", { nom: 42 }),
|
makeEmployeeContext({ idModule: "M1" }),
|
||||||
makeEmployeeContext({ idModule: "M1" }),
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
assertEquals(res.status, 400);
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
@@ -331,12 +319,17 @@ Deno.test({
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /ajustements numEtud=0 → 400 pour mauvaise raison",
|
name:
|
||||||
|
"robustness [BUG]: POST /ajustements numEtud=0 → 400 pour mauvaise raison",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
const [ue] = await seedUes([{ nom: "UE Info" }]);
|
const [ue] = await seedUes([{ nom: "UE Info" }]);
|
||||||
const res = await ajustementsHandler.POST!(
|
const res = await ajustementsHandler.POST!(
|
||||||
makeJsonRequest("/ajustements", "POST", { numEtud: 0, idUE: ue.id, valeur: 10.0 }),
|
makeJsonRequest("/ajustements", "POST", {
|
||||||
|
numEtud: 0,
|
||||||
|
idUE: ue.id,
|
||||||
|
valeur: 10.0,
|
||||||
|
}),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
// !0 === true → retourne 400 à cause du falsy check, pas d'une vraie validation
|
// !0 === true → retourne 400 à cause du falsy check, pas d'une vraie validation
|
||||||
@@ -353,9 +346,17 @@ Deno.test({
|
|||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await seedPromotions([{ id: "P1" }]);
|
await seedPromotions([{ id: "P1" }]);
|
||||||
const [s] = await seedStudents([{ nom: "Test", prenom: "User", idPromo: "P1" }]);
|
const [s] = await seedStudents([{
|
||||||
|
nom: "Test",
|
||||||
|
prenom: "User",
|
||||||
|
idPromo: "P1",
|
||||||
|
}]);
|
||||||
const res = await ajustementsHandler.POST!(
|
const res = await ajustementsHandler.POST!(
|
||||||
makeJsonRequest("/ajustements", "POST", { numEtud: s.numEtud, idUE: 0, valeur: 10.0 }),
|
makeJsonRequest("/ajustements", "POST", {
|
||||||
|
numEtud: s.numEtud,
|
||||||
|
idUE: 0,
|
||||||
|
valeur: 10.0,
|
||||||
|
}),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
assertEquals(res.status, 400); // !0 → 400, message trompeur
|
assertEquals(res.status, 400); // !0 → 400, message trompeur
|
||||||
@@ -369,14 +370,20 @@ Deno.test({
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness: POST /ue-modules coeff=0 → 201 (zéro est une valeur valide)",
|
name:
|
||||||
|
"robustness: POST /ue-modules coeff=0 → 201 (zéro est une valeur valide)",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await seedPromotions([{ id: "P1" }]);
|
await seedPromotions([{ id: "P1" }]);
|
||||||
await seedModules([{ id: "M1", nom: "Mod" }]);
|
await seedModules([{ id: "M1", nom: "Mod" }]);
|
||||||
const [ue] = await seedUes([{ nom: "UE Info" }]);
|
const [ue] = await seedUes([{ nom: "UE Info" }]);
|
||||||
const res = await ueModulesHandler.POST!(
|
const res = await ueModulesHandler.POST!(
|
||||||
makeJsonRequest("/ue-modules", "POST", { idModule: "M1", idUE: ue.id, idPromo: "P1", coeff: 0 }),
|
makeJsonRequest("/ue-modules", "POST", {
|
||||||
|
idModule: "M1",
|
||||||
|
idUE: ue.id,
|
||||||
|
idPromo: "P1",
|
||||||
|
coeff: 0,
|
||||||
|
}),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
// coeff === undefined → false pour 0 → passe ✓
|
// coeff === undefined → false pour 0 → passe ✓
|
||||||
@@ -392,7 +399,8 @@ Deno.test({
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness: GET /modules avec SQL injection dans id → 404 (Drizzle paramètre)",
|
name:
|
||||||
|
"robustness: GET /modules avec SQL injection dans id → 404 (Drizzle paramètre)",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
const injectionId = "'; DROP TABLE modules; --";
|
const injectionId = "'; DROP TABLE modules; --";
|
||||||
@@ -409,7 +417,8 @@ Deno.test({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness: POST /modules avec SQL injection dans id → s'insère littéralement (safe)",
|
name:
|
||||||
|
"robustness: POST /modules avec SQL injection dans id → s'insère littéralement (safe)",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
const injectionId = "'; DROP TABLE modules; --";
|
const injectionId = "'; DROP TABLE modules; --";
|
||||||
@@ -429,52 +438,72 @@ Deno.test({
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /notes note > 20 → devrait être 400, retourne 201",
|
name: "robustness: POST /notes note > 20 → 400",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await seedPromotions([{ id: "P1" }]);
|
await seedPromotions([{ id: "P1" }]);
|
||||||
const [s] = await seedStudents([{ nom: "Test", prenom: "User", idPromo: "P1" }]);
|
const [s] = await seedStudents([{
|
||||||
|
nom: "Test",
|
||||||
|
prenom: "User",
|
||||||
|
idPromo: "P1",
|
||||||
|
}]);
|
||||||
await seedModules([{ id: "M1", nom: "Mod" }]);
|
await seedModules([{ id: "M1", nom: "Mod" }]);
|
||||||
const res = await notesHandler.POST!(
|
const res = await notesHandler.POST!(
|
||||||
makeJsonRequest("/notes", "POST", { note: 999, numEtud: s.numEtud, idModule: "M1" }),
|
makeJsonRequest("/notes", "POST", {
|
||||||
|
note: 999,
|
||||||
|
numEtud: s.numEtud,
|
||||||
|
idModule: "M1",
|
||||||
|
}),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
// Aucune validation de borne → 999 s'insère → 201
|
assertEquals(res.status, 400);
|
||||||
assertEquals(res.status, 400); // ← va échouer, expose le manque de validation métier
|
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /notes note < 0 → devrait être 400, retourne 201",
|
name: "robustness: POST /notes note < 0 → 400",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await seedPromotions([{ id: "P1" }]);
|
await seedPromotions([{ id: "P1" }]);
|
||||||
const [s] = await seedStudents([{ nom: "Test", prenom: "User", idPromo: "P1" }]);
|
const [s] = await seedStudents([{
|
||||||
|
nom: "Test",
|
||||||
|
prenom: "User",
|
||||||
|
idPromo: "P1",
|
||||||
|
}]);
|
||||||
await seedModules([{ id: "M1", nom: "Mod" }]);
|
await seedModules([{ id: "M1", nom: "Mod" }]);
|
||||||
const res = await notesHandler.POST!(
|
const res = await notesHandler.POST!(
|
||||||
makeJsonRequest("/notes", "POST", { note: -5, numEtud: s.numEtud, idModule: "M1" }),
|
makeJsonRequest("/notes", "POST", {
|
||||||
|
note: -5,
|
||||||
|
numEtud: s.numEtud,
|
||||||
|
idModule: "M1",
|
||||||
|
}),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
assertEquals(res.status, 400); // ← va échouer
|
assertEquals(res.status, 400);
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness [BUG]: POST /ue-modules coeff négatif → devrait être 400, retourne 201",
|
name: "robustness: POST /ue-modules coeff négatif → 400",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await seedPromotions([{ id: "P1" }]);
|
await seedPromotions([{ id: "P1" }]);
|
||||||
await seedModules([{ id: "M1", nom: "Mod" }]);
|
await seedModules([{ id: "M1", nom: "Mod" }]);
|
||||||
const [ue] = await seedUes([{ nom: "UE Info" }]);
|
const [ue] = await seedUes([{ nom: "UE Info" }]);
|
||||||
const res = await ueModulesHandler.POST!(
|
const res = await ueModulesHandler.POST!(
|
||||||
makeJsonRequest("/ue-modules", "POST", { idModule: "M1", idUE: ue.id, idPromo: "P1", coeff: -3 }),
|
makeJsonRequest("/ue-modules", "POST", {
|
||||||
|
idModule: "M1",
|
||||||
|
idUE: ue.id,
|
||||||
|
idPromo: "P1",
|
||||||
|
coeff: -3,
|
||||||
|
}),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
assertEquals(res.status, 400); // ← va échouer
|
assertEquals(res.status, 400);
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
@@ -491,7 +520,10 @@ Deno.test({
|
|||||||
// Ce test crée un module
|
// Ce test crée un module
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
await modulesHandler.POST!(
|
await modulesHandler.POST!(
|
||||||
makeJsonRequest("/modules", "POST", { id: "ISOLATION-TEST", nom: "Test" }),
|
makeJsonRequest("/modules", "POST", {
|
||||||
|
id: "ISOLATION-TEST",
|
||||||
|
nom: "Test",
|
||||||
|
}),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -500,7 +532,8 @@ Deno.test({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "robustness: isolation — truncateAll efface bien les données du test précédent",
|
name:
|
||||||
|
"robustness: isolation — truncateAll efface bien les données du test précédent",
|
||||||
async fn() {
|
async fn() {
|
||||||
await truncateAll();
|
await truncateAll();
|
||||||
// Le module créé dans le test précédent ne doit plus exister
|
// Le module créé dans le test précédent ne doit plus exister
|
||||||
|
|||||||
Reference in New Issue
Block a user