// Mock de fetch() pour les tests — supporte méthodes HTTP et status codes type HttpMethod = "GET" | "POST" | "PUT" | "DELETE"; export interface MockRoute { method?: HttpMethod; status?: number; body?: unknown; headers?: Record; } // deno-lint-ignore no-explicit-any let _originalFetch: ((input: any, init?: any) => Promise) | null = null; let _calls: { url: string; method: string; body?: unknown }[] = []; /** * Remplace globalThis.fetch par un mock configurable. * * Usage simple (GET 200 par défaut) : * mockFetch({ "/students": studentsData }) * * Usage avancé (méthode + status) : * mockFetch({ "/students": { method: "POST", status: 201, body: newStudent } }) */ export function mockFetch( routes: Record, ): void { _originalFetch = globalThis.fetch; _calls = []; globalThis.fetch = async ( input: string | URL | Request, init?: RequestInit, ): Promise => { const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url; const method = (init?.method ?? "GET").toUpperCase(); // Parse le body si présent let reqBody: unknown = undefined; if (init?.body) { try { reqBody = JSON.parse(init.body as string); } catch { reqBody = init.body; } } _calls.push({ url, method, body: reqBody }); for (const [pattern, config] of Object.entries(routes)) { if (!url.includes(pattern)) continue; // Config simple : la valeur est directement le body de réponse (GET 200) if (!isRouteConfig(config)) { return new Response(JSON.stringify(config), { status: 200, headers: { "Content-Type": "application/json" }, }); } // Config avancée : vérifier la méthode si spécifiée if (config.method && config.method !== method) continue; const status = config.status ?? 200; // 204 : pas de body if (status === 204) { return new Response(null, { status: 204 }); } return new Response( config.body !== undefined ? JSON.stringify(config.body) : null, { status, headers: { "Content-Type": "application/json", ...config.headers, }, }, ); } return new Response(JSON.stringify({ error: "Not Found" }), { status: 404, headers: { "Content-Type": "application/json" }, }); }; } /** * Restaure le fetch original. */ export function restoreFetch(): void { if (_originalFetch) { globalThis.fetch = _originalFetch; _originalFetch = null; } _calls = []; } /** * Retourne la liste des appels fetch interceptés. */ export function getFetchCalls(): { url: string; method: string; body?: unknown }[] { return [..._calls]; } function isRouteConfig(value: unknown): value is MockRoute { if (typeof value !== "object" || value === null || Array.isArray(value)) { return false; } const v = value as Record; return "status" in v || "method" in v || "body" in v; }