Files
Alfred/tests/web/hooks/use-models.test.ts
lanyuanxiaoyao b1dec691e9 refactor(web): 前端目录重构 — consoles/pages → layouts/features + shared
- consoles/admin/ → layouts/admin-layout/
- consoles/workbench/ → layouts/workbench-layout/ + features/chat/
- pages/ → features/ (dashboard, models, projects, not-found)
- components/ → shared/components/
- hooks/ → shared/hooks/
- utils/ → shared/utils/
- 更新所有 import 路径 (src/web/ + tests/web/)
- 更新开发文档 (README.md, frontend.md, architecture.md)
2026-06-02 23:17:28 +08:00

112 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, test } from "bun:test";
import {
createModel,
deleteModel,
fetchModel,
fetchModelList,
testModelConnection,
updateModel,
} from "../../../src/web/shared/hooks/use-models";
import { installFetchMock, jsonResponse } from "../test-utils";
const MODEL = {
capabilities: ["text"] as Array<"text">,
contextLength: null,
createdAt: "2024-01-01T00:00:00.000Z",
id: "m1",
maxOutputTokens: null,
modelId: "gpt-4o",
name: "GPT-4o",
providerId: "pv1",
updatedAt: "2024-01-01T00:00:00.000Z",
};
async function expectRejectsWithMessage(action: () => Promise<unknown>, message: string) {
try {
await action();
throw new Error("expected rejection");
} catch (error) {
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toBe(message);
}
}
function jsonBody(body: BodyInit | null | undefined): unknown {
return JSON.parse(typeof body === "string" ? body : "{}");
}
describe("use-models request helpers", () => {
test("fetchModelList 按协议拼接 query 参数(含 providerId", async () => {
const calls = installFetchMock(() => jsonResponse({ items: [MODEL], page: 1, pageSize: 20, total: 1 }));
const result = await fetchModelList({ keyword: "GPT", page: 1, pageSize: 20, providerId: "pv1" });
expect(result.items).toHaveLength(1);
expect(calls[0]?.method).toBe("GET");
expect(calls[0]?.url).toContain("providerId=pv1");
expect(calls[0]?.url).toContain("keyword=GPT");
});
test("模型 CRUD 使用正确 method、URL 与 body", async () => {
const calls = installFetchMock((call) => {
if (call.method === "DELETE") return new Response(null, { status: 204 });
return jsonResponse(
{ model: MODEL },
{ status: call.method === "POST" && call.url === "/api/models" ? 201 : 200 },
);
});
await createModel({
capabilities: ["text"],
modelId: "gpt-4o",
name: "GPT-4o",
providerId: "pv1",
});
await updateModel("m1", { name: "GPT-4o Mini" });
await deleteModel("m1");
await fetchModel("m1");
expect(calls.map((call) => `${call.method} ${call.url}`)).toEqual([
"POST /api/models",
"PATCH /api/models/m1",
"DELETE /api/models/m1",
"GET /api/models/m1",
]);
expect(jsonBody(calls[0]?.body)).toEqual({
capabilities: ["text"],
modelId: "gpt-4o",
name: "GPT-4o",
providerId: "pv1",
});
expect(jsonBody(calls[1]?.body)).toEqual({ name: "GPT-4o Mini" });
});
test("错误响应优先使用后端 error 字段", async () => {
installFetchMock(() => jsonResponse({ error: "模型名称已存在" }, { status: 409 }));
await expectRejectsWithMessage(
() => createModel({ capabilities: ["text"], modelId: "gpt-4o", name: "重复", providerId: "pv1" }),
"模型名称已存在",
);
});
test("非 JSON 错误响应回退到 HTTP 状态", async () => {
installFetchMock(() => new Response("broken", { status: 500 }));
await expectRejectsWithMessage(() => fetchModel("m-missing"), "HTTP 500");
});
test("testModelConnection 调用正确 URL 和 body", async () => {
const calls = installFetchMock(() => jsonResponse({ modelTestResponse: { message: "模型连接成功", ok: true } }));
const result = await testModelConnection({ modelId: "gpt-4o", providerId: "pv1" });
expect(result.ok).toBe(true);
expect(result.message).toBe("模型连接成功");
expect(calls[0]?.method).toBe("POST");
expect(calls[0]?.url).toBe("/api/models/test");
expect(jsonBody(calls[0]?.body)).toEqual({ modelId: "gpt-4o", providerId: "pv1" });
});
});