import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { Provider } from "@/types";
const apiMocks = vi.hoisted(() => ({
getCurrent: vi.fn(),
getLiveProviderSettings: vi.fn(),
getOpenClawLiveProvider: vi.fn(),
}));
vi.mock("@/lib/api", () => ({
providersApi: {
getCurrent: apiMocks.getCurrent,
},
vscodeApi: {
getLiveProviderSettings: apiMocks.getLiveProviderSettings,
},
openclawApi: {
getLiveProvider: apiMocks.getOpenClawLiveProvider,
},
}));
vi.mock("@/components/common/FullScreenPanel", () => ({
FullScreenPanel: ({
isOpen,
children,
footer,
}: {
isOpen: boolean;
children: React.ReactNode;
footer?: React.ReactNode;
}) =>
isOpen ? (
) : null,
}));
vi.mock("@/components/providers/forms/ProviderForm", () => ({
ProviderForm: ({
initialData,
onSubmit,
isProxyTakeover,
}: {
initialData: {
name?: string;
websiteUrl?: string;
notes?: string;
settingsConfig?: Record;
meta?: Record;
icon?: string;
iconColor?: string;
};
onSubmit: (values: {
name: string;
websiteUrl: string;
notes?: string;
settingsConfig: string;
meta?: Record;
icon?: string;
iconColor?: string;
}) => void;
isProxyTakeover?: boolean;
}) => (
),
}));
import { EditProviderDialog } from "@/components/providers/EditProviderDialog";
describe("EditProviderDialog", () => {
beforeEach(() => {
apiMocks.getCurrent.mockReset();
apiMocks.getLiveProviderSettings.mockReset();
apiMocks.getOpenClawLiveProvider.mockReset();
});
it("保留 Codex 数据库中的 modelCatalog,避免 live 配置缺字段时清空模型映射", async () => {
const dbModelCatalog = {
models: [
{
model: "deepseek-v4-flash",
displayName: "DeepSeek V4 Flash",
contextWindow: 1000000,
},
],
};
const provider: Provider = {
id: "deepseek",
name: "DeepSeek",
category: "aggregator",
settingsConfig: {
auth: {
OPENAI_API_KEY: "db-key",
},
config: 'model_provider = "custom"\nmodel = "deepseek-v4-flash"\n',
modelCatalog: dbModelCatalog,
},
};
const liveSettings = {
auth: {
OPENAI_API_KEY: "live-key",
},
config: 'model_provider = "custom"\nmodel = "deepseek-v4-pro"\n',
};
const handleSubmit = vi.fn().mockResolvedValue(undefined);
apiMocks.getCurrent.mockResolvedValue(provider.id);
apiMocks.getLiveProviderSettings.mockResolvedValue(liveSettings);
render(
,
);
await waitFor(() => {
expect(
JSON.parse(screen.getByTestId("settings-config").textContent ?? "{}"),
).toEqual({
...liveSettings,
modelCatalog: dbModelCatalog,
});
});
fireEvent.click(screen.getByRole("button", { name: "common.save" }));
await waitFor(() => expect(handleSubmit).toHaveBeenCalledTimes(1));
expect(handleSubmit.mock.calls[0][0].provider.settingsConfig).toEqual({
...liveSettings,
modelCatalog: dbModelCatalog,
});
});
it("代理接管中编辑 Codex 供应商时展示数据库配置而不是读取 live 代理配置", async () => {
const provider: Provider = {
id: "deepseek",
name: "DeepSeek",
category: "custom",
settingsConfig: {
auth: {
OPENAI_API_KEY: "db-key",
},
config:
'model_provider = "custom"\n[model_providers.custom]\nbase_url = "https://api.deepseek.com/v1"\n',
},
};
apiMocks.getCurrent.mockResolvedValue(provider.id);
apiMocks.getLiveProviderSettings.mockResolvedValue({
auth: {
OPENAI_API_KEY: "PROXY_MANAGED",
},
config:
'model_provider = "custom"\n[model_providers.custom]\nbase_url = "http://127.0.0.1:15721/v1"\nexperimental_bearer_token = "PROXY_MANAGED"\n',
});
render(
,
);
await waitFor(() => {
expect(screen.getByTestId("is-proxy-takeover").textContent).toBe("true");
});
expect(apiMocks.getLiveProviderSettings).not.toHaveBeenCalled();
expect(
JSON.parse(screen.getByTestId("settings-config").textContent ?? "{}"),
).toEqual(provider.settingsConfig);
});
});