Files
Alfred/tests/web/utils/api.test.ts
lanyuanxiaoyao ab7b7fb189 fix: 质量修复 — ESLint 规则 TS6 兼容 + catch 注解 + 空函数体注释化 + 后端架构对齐 + 前端红线修复
- enforce-catch-type: 增加 TSUnknownKeyword 判断,消除28个 TS6 假阳性
- no-empty-function: 统一为注释方案,移除测试/生产分支和 eslint-disable 引导
- logger.ts: 空函数体改为注释说明,删除无用 eslint-disable 指令
- 补充15处 catch 子句 : unknown 类型注解
- 清理7个测试文件失效 eslint-disable 指令
- chat/send.ts: 提取 getModelWithProvider DAO,消除直接 Drizzle 操作
- projects/update.ts: 修复死代码+条件逻辑 bug
- providers/update.ts: 补充至少一个字段校验
- 前端: inline style → CSS className, ProviderFormModal whitespace 校验
- 开发文档: 更新 Zod 使用说明(AI SDK 框架级约束)
2026-06-01 23:11:42 +08:00

134 lines
4.6 KiB
TypeScript

import { describe, expect, mock, test } from "bun:test";
import { handleResponse, handleVoidResponse } from "../../../src/web/utils/api";
function expectRejects(action: () => Promise<unknown>, 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<string, unknown>;
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<string, unknown>;
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<string, unknown>;
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<string, unknown>)["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<string, unknown>)["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<string, unknown>;
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<string, unknown>)["DEV"] = true;
const response = new Response(null, { status: 204 });
await handleVoidResponse(response);
(import.meta.env as Record<string, unknown>)["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<string, unknown>;
expect(msg).toMatch(/\[Alfred:DEBUG\] API request/);
expect(data).toHaveProperty("duration");
expect(data).toHaveProperty("status", 204);
expect(data).toHaveProperty("url");
});
});