import { describe, it, expect, vi, beforeEach } from "vitest"; // Validation pure (pas de fetch). describe("presentationController.validateReponses", () => { beforeEach(() => { vi.resetModules(); }); it("accepte les 5 champs requis non vides", async () => { const { validateReponses } = await import("../presentationController"); const result = validateReponses({ prenom_age_ville: "Pierre, 30 ans, Alger", formation_metier: "Ingénieur", situation_familiale: "Marié, deux enfants", loisirs: "Lecture, randonnée", motivation_canada: "Opportunités professionnelles", }); expect("ok" in result && result.ok).toBe(true); }); it("rejette si reponses non objet", async () => { const { validateReponses } = await import("../presentationController"); const result = validateReponses("string"); expect("error" in result).toBe(true); if ("error" in result) expect(result.code).toBe("VALIDATION_ERROR"); }); it.each([ "prenom_age_ville", "formation_metier", "situation_familiale", "loisirs", "motivation_canada", ])("rejette si %s manquant", async (field) => { const { validateReponses } = await import("../presentationController"); const all: Record = { prenom_age_ville: "a", formation_metier: "b", situation_familiale: "c", loisirs: "d", motivation_canada: "e", }; delete all[field]; const result = validateReponses(all); expect("error" in result).toBe(true); }); it("rejette les champs vides ou whitespace", async () => { const { validateReponses } = await import("../presentationController"); const result = validateReponses({ prenom_age_ville: " ", formation_metier: "b", situation_familiale: "c", loisirs: "d", motivation_canada: "e", }); expect("error" in result).toBe(true); }); }); // Pipeline complet — fetch mocké. describe("presentationController.generate", () => { beforeEach(() => { vi.resetModules(); vi.restoreAllMocks(); }); const VALID_REPONSES = { prenom_age_ville: "Pierre, 30 ans, Alger", formation_metier: "Ingénieur", situation_familiale: "Marié", loisirs: "Lecture", motivation_canada: "Travail", }; it("succès → renvoie { presentation: string }", async () => { vi.stubGlobal( "fetch", vi.fn().mockResolvedValue({ ok: true, json: async () => ({ choices: [ { message: { content: "Bonjour, je m'appelle Pierre. Voilà." } }, ], }), }), ); const { generate } = await import("../presentationController"); const result = await generate(VALID_REPONSES); expect("data" in result).toBe(true); if ("data" in result) { expect(result.data.presentation).toContain("Pierre"); } }); it("DeepSeek non-OK → INTERNAL_ERROR 500", async () => { vi.stubGlobal( "fetch", vi.fn().mockResolvedValue({ ok: false, status: 500, statusText: "I" }), ); const { generate } = await import("../presentationController"); const result = await generate(VALID_REPONSES); expect("error" in result).toBe(true); if ("error" in result) { expect(result.code).toBe("INTERNAL_ERROR"); expect(result.status).toBe(500); } }); it("réponse vide → INTERNAL_ERROR 500", async () => { vi.stubGlobal( "fetch", vi.fn().mockResolvedValue({ ok: true, json: async () => ({ choices: [{ message: { content: "" } }] }), }), ); const { generate } = await import("../presentationController"); const result = await generate(VALID_REPONSES); expect("error" in result).toBe(true); }); it("fetch throw (timeout) → INTERNAL_ERROR 500", async () => { vi.stubGlobal( "fetch", vi.fn().mockRejectedValue(new Error("network down")), ); const { generate } = await import("../presentationController"); const result = await generate(VALID_REPONSES); expect("error" in result).toBe(true); if ("error" in result) expect(result.code).toBe("INTERNAL_ERROR"); }); it("rejette les body invalides en court-circuitant fetch", async () => { const fetchSpy = vi.fn(); vi.stubGlobal("fetch", fetchSpy); const { generate } = await import("../presentationController"); const result = await generate({ prenom_age_ville: "" }); expect("error" in result).toBe(true); expect(fetchSpy).not.toHaveBeenCalled(); }); });