mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-05-06 22:01:44 +08:00
Rewrite setCodexBaseUrl/extractCodexBaseUrl to understand TOML section boundaries, ensuring base_url is written into the correct [model_providers.<name>] section instead of being appended to file end. - Add section-aware TOML helpers in providerConfigUtils.ts - Extract shared update_codex_toml_field/remove_codex_toml_base_url_if in codex_config.rs, deduplicate proxy.rs TOML editing logic - Replace scattered inline base_url regexes with extractCodexBaseUrl() - Add comprehensive tests for both Rust and TypeScript implementations
152 lines
4.5 KiB
TypeScript
152 lines
4.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
extractCodexBaseUrl,
|
|
extractCodexModelName,
|
|
setCodexBaseUrl,
|
|
setCodexModelName,
|
|
} from "@/utils/providerConfigUtils";
|
|
|
|
describe("Codex TOML utils", () => {
|
|
it("removes base_url line when set to empty", () => {
|
|
const input = [
|
|
'model_provider = "openai"',
|
|
'base_url = "https://api.example.com/v1"',
|
|
'model = "gpt-5-codex"',
|
|
"",
|
|
].join("\n");
|
|
|
|
const output = setCodexBaseUrl(input, "");
|
|
|
|
expect(output).not.toMatch(/^\s*base_url\s*=/m);
|
|
expect(extractCodexBaseUrl(output)).toBeUndefined();
|
|
expect(extractCodexModelName(output)).toBe("gpt-5-codex");
|
|
});
|
|
|
|
it("removes only the top-level model line when set to empty", () => {
|
|
const input = [
|
|
'model_provider = "openai"',
|
|
'base_url = "https://api.example.com/v1"',
|
|
'model = "gpt-5-codex"',
|
|
"",
|
|
"[profiles.default]",
|
|
'model = "profile-model"',
|
|
"",
|
|
].join("\n");
|
|
|
|
const output = setCodexModelName(input, "");
|
|
|
|
expect(output).not.toMatch(/^model\s*=\s*"gpt-5-codex"$/m);
|
|
expect(output).toMatch(/^\[profiles\.default\]\nmodel = "profile-model"$/m);
|
|
expect(extractCodexModelName(output)).toBeUndefined();
|
|
expect(extractCodexBaseUrl(output)).toBe("https://api.example.com/v1");
|
|
});
|
|
|
|
it("updates existing values when non-empty", () => {
|
|
const input = [
|
|
'model_provider = "openai"',
|
|
"base_url = 'https://old.example/v1'",
|
|
'model = "old-model"',
|
|
"",
|
|
].join("\n");
|
|
|
|
const output1 = setCodexBaseUrl(input, " https://new.example/v1 \n");
|
|
expect(extractCodexBaseUrl(output1)).toBe("https://new.example/v1");
|
|
|
|
const output2 = setCodexModelName(output1, " new-model \n");
|
|
expect(extractCodexModelName(output2)).toBe("new-model");
|
|
});
|
|
|
|
it("reads and writes base_url in the active provider section", () => {
|
|
const input = [
|
|
'model_provider = "custom"',
|
|
'model = "gpt-5.4"',
|
|
"",
|
|
"[model_providers.custom]",
|
|
'name = "custom"',
|
|
'wire_api = "responses"',
|
|
"",
|
|
"[profiles.default]",
|
|
'approval_policy = "never"',
|
|
"",
|
|
].join("\n");
|
|
|
|
const output = setCodexBaseUrl(input, "https://api.example.com/v1");
|
|
|
|
expect(output).toContain(
|
|
'[model_providers.custom]\nname = "custom"\nwire_api = "responses"\nbase_url = "https://api.example.com/v1"',
|
|
);
|
|
expect(extractCodexBaseUrl(output)).toBe("https://api.example.com/v1");
|
|
});
|
|
|
|
it("recovers a single misplaced base_url from another section", () => {
|
|
const input = [
|
|
'model_provider = "custom"',
|
|
'model = "gpt-5.4"',
|
|
"",
|
|
"[model_providers.custom]",
|
|
'name = "custom"',
|
|
'wire_api = "responses"',
|
|
"",
|
|
"[profiles.default]",
|
|
'approval_policy = "never"',
|
|
'base_url = "https://wrong.example/v1"',
|
|
"",
|
|
].join("\n");
|
|
|
|
expect(extractCodexBaseUrl(input)).toBe("https://wrong.example/v1");
|
|
|
|
const output = setCodexBaseUrl(input, "https://fixed.example/v1");
|
|
|
|
expect(output).toContain(
|
|
'[model_providers.custom]\nname = "custom"\nwire_api = "responses"\nbase_url = "https://fixed.example/v1"',
|
|
);
|
|
expect(output).not.toContain("https://wrong.example/v1");
|
|
expect(output.match(/base_url\s*=/g)).toHaveLength(1);
|
|
});
|
|
|
|
it("does not treat mcp_servers base_url as provider base_url", () => {
|
|
const input = [
|
|
'model_provider = "azure"',
|
|
'model = "gpt-4"',
|
|
"",
|
|
"[model_providers.azure]",
|
|
'name = "Azure OpenAI"',
|
|
'wire_api = "responses"',
|
|
"",
|
|
"[mcp_servers.my_server]",
|
|
'base_url = "http://localhost:8080"',
|
|
"",
|
|
].join("\n");
|
|
|
|
expect(extractCodexBaseUrl(input)).toBeUndefined();
|
|
|
|
const output = setCodexBaseUrl(input, "https://new.azure/v1");
|
|
|
|
expect(output).toContain(
|
|
'[model_providers.azure]\nname = "Azure OpenAI"\nwire_api = "responses"\nbase_url = "https://new.azure/v1"',
|
|
);
|
|
expect(output).toContain(
|
|
'[mcp_servers.my_server]\nbase_url = "http://localhost:8080"',
|
|
);
|
|
});
|
|
|
|
it("reads model only from the top-level config", () => {
|
|
const input = [
|
|
'model_provider = "custom"',
|
|
"",
|
|
"[profiles.default]",
|
|
'model = "profile-model"',
|
|
"",
|
|
].join("\n");
|
|
|
|
expect(extractCodexModelName(input)).toBeUndefined();
|
|
});
|
|
|
|
it("handles single-quoted values", () => {
|
|
const input = "base_url = 'https://api.example.com/v1'\nmodel = 'gpt-5'\n";
|
|
|
|
expect(extractCodexBaseUrl(input)).toBe("https://api.example.com/v1");
|
|
expect(extractCodexModelName(input)).toBe("gpt-5");
|
|
});
|
|
});
|