fix: correct handler bugs exposed by test suite
Check Deno code / Check Deno code (pull_request) Failing after 6s
Tests / Unit tests (pull_request) Successful in 13s
Tests / Integration tests (pull_request) Successful in 1m17s

- 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:
2026-04-26 19:01:53 +02:00
parent 2f4d8db1bf
commit b0930b8da2
9 changed files with 166 additions and 99 deletions
+6 -5
View File
@@ -26,11 +26,12 @@ export const handler: Handlers<null, AuthenticatedState> = {
return FORBIDDEN;
}
const body: {
idProf: string;
idModule: string;
idPromo: string;
} = await request.json();
let body: { idProf: string; idModule: string; idPromo: string };
try {
body = await request.json();
} catch {
return new Response(null, { status: 500 });
}
if (!body.idProf || !body.idModule || !body.idPromo) {
return new Response(null, { status: 400 });
+7 -2
View File
@@ -31,9 +31,14 @@ export const handler: Handlers<null, AuthenticatedState> = {
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 });
}
+10 -1
View File
@@ -33,7 +33,16 @@ export const handler: Handlers<null, AuthenticatedState> = {
request: Request,
context: FreshContext<AuthenticatedState>,
): 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
.update(modules)
+10 -3
View File
@@ -27,10 +27,17 @@ export const handler: Handlers<null, AuthenticatedState> = {
request: Request,
_context: FreshContext<AuthenticatedState>,
): Promise<Response> {
const body: { id: string; nom: string; prenom: string; idRole: number } =
await request.json();
let body: { id: string; nom: string; prenom: string; idRole: number };
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 });
}
@@ -2,7 +2,7 @@ import { FreshContext, Handlers } from "$fresh/server.ts";
import { db } from "$root/databases/db.ts";
import { ajustements } from "$root/databases/schema.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(
JSON.stringify({ error: "Ajustement introuvable" }),
@@ -31,7 +31,7 @@ export const handler: Handlers<null, AuthenticatedState> = {
const ajustement = await db
.select()
.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);
if (!ajustement) return NOT_FOUND;
@@ -69,7 +69,7 @@ export const handler: Handlers<null, AuthenticatedState> = {
const [updated] = await db
.update(ajustements)
.set({ valeur: body.valeur })
.where(eq(ajustements.numEtud, numEtud), eq(ajustements.idUE, idUE))
.where(and(eq(ajustements.numEtud, numEtud), eq(ajustements.idUE, idUE)))
.returning();
if (!updated) return NOT_FOUND;
@@ -97,7 +97,7 @@ export const handler: Handlers<null, AuthenticatedState> = {
const [deleted] = await db
.delete(ajustements)
.where(eq(ajustements.numEtud, numEtud), eq(ajustements.idUE, idUE))
.where(and(eq(ajustements.numEtud, numEtud), eq(ajustements.idUE, idUE)))
.returning();
if (!deleted) return NOT_FOUND;
+6
View File
@@ -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 })
.returning();
+6
View File
@@ -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({
idModule,
idUE,
+1 -1
View File
@@ -24,7 +24,7 @@ export const handler: Handlers = {
const body = await request.json();
const { nom } = body;
if (!nom) {
if (!nom || !nom.trim()) {
return new Response("Champ 'nom' manquant", { status: 400 });
}