Files
cc-switch/tests/hooks/useCodexConfigState.catalog.test.ts
Jason 85b38c1bed refactor(codex): decouple model mapping from local-routing toggle
Align the Codex provider form with Claude Code. Model mapping (the
catalog) is independent from route takeover: native Responses providers
(MiMo, Doubao, MiniMax) need it for direct connect with no proxy, while
Chat providers use the proxy regardless of any per-provider flag.

- Remove the "Needs Local Routing" toggle. It had no backend field and
  only gated catalog/reasoning persistence, which is equivalent to
  "is the mapping filled".
- Always show model mapping for non-official providers; persist when the
  list is non-empty (backend already keys off modelCatalog.models).
- Gate reasoning visibility/persistence on Chat format, not the toggle.
- Mark the Chat upstream-format option "routing required" and refresh the
  advanced-section hint (zh/en/ja/zh-TW); drop dead localRouting* keys.
- Move the model-mapping block above the custom User-Agent block.

fix(codex): preserve native-profile hidden catalog fields on edit

useCodexConfigState dropped supportsParallelToolCalls / inputModalities /
baseInstructions when loading a saved provider, so editing and saving a
native Responses provider lost parallel tools, image input and the
official base instructions from the generated catalog. Preserve them on
load (camelCase + snake_case) and compare them in the row sync. Add a
regression test.
2026-06-29 23:30:58 +08:00

80 lines
2.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { renderHook } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import { useCodexConfigState } from "@/components/providers/forms/hooks/useCodexConfigState";
// 回归:编辑已存在的原生 Responses 供应商时,读回 modelCatalog 必须保留隐藏字段
// (supportsParallelToolCalls / inputModalities / baseInstructions),否则保存会
// 把它们剥掉,导致生成的 Codex catalog 丢官方 base_instructions、并行工具、图像模态。
//
// 注意:initialData 必须是稳定引用(hook 的 init effect 依赖 [initialData])。
// 写成内联字面量会每次 re-render 产生新引用 → effect 反复 setState → 死循环 OOM。
describe("useCodexConfigState catalog load", () => {
it("preserves native-profile hidden fields (camelCase, DB SSOT)", () => {
const initialData = {
settingsConfig: {
auth: { OPENAI_API_KEY: "sk-x" },
config: "",
modelCatalog: {
models: [
{
model: "MiniMax-M3",
displayName: "MiniMax-M3",
contextWindow: 1000000,
supportsParallelToolCalls: true,
inputModalities: ["text", "image"],
baseInstructions: "You are Codex, based on MiniMax-M3.",
},
],
},
},
};
const { result } = renderHook(() => useCodexConfigState({ initialData }));
expect(result.current.codexCatalogModels).toEqual([
{
model: "MiniMax-M3",
displayName: "MiniMax-M3",
contextWindow: 1000000,
supportsParallelToolCalls: true,
inputModalities: ["text", "image"],
baseInstructions: "You are Codex, based on MiniMax-M3.",
},
]);
});
it("maps snake_case hidden fields (live reverse-parse fallback) to camelCase", () => {
const initialData = {
settingsConfig: {
auth: {},
config: "",
modelCatalog: {
models: [
{
model: "mimo-v2.5-pro",
display_name: "MiMo V2.5 Pro",
context_window: 262144,
supports_parallel_tool_calls: false,
input_modalities: ["text"],
base_instructions: "You are MiMo, developed by Xiaomi.",
},
],
},
},
};
const { result } = renderHook(() => useCodexConfigState({ initialData }));
expect(result.current.codexCatalogModels).toEqual([
{
model: "mimo-v2.5-pro",
displayName: "MiMo V2.5 Pro",
contextWindow: 262144,
supportsParallelToolCalls: false,
inputModalities: ["text"],
baseInstructions: "You are MiMo, developed by Xiaomi.",
},
]);
});
});