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,189 @@
|
||||
// Unit tests for /ajustements endpoints — fixtures, mock API, mock DB
|
||||
|
||||
import { assertEquals, assertExists } from "@std/assert";
|
||||
import { mockFetch, restoreFetch } from "../helpers/api_mock.ts";
|
||||
import { createMockDb } from "../helpers/db_mock.ts";
|
||||
import { type Ajustement, ajustements } from "../helpers/fixtures.ts";
|
||||
|
||||
// --- Fixtures ---
|
||||
|
||||
Deno.test("ajustements: fixtures have correct shape", () => {
|
||||
assertEquals(ajustements.length, 2);
|
||||
assertEquals(typeof ajustements[0].numEtud, "number");
|
||||
assertEquals(typeof ajustements[0].idUE, "number");
|
||||
assertEquals(typeof ajustements[0].valeur, "number");
|
||||
});
|
||||
|
||||
// --- Mock API ---
|
||||
|
||||
Deno.test("mock API: GET /ajustements returns list", async () => {
|
||||
mockFetch({ "/ajustements": ajustements });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements");
|
||||
assertEquals(res.status, 200);
|
||||
const data: Ajustement[] = await res.json();
|
||||
assertEquals(data.length, 2);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ajustements?numEtud filters by student", async () => {
|
||||
const filtered = ajustements.filter((a) => a.numEtud === 21212006);
|
||||
mockFetch({ "/ajustements": filtered });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements?numEtud=21212006");
|
||||
const data: Ajustement[] = await res.json();
|
||||
assertEquals(data.length, 1);
|
||||
assertEquals(data[0].numEtud, 21212006);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ajustements?numEtud=NaN returns 400", async () => {
|
||||
mockFetch({ "/ajustements": { status: 400 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements?numEtud=abc");
|
||||
assertEquals(res.status, 400);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /ajustements creates ajustement (201) as employee", async () => {
|
||||
const newAjust: Ajustement = { numEtud: 21212007, idUE: 2, valeur: 14.0 };
|
||||
mockFetch({ "/ajustements": { method: "POST", status: 201, body: newAjust } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(newAjust),
|
||||
});
|
||||
assertEquals(res.status, 201);
|
||||
const data: Ajustement = await res.json();
|
||||
assertEquals(data.numEtud, 21212007);
|
||||
assertEquals(data.valeur, 14.0);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /ajustements 403 for non-employee", async () => {
|
||||
mockFetch({ "/ajustements": { method: "POST", status: 403 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements", { method: "POST" });
|
||||
assertEquals(res.status, 403);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /ajustements 400 on missing fields", async () => {
|
||||
mockFetch({ "/ajustements": { method: "POST", status: 400 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ numEtud: 21212006 }),
|
||||
});
|
||||
assertEquals(res.status, 400);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ajustements/:numEtud/:idUE returns ajustement (employee)", async () => {
|
||||
mockFetch({ "/ajustements/21212006/1": ajustements[0] });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements/21212006/1");
|
||||
assertEquals(res.status, 200);
|
||||
const data: Ajustement = await res.json();
|
||||
assertEquals(data.valeur, 13.25);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ajustements/:numEtud/:idUE 403 for non-employee", async () => {
|
||||
mockFetch({ "/ajustements/21212006/1": { status: 403 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements/21212006/1");
|
||||
assertEquals(res.status, 403);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ajustements/:numEtud/:idUE 404 when not found", async () => {
|
||||
mockFetch({ "/ajustements/99999/9": { status: 404, body: { error: "Ajustement introuvable" } } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements/99999/9");
|
||||
assertEquals(res.status, 404);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: PUT /ajustements/:numEtud/:idUE updates valeur", async () => {
|
||||
const updated: Ajustement = { ...ajustements[0], valeur: 18.0 };
|
||||
mockFetch({ "/ajustements/21212006/1": { method: "PUT", status: 200, body: updated } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements/21212006/1", {
|
||||
method: "PUT",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ valeur: 18.0 }),
|
||||
});
|
||||
assertEquals(res.status, 200);
|
||||
const data: Ajustement = await res.json();
|
||||
assertEquals(data.valeur, 18.0);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: DELETE /ajustements/:numEtud/:idUE returns 204", async () => {
|
||||
mockFetch({ "/ajustements/21212006/1": { method: "DELETE", status: 204 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ajustements/21212006/1", { method: "DELETE" });
|
||||
assertEquals(res.status, 204);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
// --- Mock DB ---
|
||||
|
||||
Deno.test("mock DB: find ajustement by composite key", () => {
|
||||
const db = createMockDb({ tables: { ajustements: [...ajustements] } });
|
||||
const a = db.findOne<Ajustement>("ajustements", (a) => a.numEtud === 21212006 && a.idUE === 1);
|
||||
assertExists(a);
|
||||
assertEquals(a.valeur, 13.25);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: filter ajustements by numEtud", () => {
|
||||
const db = createMockDb({ tables: { ajustements: [...ajustements] } });
|
||||
const rows = db.findMany<Ajustement>("ajustements", (a) => a.numEtud === 21212006);
|
||||
assertEquals(rows.length, 1);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: insert ajustement", () => {
|
||||
const db = createMockDb({ tables: { ajustements: [...ajustements] } });
|
||||
db.insert<Ajustement>("ajustements", { numEtud: 21212007, idUE: 2, valeur: 14.0 });
|
||||
assertEquals(db.getTable("ajustements").length, 3);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: update ajustement valeur", () => {
|
||||
const db = createMockDb({ tables: { ajustements: [...ajustements] } });
|
||||
db.updateWhere<Ajustement>("ajustements", (a) => a.numEtud === 21212006 && a.idUE === 1, { valeur: 20.0 });
|
||||
assertEquals(
|
||||
db.findOne<Ajustement>("ajustements", (a) => a.numEtud === 21212006 && a.idUE === 1)?.valeur,
|
||||
20.0,
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: delete ajustement", () => {
|
||||
const db = createMockDb({ tables: { ajustements: [...ajustements] } });
|
||||
db.deleteWhere<Ajustement>("ajustements", (a) => a.numEtud === 21212006 && a.idUE === 1);
|
||||
assertEquals(db.getTable("ajustements").length, 1);
|
||||
});
|
||||
@@ -0,0 +1,179 @@
|
||||
// Unit tests for /enseignements endpoints — fixtures, mock API, mock DB
|
||||
|
||||
import { assertEquals, assertExists } from "@std/assert";
|
||||
import { mockFetch, restoreFetch } from "../helpers/api_mock.ts";
|
||||
import { createMockDb } from "../helpers/db_mock.ts";
|
||||
import { enseignements } from "../helpers/fixtures.ts";
|
||||
|
||||
interface Enseignement {
|
||||
idProf: string;
|
||||
idModule: string;
|
||||
idPromo: string;
|
||||
}
|
||||
|
||||
// --- Fixtures ---
|
||||
|
||||
Deno.test("enseignements: fixtures have correct shape", () => {
|
||||
assertEquals(enseignements.length, 3);
|
||||
assertEquals(typeof enseignements[0].idModule, "string");
|
||||
assertEquals(typeof enseignements[0].idPromo, "string");
|
||||
});
|
||||
|
||||
// --- Mock API ---
|
||||
|
||||
Deno.test("mock API: POST /enseignements creates enseignement (201) as employee", async () => {
|
||||
const newEns: Enseignement = { idProf: "prof.dupont", idModule: "JIN702C", idPromo: "4AFISE25/26" };
|
||||
mockFetch({ "/enseignements": { method: "POST", status: 201, body: newEns } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/enseignements", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(newEns),
|
||||
});
|
||||
assertEquals(res.status, 201);
|
||||
const data: Enseignement = await res.json();
|
||||
assertEquals(data.idModule, "JIN702C");
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /enseignements 403 for non-employee", async () => {
|
||||
mockFetch({ "/enseignements": { method: "POST", status: 403 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/enseignements", { method: "POST" });
|
||||
assertEquals(res.status, 403);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /enseignements 400 on missing fields", async () => {
|
||||
mockFetch({ "/enseignements": { method: "POST", status: 400 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/enseignements", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ idProf: "prof.dupont" }),
|
||||
});
|
||||
assertEquals(res.status, 400);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /enseignements 409 on duplicate", async () => {
|
||||
mockFetch({
|
||||
"/enseignements": { method: "POST", status: 409, body: { error: "Cet enseignement existe déjà." } },
|
||||
});
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/enseignements", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ idProf: "prof.dupont", idModule: "JIN702C", idPromo: "4AFISE25/26" }),
|
||||
});
|
||||
assertEquals(res.status, 409);
|
||||
const data = await res.json();
|
||||
assertExists(data.error);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /enseignements/:idProf/:idModule/:idPromo returns enseignement (employee)", async () => {
|
||||
const ens: Enseignement = { idProf: "prof.dupont", idModule: "JIN702C", idPromo: "4AFISE25/26" };
|
||||
mockFetch({ "/enseignements/prof.dupont/JIN702C/4AFISE25": ens });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/enseignements/prof.dupont/JIN702C/4AFISE25%2F26");
|
||||
assertEquals(res.status, 200);
|
||||
const data: Enseignement = await res.json();
|
||||
assertEquals(data.idProf, "prof.dupont");
|
||||
assertEquals(data.idModule, "JIN702C");
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /enseignements/:idProf/:idModule/:idPromo 403 for non-employee", async () => {
|
||||
mockFetch({ "/enseignements/prof.dupont/JIN702C/4AFISE25": { status: 403 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/enseignements/prof.dupont/JIN702C/4AFISE25%2F26");
|
||||
assertEquals(res.status, 403);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /enseignements/:idProf/:idModule/:idPromo 404 when not found", async () => {
|
||||
mockFetch({ "/enseignements/ghost/GHOST/GHOST": { status: 404, body: { error: "Ressource introuvable" } } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/enseignements/ghost/GHOST/GHOST");
|
||||
assertEquals(res.status, 404);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: DELETE /enseignements/:idProf/:idModule/:idPromo returns 204 (employee)", async () => {
|
||||
mockFetch({ "/enseignements/prof.dupont/JIN702C/4AFISE25": { method: "DELETE", status: 204 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/enseignements/prof.dupont/JIN702C/4AFISE25%2F26", {
|
||||
method: "DELETE",
|
||||
});
|
||||
assertEquals(res.status, 204);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: DELETE /enseignements/:idProf/:idModule/:idPromo 403 for non-employee", async () => {
|
||||
mockFetch({ "/enseignements/prof.dupont/JIN702C/4AFISE25": { method: "DELETE", status: 403 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/enseignements/prof.dupont/JIN702C/4AFISE25%2F26", {
|
||||
method: "DELETE",
|
||||
});
|
||||
assertEquals(res.status, 403);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
// --- Mock DB ---
|
||||
|
||||
Deno.test("mock DB: find enseignement by composite key", () => {
|
||||
const data = [
|
||||
{ idProf: "prof.dupont", idModule: "JIN702C", idPromo: "4AFISE25/26" },
|
||||
{ idProf: "prof.moreau", idModule: "JIN703C", idPromo: "4AFISE25/26" },
|
||||
];
|
||||
const db = createMockDb({ tables: { enseignements: data } });
|
||||
const e = db.findOne<Enseignement>("enseignements", (e) => e.idProf === "prof.dupont" && e.idModule === "JIN702C");
|
||||
assertExists(e);
|
||||
assertEquals(e.idPromo, "4AFISE25/26");
|
||||
});
|
||||
|
||||
Deno.test("mock DB: insert enseignement", () => {
|
||||
const db = createMockDb({ tables: { enseignements: [] } });
|
||||
db.insert<Enseignement>("enseignements", { idProf: "prof.dupont", idModule: "JIN702C", idPromo: "4AFISE25/26" });
|
||||
assertEquals(db.getTable("enseignements").length, 1);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: delete enseignement", () => {
|
||||
const data = [
|
||||
{ idProf: "prof.dupont", idModule: "JIN702C", idPromo: "4AFISE25/26" },
|
||||
{ idProf: "prof.moreau", idModule: "JIN703C", idPromo: "4AFISE25/26" },
|
||||
];
|
||||
const db = createMockDb({ tables: { enseignements: data } });
|
||||
db.deleteWhere<Enseignement>("enseignements", (e) => e.idProf === "prof.dupont");
|
||||
assertEquals(db.getTable("enseignements").length, 1);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: filter enseignements by idModule", () => {
|
||||
const data = [
|
||||
{ idProf: "prof.dupont", idModule: "JIN702C", idPromo: "4AFISE25/26" },
|
||||
{ idProf: "prof.dupont", idModule: "JIN702C", idPromo: "3AFISE25/26" },
|
||||
{ idProf: "prof.moreau", idModule: "JIN703C", idPromo: "4AFISE25/26" },
|
||||
];
|
||||
const db = createMockDb({ tables: { enseignements: data } });
|
||||
const rows = db.findMany<Enseignement>("enseignements", (e) => e.idModule === "JIN702C");
|
||||
assertEquals(rows.length, 2);
|
||||
});
|
||||
@@ -0,0 +1,196 @@
|
||||
// Unit tests for /notes endpoints — fixtures, mock API, mock DB
|
||||
|
||||
import { assertEquals, assertExists } from "@std/assert";
|
||||
import { mockFetch, restoreFetch } from "../helpers/api_mock.ts";
|
||||
import { createMockDb } from "../helpers/db_mock.ts";
|
||||
import { type Note, notes } from "../helpers/fixtures.ts";
|
||||
|
||||
// --- Fixtures ---
|
||||
|
||||
Deno.test("notes: fixtures have correct shape", () => {
|
||||
assertEquals(notes.length, 4);
|
||||
assertEquals(typeof notes[0].note, "number");
|
||||
assertEquals(typeof notes[0].numEtud, "number");
|
||||
assertEquals(typeof notes[0].idModule, "string");
|
||||
});
|
||||
|
||||
Deno.test("notes: fixtures use decimal values", () => {
|
||||
assertEquals(notes[0].note, 15.5);
|
||||
});
|
||||
|
||||
// --- Mock API ---
|
||||
|
||||
Deno.test("mock API: GET /notes returns list", async () => {
|
||||
mockFetch({ "/notes": notes });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes");
|
||||
assertEquals(res.status, 200);
|
||||
const data: Note[] = await res.json();
|
||||
assertEquals(data.length, 4);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /notes?numEtud filters by student", async () => {
|
||||
const filtered = notes.filter((n) => n.numEtud === 21212006);
|
||||
mockFetch({ "/notes": filtered });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes?numEtud=21212006");
|
||||
const data: Note[] = await res.json();
|
||||
assertEquals(data.length, 2);
|
||||
assertEquals(data.every((n) => n.numEtud === 21212006), true);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /notes?idModule filters by module", async () => {
|
||||
const filtered = notes.filter((n) => n.idModule === "JIN702C");
|
||||
mockFetch({ "/notes": filtered });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes?idModule=JIN702C");
|
||||
const data: Note[] = await res.json();
|
||||
assertEquals(data.length, 2);
|
||||
assertEquals(data.every((n) => n.idModule === "JIN702C"), true);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /notes?numEtud=NaN returns 400", async () => {
|
||||
mockFetch({ "/notes": { status: 400 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes?numEtud=abc");
|
||||
assertEquals(res.status, 400);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /notes creates note (201)", async () => {
|
||||
const newNote: Note = { note: 14.0, numEtud: 21212006, idModule: "JIN704C" };
|
||||
mockFetch({ "/notes": { method: "POST", status: 201, body: newNote } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(newNote),
|
||||
});
|
||||
assertEquals(res.status, 201);
|
||||
const data: Note = await res.json();
|
||||
assertEquals(data.note, 14.0);
|
||||
assertEquals(data.numEtud, 21212006);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /notes 400 on missing fields", async () => {
|
||||
mockFetch({ "/notes": { method: "POST", status: 400 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ numEtud: 21212006 }),
|
||||
});
|
||||
assertEquals(res.status, 400);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /notes/:numEtud/:idModule returns note", async () => {
|
||||
mockFetch({ "/notes/21212006/JIN702C": notes[0] });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes/21212006/JIN702C");
|
||||
assertEquals(res.status, 200);
|
||||
const data: Note = await res.json();
|
||||
assertEquals(data.note, 15.5);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /notes/:numEtud/:idModule 404 when not found", async () => {
|
||||
mockFetch({ "/notes/99999/GHOST": { status: 404, body: { error: "Ressource introuvable" } } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes/99999/GHOST");
|
||||
assertEquals(res.status, 404);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: PUT /notes/:numEtud/:idModule updates note", async () => {
|
||||
const updated: Note = { ...notes[0], note: 17.0 };
|
||||
mockFetch({ "/notes/21212006/JIN702C": { method: "PUT", status: 200, body: updated } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes/21212006/JIN702C", {
|
||||
method: "PUT",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ note: 17.0 }),
|
||||
});
|
||||
assertEquals(res.status, 200);
|
||||
const data: Note = await res.json();
|
||||
assertEquals(data.note, 17.0);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: DELETE /notes/:numEtud/:idModule returns 204", async () => {
|
||||
mockFetch({ "/notes/21212006/JIN702C": { method: "DELETE", status: 204 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes/21212006/JIN702C", { method: "DELETE" });
|
||||
assertEquals(res.status, 204);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: DELETE /notes/:numEtud/:idModule 404 when not found", async () => {
|
||||
mockFetch({ "/notes/99999/GHOST": { method: "DELETE", status: 404 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/notes/99999/GHOST", { method: "DELETE" });
|
||||
assertEquals(res.status, 404);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
// --- Mock DB ---
|
||||
|
||||
Deno.test("mock DB: find note by composite key", () => {
|
||||
const db = createMockDb({ tables: { notes: [...notes] } });
|
||||
const n = db.findOne<Note>("notes", (n) => n.numEtud === 21212006 && n.idModule === "JIN702C");
|
||||
assertExists(n);
|
||||
assertEquals(n.note, 15.5);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: filter notes by numEtud", () => {
|
||||
const db = createMockDb({ tables: { notes: [...notes] } });
|
||||
const rows = db.findMany<Note>("notes", (n) => n.numEtud === 21212006);
|
||||
assertEquals(rows.length, 2);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: insert note", () => {
|
||||
const db = createMockDb({ tables: { notes: [...notes] } });
|
||||
db.insert<Note>("notes", { note: 10.0, numEtud: 21212006, idModule: "JIN704C" });
|
||||
assertEquals(db.getTable("notes").length, 5);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: update note value", () => {
|
||||
const db = createMockDb({ tables: { notes: [...notes] } });
|
||||
db.updateWhere<Note>("notes", (n) => n.numEtud === 21212006 && n.idModule === "JIN702C", { note: 20.0 });
|
||||
assertEquals(
|
||||
db.findOne<Note>("notes", (n) => n.numEtud === 21212006 && n.idModule === "JIN702C")?.note,
|
||||
20.0,
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: delete note", () => {
|
||||
const db = createMockDb({ tables: { notes: [...notes] } });
|
||||
db.deleteWhere<Note>("notes", (n) => n.numEtud === 21212006 && n.idModule === "JIN702C");
|
||||
assertEquals(db.getTable("notes").length, 3);
|
||||
});
|
||||
@@ -0,0 +1,173 @@
|
||||
// Unit tests for /ue-modules endpoints — fixtures, mock API, mock DB
|
||||
|
||||
import { assertEquals, assertExists } from "@std/assert";
|
||||
import { mockFetch, restoreFetch } from "../helpers/api_mock.ts";
|
||||
import { createMockDb } from "../helpers/db_mock.ts";
|
||||
import { type UeModule, ueModules } from "../helpers/fixtures.ts";
|
||||
|
||||
// --- Fixtures ---
|
||||
|
||||
Deno.test("ue_modules: fixtures have correct shape", () => {
|
||||
assertEquals(ueModules.length, 3);
|
||||
assertEquals(typeof ueModules[0].idModule, "string");
|
||||
assertEquals(typeof ueModules[0].idUE, "number");
|
||||
assertEquals(typeof ueModules[0].idPromo, "string");
|
||||
assertEquals(typeof ueModules[0].coeff, "number");
|
||||
});
|
||||
|
||||
// --- Mock API ---
|
||||
|
||||
Deno.test("mock API: GET /ue-modules returns list", async () => {
|
||||
mockFetch({ "/ue-modules": ueModules });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ue-modules");
|
||||
assertEquals(res.status, 200);
|
||||
const data: UeModule[] = await res.json();
|
||||
assertEquals(data.length, 3);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ue-modules?idPromo filters by promo", async () => {
|
||||
const filtered = ueModules.filter((u) => u.idPromo === "4AFISE25/26");
|
||||
mockFetch({ "/ue-modules": filtered });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ue-modules?idPromo=4AFISE25%2F26");
|
||||
const data: UeModule[] = await res.json();
|
||||
assertEquals(data.length, 2);
|
||||
assertEquals(data.every((u) => u.idPromo === "4AFISE25/26"), true);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ue-modules?idUE filters by UE", async () => {
|
||||
const filtered = ueModules.filter((u) => u.idUE === 1);
|
||||
mockFetch({ "/ue-modules": filtered });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ue-modules?idUE=1");
|
||||
const data: UeModule[] = await res.json();
|
||||
assertEquals(data.length, 2);
|
||||
assertEquals(data.every((u) => u.idUE === 1), true);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /ue-modules creates association (201)", async () => {
|
||||
const newUeModule: UeModule = { idModule: "JIN705C", idUE: 2, idPromo: "3AFISE25/26", coeff: 3.0 };
|
||||
mockFetch({ "/ue-modules": { method: "POST", status: 201, body: newUeModule } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ue-modules", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(newUeModule),
|
||||
});
|
||||
assertEquals(res.status, 201);
|
||||
const data: UeModule = await res.json();
|
||||
assertEquals(data.idModule, "JIN705C");
|
||||
assertEquals(data.coeff, 3.0);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /ue-modules 400 on missing fields", async () => {
|
||||
mockFetch({ "/ue-modules": { method: "POST", status: 400 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ue-modules", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ idModule: "X" }),
|
||||
});
|
||||
assertEquals(res.status, 400);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ue-modules/:idModule/:idUE/:idPromo returns association (employee)", async () => {
|
||||
mockFetch({ "/ue-modules/JIN702C/1/4AFISE25": ueModules[0] });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ue-modules/JIN702C/1/4AFISE25%2F26");
|
||||
assertEquals(res.status, 200);
|
||||
const data: UeModule = await res.json();
|
||||
assertEquals(data.coeff, 3.0);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ue-modules/:idModule/:idUE/:idPromo 403 for non-employee", async () => {
|
||||
mockFetch({ "/ue-modules/JIN702C/1/4AFISE25": { status: 403 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ue-modules/JIN702C/1/4AFISE25%2F26");
|
||||
assertEquals(res.status, 403);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: PUT /ue-modules/:idModule/:idUE/:idPromo updates coeff", async () => {
|
||||
const updated: UeModule = { ...ueModules[0], coeff: 5.0 };
|
||||
mockFetch({ "/ue-modules/JIN702C/1/4AFISE25": { method: "PUT", status: 200, body: updated } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ue-modules/JIN702C/1/4AFISE25%2F26", {
|
||||
method: "PUT",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ coeff: 5.0 }),
|
||||
});
|
||||
assertEquals(res.status, 200);
|
||||
const data: UeModule = await res.json();
|
||||
assertEquals(data.coeff, 5.0);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: DELETE /ue-modules/:idModule/:idUE/:idPromo returns 204", async () => {
|
||||
mockFetch({ "/ue-modules/JIN702C/1/4AFISE25": { method: "DELETE", status: 204 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ue-modules/JIN702C/1/4AFISE25%2F26", { method: "DELETE" });
|
||||
assertEquals(res.status, 204);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
// --- Mock DB ---
|
||||
|
||||
Deno.test("mock DB: find ue-module by composite key", () => {
|
||||
const db = createMockDb({ tables: { ueModules: [...ueModules] } });
|
||||
const u = db.findOne<UeModule>("ueModules", (u) => u.idModule === "JIN702C" && u.idUE === 1 && u.idPromo === "4AFISE25/26");
|
||||
assertExists(u);
|
||||
assertEquals(u.coeff, 3.0);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: filter ue-modules by promo", () => {
|
||||
const db = createMockDb({ tables: { ueModules: [...ueModules] } });
|
||||
const rows = db.findMany<UeModule>("ueModules", (u) => u.idPromo === "4AFISE25/26");
|
||||
assertEquals(rows.length, 2);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: insert ue-module", () => {
|
||||
const db = createMockDb({ tables: { ueModules: [...ueModules] } });
|
||||
db.insert<UeModule>("ueModules", { idModule: "JIN705C", idUE: 2, idPromo: "3AFISE25/26", coeff: 1.5 });
|
||||
assertEquals(db.getTable("ueModules").length, 4);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: update ue-module coeff", () => {
|
||||
const db = createMockDb({ tables: { ueModules: [...ueModules] } });
|
||||
db.updateWhere<UeModule>("ueModules", (u) => u.idModule === "JIN702C" && u.idUE === 1, { coeff: 6.0 });
|
||||
assertEquals(
|
||||
db.findOne<UeModule>("ueModules", (u) => u.idModule === "JIN702C" && u.idUE === 1)?.coeff,
|
||||
6.0,
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: delete ue-module", () => {
|
||||
const db = createMockDb({ tables: { ueModules: [...ueModules] } });
|
||||
db.deleteWhere<UeModule>("ueModules", (u) => u.idModule === "JIN702C" && u.idUE === 1);
|
||||
assertEquals(db.getTable("ueModules").length, 2);
|
||||
});
|
||||
@@ -0,0 +1,160 @@
|
||||
// Unit tests for /ues endpoints — fixtures, mock API, mock DB
|
||||
|
||||
import { assertEquals, assertExists } from "@std/assert";
|
||||
import { mockFetch, restoreFetch } from "../helpers/api_mock.ts";
|
||||
import { createMockDb } from "../helpers/db_mock.ts";
|
||||
import { type UE, ues } from "../helpers/fixtures.ts";
|
||||
|
||||
// --- Fixtures ---
|
||||
|
||||
Deno.test("ues: fixtures have correct shape", () => {
|
||||
assertEquals(ues.length, 2);
|
||||
assertEquals(typeof ues[0].id, "number");
|
||||
assertEquals(typeof ues[0].nom, "string");
|
||||
});
|
||||
|
||||
// --- Mock API ---
|
||||
|
||||
Deno.test("mock API: GET /ues returns list", async () => {
|
||||
mockFetch({ "/ues": ues });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ues");
|
||||
assertEquals(res.status, 200);
|
||||
const data: UE[] = await res.json();
|
||||
assertEquals(data.length, 2);
|
||||
assertExists(data.find((u) => u.nom === "UE Informatique"));
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ues/:id returns one UE", async () => {
|
||||
mockFetch({ "/ues/1": ues[0] });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ues/1");
|
||||
assertEquals(res.status, 200);
|
||||
const data: UE = await res.json();
|
||||
assertEquals(data.id, 1);
|
||||
assertEquals(data.nom, "UE Informatique");
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: GET /ues/:id 404 when not found", async () => {
|
||||
mockFetch({ "/ues/99": { status: 404, body: { error: "Ressource introuvable" } } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ues/99");
|
||||
assertEquals(res.status, 404);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /ues creates UE (201)", async () => {
|
||||
const newUE: UE = { id: 3, nom: "UE Physique" };
|
||||
mockFetch({ "/ues": { method: "POST", status: 201, body: newUE } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ues", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ nom: "UE Physique" }),
|
||||
});
|
||||
assertEquals(res.status, 201);
|
||||
const data: UE = await res.json();
|
||||
assertEquals(data.nom, "UE Physique");
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: POST /ues 400 on missing nom", async () => {
|
||||
mockFetch({ "/ues": { method: "POST", status: 400 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ues", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
assertEquals(res.status, 400);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: PUT /ues/:id updates nom", async () => {
|
||||
const updated: UE = { id: 1, nom: "UE Informatique avancée" };
|
||||
mockFetch({ "/ues/1": { method: "PUT", status: 200, body: updated } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ues/1", {
|
||||
method: "PUT",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ nom: "UE Informatique avancée" }),
|
||||
});
|
||||
assertEquals(res.status, 200);
|
||||
const data: UE = await res.json();
|
||||
assertEquals(data.nom, "UE Informatique avancée");
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: PUT /ues/:id 404 when not found", async () => {
|
||||
mockFetch({ "/ues/99": { method: "PUT", status: 404 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ues/99", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ nom: "X" }),
|
||||
});
|
||||
assertEquals(res.status, 404);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: DELETE /ues/:id returns 204", async () => {
|
||||
mockFetch({ "/ues/1": { method: "DELETE", status: 204 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ues/1", { method: "DELETE" });
|
||||
assertEquals(res.status, 204);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("mock API: DELETE /ues/:id 404 when not found", async () => {
|
||||
mockFetch({ "/ues/99": { method: "DELETE", status: 404 } });
|
||||
try {
|
||||
const res = await fetch("http://localhost/api/ues/99", { method: "DELETE" });
|
||||
assertEquals(res.status, 404);
|
||||
} finally {
|
||||
restoreFetch();
|
||||
}
|
||||
});
|
||||
|
||||
// --- Mock DB ---
|
||||
|
||||
Deno.test("mock DB: find UE by id", () => {
|
||||
const db = createMockDb({ tables: { ues: [...ues] } });
|
||||
const u = db.findOne<UE>("ues", (u) => u.id === 1);
|
||||
assertExists(u);
|
||||
assertEquals(u.nom, "UE Informatique");
|
||||
});
|
||||
|
||||
Deno.test("mock DB: insert UE", () => {
|
||||
const db = createMockDb({ tables: { ues: [...ues] } });
|
||||
db.insert<UE>("ues", { id: 3, nom: "UE Physique" });
|
||||
assertEquals(db.getTable("ues").length, 3);
|
||||
});
|
||||
|
||||
Deno.test("mock DB: update UE nom", () => {
|
||||
const db = createMockDb({ tables: { ues: [...ues] } });
|
||||
db.updateWhere<UE>("ues", (u) => u.id === 1, { nom: "Updated" });
|
||||
assertEquals(db.findOne<UE>("ues", (u) => u.id === 1)?.nom, "Updated");
|
||||
});
|
||||
|
||||
Deno.test("mock DB: delete UE", () => {
|
||||
const db = createMockDb({ tables: { ues: [...ues] } });
|
||||
db.deleteWhere<UE>("ues", (u) => u.id === 1);
|
||||
assertEquals(db.getTable("ues").length, 1);
|
||||
});
|
||||
Reference in New Issue
Block a user