/* eslint-disable @typescript-eslint/no-empty-function */ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { render } from "@testing-library/react"; import { describe, expect, mock, test } from "bun:test"; import { createElement, useRef } from "react"; import { useCreateModel, useDeleteModel, useTestModelConnection, useUpdateModel, } from "../../../src/web/hooks/use-models"; import { useArchiveProject, useCreateProject, useDeleteProject, useRestoreProject, useUpdateProject, } from "../../../src/web/hooks/use-projects"; import { useCreateProvider, useDeleteProvider, useTestProviderConfig, useUpdateProvider, } from "../../../src/web/hooks/use-providers"; import { installFetchMock, jsonResponse } from "../test-utils"; const MODEL = { autoAdapt: true, capabilities: ["text"] as string[], createdAt: "2024-01-01T00:00:00.000Z", customApiKey: null, customBaseUrl: null, description: "测试模型", id: "m1", modelId: "gpt-4", name: "测试模型", providerId: "prov-1", updatedAt: "2024-01-01T00:00:00.000Z", }; const PROJECT = { archivedAt: null, createdAt: "2024-01-01T00:00:00.000Z", description: "测试", id: "p1", name: "测试项目", status: "active" as const, updatedAt: "2024-01-01T00:00:00.000Z", }; const PROVIDER = { createdAt: "2024-01-01T00:00:00.000Z", id: "prov-1", name: "测试供应商", type: "openai" as const, updatedAt: "2024-01-01T00:00:00.000Z", }; function getLogMessages(spy: ReturnType) { return spy.mock.calls.map((c) => c[0] as string).filter((s) => s.includes("[Alfred:INFO]")); } function makeQueryClient() { return new QueryClient({ defaultOptions: { queries: { retry: false } }, }); } function setupModelFetches(result: unknown) { installFetchMock((call) => { if (call.method === "DELETE") return new Response(null, { status: 204 }); if (call.url.includes("test")) return jsonResponse({ modelTestResponse: { message: "ok", ok: true } }); return jsonResponse({ model: result }, { status: 201 }); }); } function setupProjectFetches(result: unknown) { installFetchMock((call) => { if (call.method === "DELETE") return new Response(null, { status: 204 }); if (call.url.includes("archive")) return jsonResponse({ project: result }); if (call.url.includes("restore")) return jsonResponse({ project: result }); return jsonResponse({ project: result }, { status: 201 }); }); } function setupProviderFetches(result: unknown) { installFetchMock((call) => { if (call.method === "DELETE") return new Response(null, { status: 204 }); if (call.url.includes("test")) return jsonResponse({ providerTestResponse: { message: "ok", ok: true } }); return jsonResponse({ provider: result }, { status: 201 }); }); } 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 }; } describe("useProjects onSuccess 日志", () => { const qc = makeQueryClient(); test("create onSuccess", async () => { setupProjectFetches(PROJECT); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useCreateProject(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate({ name: "x" })); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/项目创建成功/); restore(); }); test("update onSuccess", async () => { setupProjectFetches(PROJECT); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useUpdateProject(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate({ data: { name: "y" }, id: "p1" })); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/项目更新成功/); restore(); }); test("delete onSuccess", async () => { setupProjectFetches(PROJECT); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useDeleteProject(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate("p1")); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/项目删除成功/); restore(); }); test("archive onSuccess", async () => { setupProjectFetches(PROJECT); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useArchiveProject(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate("p1")); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/项目归档成功/); restore(); }); test("restore onSuccess", async () => { setupProjectFetches(PROJECT); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useRestoreProject(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate("p1")); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/项目恢复成功/); restore(); }); }); describe("useModels onSuccess 日志", () => { const qc = makeQueryClient(); test("create onSuccess", async () => { setupModelFetches(MODEL); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useCreateModel(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate({ capabilities: ["text"], modelId: "gpt-4", name: "x", providerId: "p1" })); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/模型创建成功/); restore(); }); test("update onSuccess", async () => { setupModelFetches(MODEL); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useUpdateModel(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate({ data: { name: "y" }, id: "m1" })); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/模型更新成功/); restore(); }); test("delete onSuccess", async () => { setupModelFetches(MODEL); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useDeleteModel(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate("m1")); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/模型删除成功/); restore(); }); test("test onSuccess", async () => { setupModelFetches(MODEL); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useTestModelConnection(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate({ modelId: "gpt-4", providerId: "p1" })); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); restore(); // useTestModelConnection has no onSuccess logger const infoCalls = spy.mock.calls.filter((c) => typeof c[0] === "string" && c[0].includes("[Alfred:INFO]")); expect(infoCalls.length).toBe(0); }); }); describe("useProviders onSuccess 日志", () => { const qc = makeQueryClient(); test("create onSuccess", async () => { setupProviderFetches(PROVIDER); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useCreateProvider(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate({ apiKey: "k", baseUrl: "http://x", name: "x", type: "openai" })); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/供应商创建成功/); restore(); }); test("update onSuccess", async () => { setupProviderFetches(PROVIDER); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useUpdateProvider(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate({ data: { name: "y" }, id: "prov-1" })); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/供应商更新成功/); restore(); }); test("delete onSuccess", async () => { setupProviderFetches(PROVIDER); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useDeleteProvider(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate("prov-1")); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); const msgs = getLogMessages(spy); expect(msgs).toHaveLength(1); expect(msgs[0]).toMatch(/供应商删除成功/); restore(); }); test("test onSuccess", async () => { setupProviderFetches(PROVIDER); const { restore, spy } = spyConsoleLog(); function T({ onResult }: { onResult: (fn: () => void) => void }) { const { mutate } = useTestProviderConfig(); const c = useRef(false); if (!c.current) { c.current = true; onResult(() => mutate({ apiKey: "k", baseUrl: "http://x", name: "x", type: "openai" })); } return null; } render(createElement(QueryClientProvider, { client: qc }, createElement(T, { onResult: (fn) => fn() }))); await new Promise((r) => setTimeout(r, 200)); restore(); // useTestProviderConfig has no onSuccess logger const infoMsgs = spy.mock.calls.filter((c) => typeof c[0] === "string" && String(c[0]).includes("[Alfred:INFO]")); expect(infoMsgs.length).toBe(0); }); });