Files
cc-switch/tests/hooks/useOpencodeFormState.test.tsx
T
funkpopoandJason 38a7c9c479 fix(opencode): 支持导入和编辑内置供应商覆盖 (#7749)
* feat: Enhance OpenCode provider configuration and import functionality

- Added support for built-in provider defaults in OpenCode configuration.
- Updated OpenCode provider options to allow omitting npm and model definitions.
- Improved import logic for OpenCode providers from live config, ensuring existing credentials are respected.
- Introduced tests for credential handling and built-in provider editing.
- Enhanced UI components to reflect changes in provider configuration and selection.
- Added localization for new built-in defaults feature.

* refactor(tests): enhance test environment isolation and settings management

* Refactor OpenCode provider handling and validation

- Removed TestSettingsGuard to simplify settings management during tests.
- Enhanced validation in handleDuplicateProvider to ensure npm and model definitions are provided for non-OMO providers.
- Updated OpenCodeFormFields to conditionally allow built-in defaults based on props.
- Improved ProviderForm to manage existing OpenCode overrides and prevent incomplete copies from being added.
- Added comprehensive tests for OpenCode provider duplication and validation scenarios.
- Introduced new error messages for better user feedback on provider requirements.
- Created opencode_tests.rs to cover various scenarios for OpenCode provider imports and updates.

---------

Co-authored-by: Jason <farion1231@gmail.com>
2026-09-30 07:55:43 +08:00

148 lines
3.9 KiB
TypeScript

import { act, renderHook } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { useOpencodeFormState } from "@/components/providers/forms/hooks/useOpencodeFormState";
const renderOpencodeFormState = (
initialSettingsConfig: Record<string, unknown>,
) => {
let settingsConfig = JSON.stringify(initialSettingsConfig);
const onSettingsConfigChange = vi.fn((nextConfig: string) => {
settingsConfig = nextConfig;
});
const hook = renderHook(() =>
useOpencodeFormState({
appId: "opencode",
initialData: { settingsConfig: initialSettingsConfig },
onSettingsConfigChange,
getSettingsConfig: () => settingsConfig,
}),
);
return {
...hook,
onSettingsConfigChange,
getSettingsConfig: () => settingsConfig,
};
};
describe("useOpencodeFormState", () => {
it("edits an existing built-in override without inventing a package or models", () => {
const { result, getSettingsConfig } = renderOpencodeFormState({
name: "OpenCode Go",
options: { apiKey: "test-key" },
});
expect(result.current.opencodeNpm).toBe("");
expect(result.current.opencodeModels).toEqual({});
act(() => result.current.handleOpencodeApiKeyChange("edited-test-key"));
expect(JSON.parse(getSettingsConfig())).toEqual({
name: "OpenCode Go",
options: { apiKey: "edited-test-key" },
});
});
it("hydrates provider headers from options", () => {
const { result } = renderOpencodeFormState({
npm: "@ai-sdk/openai-compatible",
options: {
headers: {
"HTTP-Referer": "https://cc-switch.app",
"X-Title": "CC Switch",
},
},
models: {},
});
expect(result.current.opencodeHeaders).toEqual({
"HTTP-Referer": "https://cc-switch.app",
"X-Title": "CC Switch",
});
});
it("writes provider headers to options", () => {
const { result, getSettingsConfig } = renderOpencodeFormState({
npm: "@ai-sdk/openai-compatible",
options: {},
models: {},
});
act(() => {
result.current.handleOpencodeHeadersChange({
"X-Title": "CC Switch",
});
});
expect(JSON.parse(getSettingsConfig()).options.headers).toEqual({
"X-Title": "CC Switch",
});
});
it("removes options.headers when all provider headers are removed", () => {
const { result, getSettingsConfig } = renderOpencodeFormState({
npm: "@ai-sdk/openai-compatible",
options: {
headers: {
"X-Title": "CC Switch",
},
},
models: {},
});
act(() => {
result.current.handleOpencodeHeadersChange({});
});
expect(JSON.parse(getSettingsConfig()).options.headers).toBeUndefined();
});
it("preserves legitimate headers whose names start with header-", () => {
const { result, getSettingsConfig } = renderOpencodeFormState({
npm: "@ai-sdk/openai-compatible",
options: {
headers: {
"header-version": "v1",
"X-Title": "Old",
},
},
models: {},
});
act(() => {
result.current.handleOpencodeHeadersChange({
"header-version": "v1",
"X-Title": "New",
});
});
expect(JSON.parse(getSettingsConfig()).options.headers).toEqual({
"header-version": "v1",
"X-Title": "New",
});
});
it("preserves legitimate options whose names start with option-", () => {
const { result, getSettingsConfig } = renderOpencodeFormState({
npm: "@ai-sdk/openai-compatible",
options: {
"option-mode": "legacy",
timeout: 100,
},
models: {},
});
act(() => {
result.current.handleOpencodeExtraOptionsChange({
"option-mode": "legacy",
timeout: "200",
"draft-option:123": "",
});
});
expect(JSON.parse(getSettingsConfig()).options).toEqual({
"option-mode": "legacy",
timeout: 200,
});
});
});