// #109 - Unit tests for /students endpoints // Tests purs : fixtures, mock API, mock DB — aucun appel réseau réel import { assertEquals, assertExists } from "@std/assert"; import { getFetchCalls, mockFetch, restoreFetch } from "../helpers/api_mock.ts"; import { createMockDb } from "../helpers/db_mock.ts"; import { type Student, students } from "../helpers/fixtures.ts"; // --- Fixtures --- Deno.test("students: fixtures have correct shape", () => { assertEquals(students.length, 3); assertEquals(typeof students[0].numEtud, "number"); assertEquals(typeof students[0].nom, "string"); assertEquals(typeof students[0].prenom, "string"); assertEquals(typeof students[0].idPromo, "string"); }); Deno.test("students: two students belong to the same promo", () => { const promo4 = students.filter((s) => s.idPromo === "4AFISE25/26"); assertEquals(promo4.length, 2); }); // --- Mock API - GET /students --- Deno.test("mock API: GET /students returns list", async () => { mockFetch({ "/students": students }); try { const res = await fetch("http://localhost/api/students"); assertEquals(res.status, 200); const data: Student[] = await res.json(); assertEquals(data.length, 3); assertExists(data.find((s) => s.nom === "Dupont")); } finally { restoreFetch(); } }); Deno.test("mock API: GET /students?idPromo filters by promo", async () => { const filtered = students.filter((s) => s.idPromo === "4AFISE25/26"); mockFetch({ "/students": filtered }); try { const res = await fetch( "http://localhost/api/students?idPromo=4AFISE25/26", ); const data: Student[] = await res.json(); assertEquals(data.length, 2); assertEquals(data.every((s) => s.idPromo === "4AFISE25/26"), true); } finally { restoreFetch(); } }); Deno.test("mock API: GET /students/:numEtud returns one student", async () => { mockFetch({ "/students/21212006": students[0] }); try { const res = await fetch("http://localhost/api/students/21212006"); assertEquals(res.status, 200); const data: Student = await res.json(); assertEquals(data.numEtud, 21212006); assertEquals(data.nom, "Dupont"); } finally { restoreFetch(); } }); Deno.test("mock API: GET /students/:numEtud 404 when not found", async () => { mockFetch({ "/students/99999": { status: 404, body: { error: "Ressource introuvable" }, }, }); try { const res = await fetch("http://localhost/api/students/99999"); assertEquals(res.status, 404); } finally { restoreFetch(); } }); Deno.test("mock API: POST /students creates student", async () => { const newStudent = students[0]; mockFetch({ "/students": { method: "POST", status: 201, body: newStudent } }); try { const res = await fetch("http://localhost/api/students", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ nom: "Dupont", prenom: "Jean", idPromo: "4AFISE25/26", }), }); assertEquals(res.status, 201); const data: Student = await res.json(); assertEquals(data.nom, "Dupont"); } finally { restoreFetch(); } }); Deno.test("mock API: PUT /students/:numEtud updates student", async () => { const updated = { ...students[0], nom: "Dupont-Modifié" }; mockFetch({ "/students/21212006": { method: "PUT", status: 200, body: updated }, }); try { const res = await fetch("http://localhost/api/students/21212006", { method: "PUT", headers: { "content-type": "application/json" }, body: JSON.stringify({ nom: "Dupont-Modifié", prenom: "Jean", idPromo: "4AFISE25/26", }), }); assertEquals(res.status, 200); const data: Student = await res.json(); assertEquals(data.nom, "Dupont-Modifié"); } finally { restoreFetch(); } }); Deno.test("mock API: DELETE /students/:numEtud returns 204", async () => { mockFetch({ "/students/21212006": { method: "DELETE", status: 204 } }); try { const res = await fetch("http://localhost/api/students/21212006", { method: "DELETE", }); assertEquals(res.status, 204); } finally { restoreFetch(); } }); Deno.test("mock API: POST /students 400 on missing fields", async () => { mockFetch({ "/students": { method: "POST", status: 400 } }); try { const res = await fetch("http://localhost/api/students", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ nom: "Test" }), }); assertEquals(res.status, 400); } finally { restoreFetch(); } }); // --- Mock DB --- Deno.test("mock DB: find student by numEtud", () => { const db = createMockDb({ tables: { students: [...students] } }); const s = db.findOne("students", (r) => r.numEtud === 21212006); assertExists(s); assertEquals(s.nom, "Dupont"); }); Deno.test("mock DB: filter students by idPromo", () => { const db = createMockDb({ tables: { students: [...students] } }); const rows = db.findMany( "students", (s) => s.idPromo === "4AFISE25/26", ); assertEquals(rows.length, 2); }); Deno.test("mock DB: insert student increments count", () => { const db = createMockDb({ tables: { students: [...students] } }); db.insert("students", { numEtud: 21212099, nom: "Test", prenom: "Ing", idPromo: "4AFISE25/26", }); assertEquals(db.getTable("students").length, 4); }); Deno.test("mock DB: update student nom", () => { const db = createMockDb({ tables: { students: [...students] } }); const count = db.updateWhere( "students", (s) => s.numEtud === 21212006, { nom: "Nouveau" }, ); assertEquals(count, 1); assertEquals( db.findOne("students", (s) => s.numEtud === 21212006)?.nom, "Nouveau", ); }); Deno.test("mock DB: delete student removes exactly one", () => { const db = createMockDb({ tables: { students: [...students] } }); const count = db.deleteWhere( "students", (s) => s.numEtud === 21212006, ); assertEquals(count, 1); assertEquals(db.getTable("students").length, 2); }); Deno.test("mock API: getFetchCalls tracks student requests", async () => { mockFetch({ "/students": students }); try { await fetch("http://localhost/api/students"); await fetch("http://localhost/api/students?idPromo=4AFISE25/26"); const calls = getFetchCalls(); assertEquals(calls.length, 2); assertEquals(calls[0].method, "GET"); } finally { restoreFetch(); } });