Files
CrossRelay/src/pages/Providers.test.tsx
T
kate522 f8acf31ebc CrossRelay stage 0+1: Go gateway with Anthropic<->Chat Completions relay, Tauri shell, bilingual management UI
- Go gateway (stdlib only): /v1/messages non-stream + SSE, /v1/messages/count_tokens,
  management API /api/* (providers CRUD, apply/restore, settings, backups, logs)
- Protocol conversion: Anthropic Messages <-> Chat Completions with a streaming
  state machine keyed by content block index (text, tool_use, parallel calls)
- ~/.claude/settings.json guarded read/write: timestamped backups, managed env
  keys only, one-click restore
- Tauri 2 shell: spawns the gateway sidecar, parses the random port from stdout,
  stdin pipe watchdog prevents orphans, kills the child on exit
- React frontend: Dashboard/Providers/Settings, zh-CN + en-US, TanStack Query
- Tests: Go table-driven + httptest integration (all green), vitest + RTL
2026-10-18 17:19:13 +08:00

101 lines
3.8 KiB
TypeScript

import { describe, expect, it, vi, beforeEach } from "vitest";
import { screen, waitFor } from "@testing-library/react";
import { renderWithProviders } from "@/test/utils";
import userEvent from "@testing-library/user-event";
const listProviders = vi.fn();
const createProvider = vi.fn();
const deleteProvider = vi.fn();
const applyProvider = vi.fn();
const restore = vi.fn();
const getStatus = vi.fn();
const getSettings = vi.fn();
const listLogs = vi.fn();
const listBackups = vi.fn();
const updateProvider = vi.fn();
const updateSettings = vi.fn();
vi.mock("@/lib/api", () => ({
api: {
listProviders: () => listProviders(),
createProvider: (input: unknown) => createProvider(input),
updateProvider: (id: string, input: unknown) => updateProvider(id, input),
deleteProvider: (id: string) => deleteProvider(id),
applyProvider: (id: string) => applyProvider(id),
restore: () => restore(),
getStatus: () => getStatus(),
getSettings: () => getSettings(),
updateSettings: (patch: unknown) => updateSettings(patch),
listLogs: () => listLogs(),
listBackups: () => listBackups(),
},
}));
import Providers from "./Providers";
const provider = {
id: "p_1",
name: "DeepSeek",
preset: "deepseek",
base_url: "https://api.deepseek.com/v1",
api_key: "sk-x",
models: { default: "deepseek-chat", sonnet: "deepseek-chat", haiku: "deepseek-chat", opus: "deepseek-chat" },
enabled_for_claude: false,
created_at: "2026-10-18T00:00:00Z",
};
function renderPage() {
return renderWithProviders(<Providers />);
}
beforeEach(() => {
vi.clearAllMocks();
listProviders.mockResolvedValue({ items: [provider] });
createProvider.mockResolvedValue({ ...provider, id: "p_2" });
deleteProvider.mockResolvedValue({ deleted: "p_1" });
applyProvider.mockResolvedValue({ provider, env: { ANTHROPIC_BASE_URL: "http://127.0.0.1:1234" } });
});
describe("Providers page", () => {
it("renders the provider list", async () => {
renderPage();
expect(await screen.findByText("DeepSeek")).toBeInTheDocument();
expect(screen.getByText("https://api.deepseek.com/v1")).toBeInTheDocument();
});
it("validates the create form before submitting", async () => {
const user = userEvent.setup();
renderPage();
await user.click(await screen.findByRole("button", { name: /新增供应商|Add provider/ }));
await user.click(screen.getByRole("button", { name: /保存|Save/ }));
expect(await screen.findByText(/请填写名称|Name is required/)).toBeInTheDocument();
expect(await screen.findByText(/请填写 http|http\(s\) base URL is required/)).toBeInTheDocument();
expect(createProvider).not.toHaveBeenCalled();
});
it("creates a provider with the preset defaults", async () => {
const user = userEvent.setup();
renderPage();
await user.click(await screen.findByRole("button", { name: /新增供应商|Add provider/ }));
await user.type(screen.getByLabelText(/名称|Name/), "Kimi");
await user.type(screen.getByLabelText(/Base URL/), "https://api.moonshot.cn/v1");
await user.type(screen.getByLabelText(/API Key/), "sk-kimi");
await user.click(screen.getByRole("button", { name: /保存|Save/ }));
await waitFor(() => expect(createProvider).toHaveBeenCalledTimes(1));
const input = createProvider.mock.calls[0][0];
expect(input).toMatchObject({
name: "Kimi",
base_url: "https://api.moonshot.cn/v1",
api_key: "sk-kimi",
});
});
it("activates a provider and shows the applied env", async () => {
const user = userEvent.setup();
renderPage();
await user.click(await screen.findByRole("button", { name: /激活并写入|Activate & apply/ }));
await waitFor(() => expect(applyProvider).toHaveBeenCalledWith("p_1"));
expect(await screen.findByText(/ANTHROPIC_BASE_URL/)).toBeInTheDocument();
});
});