import { describe, expect, mock, test } from "bun:test"; import { handleResponse, handleVoidResponse } from "../../../src/web/utils/api"; function expectRejects(action: () => Promise, message: string) { return action().then( () => { throw new Error("expected rejection"); }, (error: unknown) => { expect((error as Error).message).toBe(message); }, ); } function non200Response(body: unknown, status: number): Response { return new Response(JSON.stringify(body), { headers: { "Content-Type": "application/json" }, status, }); } function spyConsoleLog() { // eslint-disable-next-line @typescript-eslint/no-explicit-any const spy = mock((..._args: any[]) => {}); const orig = console.log; console.log = spy; return { orig, restore: () => (console.log = orig), spy }; } function spyConsoleWarn() { // eslint-disable-next-line @typescript-eslint/no-explicit-any const spy = mock((..._args: any[]) => {}); const orig = console.warn; console.warn = spy; return { orig, restore: () => (console.warn = orig), spy }; } describe("api.ts 日志行为", () => { test("handleResponse 非 200 响应输出 warn 日志", async () => { const { restore, spy } = spyConsoleWarn(); const response = non200Response({ error: "项目名称已存在" }, 409); await expectRejects(() => handleResponse(response, (d) => d), "项目名称已存在"); restore(); expect(spy).toHaveBeenCalledTimes(1); const msg = spy.mock.calls[0]![0] as string; const data = spy.mock.calls[0]![1] as Record; expect(msg).toMatch(/\[Alfred:WARN\] API request failed/); expect(data).toBeObject(); expect(data).toHaveProperty("duration"); expect(data).toHaveProperty("errorBody", "项目名称已存在"); expect(data).toHaveProperty("status", 409); expect(data).toHaveProperty("url"); }); test("handleVoidResponse 非 200 响应输出 warn 日志", async () => { const { restore, spy } = spyConsoleWarn(); const response = non200Response({ error: "服务器错误" }, 500); await expectRejects(() => handleVoidResponse(response), "服务器错误"); restore(); expect(spy).toHaveBeenCalledTimes(1); const msg = spy.mock.calls[0]![0] as string; const data = spy.mock.calls[0]![1] as Record; expect(msg).toMatch(/\[Alfred:WARN\] API request failed/); expect(data).toHaveProperty("duration"); expect(data).toHaveProperty("errorBody", "服务器错误"); expect(data).toHaveProperty("status", 500); }); test("handleResponse 非 JSON 错误响应回退到 HTTP 状态", async () => { const { restore, spy } = spyConsoleWarn(); const response = new Response("broken", { status: 503 }); await expectRejects(() => handleResponse(response, (d) => d), "HTTP 503"); restore(); expect(spy).toHaveBeenCalledTimes(1); const data = spy.mock.calls[0]![1] as Record; expect(data).toHaveProperty("errorBody", "HTTP 503"); expect(data).toHaveProperty("status", 503); }); test("handleResponse 成功响应在 DEV 模式输出 debug 日志", async () => { const { restore, spy } = spyConsoleLog(); (import.meta.env as Record)["DEV"] = true; const response = new Response(JSON.stringify({ ok: true }), { headers: { "Content-Type": "application/json" }, status: 200, }); await handleResponse(response, (d) => d); (import.meta.env as Record)["DEV"] = undefined; restore(); expect(spy).toHaveBeenCalledTimes(1); const msg = spy.mock.calls[0]![0] as string; const data = spy.mock.calls[0]![1] as Record; expect(msg).toMatch(/\[Alfred:DEBUG\] API request/); expect(data).toBeObject(); expect(data).toHaveProperty("duration"); expect(data).toHaveProperty("status", 200); expect(data).toHaveProperty("url"); }); test("handleVoidResponse 成功响应在 DEV 模式输出 debug 日志", async () => { const { restore, spy } = spyConsoleLog(); (import.meta.env as Record)["DEV"] = true; const response = new Response(null, { status: 204 }); await handleVoidResponse(response); (import.meta.env as Record)["DEV"] = undefined; restore(); expect(spy).toHaveBeenCalledTimes(1); const msg = spy.mock.calls[0]![0] as string; const data = spy.mock.calls[0]![1] as Record; expect(msg).toMatch(/\[Alfred:DEBUG\] API request/); expect(data).toHaveProperty("duration"); expect(data).toHaveProperty("status", 204); expect(data).toHaveProperty("url"); }); });