Files
wehub-resource-sync 64ed910d3f
CI / Backend Checks (push) Has been cancelled
CI / Frontend Checks (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 11:57:20 +08:00

149 lines
4.5 KiB
TypeScript

import { act, renderHook } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import {
hasClaudeOneMMarker,
setClaudeOneMMarker,
stripClaudeOneMMarker,
useModelState,
} from "@/components/providers/forms/hooks/useModelState";
describe("useModelState", () => {
it("hydrates role models and display names from Claude Code env", () => {
const settingsConfig = JSON.stringify({
env: {
ANTHROPIC_MODEL: "fallback-model",
ANTHROPIC_SMALL_FAST_MODEL: "legacy-small",
ANTHROPIC_DEFAULT_SONNET_MODEL: "deepseek-v4-pro",
ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: "DeepSeek V4 Pro",
ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-k2",
ANTHROPIC_DEFAULT_OPUS_MODEL_NAME: "Kimi K2",
CLAUDE_CODE_SUBAGENT_MODEL: "subagent-model[1M]",
},
});
const { result } = renderHook(() =>
useModelState({
settingsConfig,
onConfigChange: vi.fn(),
}),
);
expect(result.current.claudeModel).toBe("fallback-model");
expect(result.current.defaultSonnetModel).toBe("deepseek-v4-pro");
expect(result.current.defaultSonnetModelName).toBe("DeepSeek V4 Pro");
expect(result.current.defaultOpusModel).toBe("kimi-k2");
expect(result.current.defaultOpusModelName).toBe("Kimi K2");
expect(result.current.defaultHaikuModel).toBe("legacy-small");
expect(result.current.defaultHaikuModelName).toBe("legacy-small");
expect(result.current.subagentModel).toBe("subagent-model[1M]");
});
it("writes and clears role display-name env fields without changing model mapping", () => {
let latestConfig = JSON.stringify({
env: {
ANTHROPIC_DEFAULT_SONNET_MODEL: "deepseek-v4-pro",
},
});
const onConfigChange = vi.fn((config: string) => {
latestConfig = config;
});
const { result } = renderHook(() =>
useModelState({
settingsConfig: latestConfig,
onConfigChange,
}),
);
act(() => {
result.current.handleModelChange(
"ANTHROPIC_DEFAULT_SONNET_MODEL_NAME",
"DeepSeek V4 Pro",
);
});
let env = JSON.parse(latestConfig).env;
expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe("deepseek-v4-pro");
expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME).toBe("DeepSeek V4 Pro");
act(() => {
result.current.handleModelChange(
"ANTHROPIC_DEFAULT_SONNET_MODEL_NAME",
"",
);
});
env = JSON.parse(latestConfig).env;
expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe("deepseek-v4-pro");
expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME).toBeUndefined();
});
it("keeps the 1M marker on request models but strips it from fallback display names", () => {
const settingsConfig = JSON.stringify({
env: {
ANTHROPIC_DEFAULT_SONNET_MODEL: "deepseek-v4-pro[1M]",
},
});
const { result } = renderHook(() =>
useModelState({
settingsConfig,
onConfigChange: vi.fn(),
}),
);
expect(result.current.defaultSonnetModel).toBe("deepseek-v4-pro[1M]");
expect(result.current.defaultSonnetModelName).toBe("deepseek-v4-pro");
});
it("writes and clears the Claude Code subagent model env field", () => {
let latestConfig = JSON.stringify({
env: {
ANTHROPIC_MODEL: "fallback-model",
},
});
const onConfigChange = vi.fn((config: string) => {
latestConfig = config;
});
const { result } = renderHook(() =>
useModelState({
settingsConfig: latestConfig,
onConfigChange,
}),
);
act(() => {
result.current.handleModelChange(
"CLAUDE_CODE_SUBAGENT_MODEL",
"subagent-model[1M]",
);
});
let env = JSON.parse(latestConfig).env;
expect(env.ANTHROPIC_MODEL).toBe("fallback-model");
expect(env.CLAUDE_CODE_SUBAGENT_MODEL).toBe("subagent-model[1M]");
act(() => {
result.current.handleModelChange("CLAUDE_CODE_SUBAGENT_MODEL", "");
});
env = JSON.parse(latestConfig).env;
expect(env.CLAUDE_CODE_SUBAGENT_MODEL).toBeUndefined();
});
it("normalizes Claude Code 1M markers for UI toggles", () => {
expect(hasClaudeOneMMarker("deepseek-v4-pro[1m]")).toBe(true);
expect(hasClaudeOneMMarker("deepseek-v4-pro [1M] ")).toBe(true);
expect(stripClaudeOneMMarker("deepseek-v4-pro [1M] ")).toBe(
"deepseek-v4-pro",
);
expect(setClaudeOneMMarker("deepseek-v4-pro [1M]", false)).toBe(
"deepseek-v4-pro",
);
expect(setClaudeOneMMarker("deepseek-v4-pro", true)).toBe(
"deepseek-v4-pro[1M]",
);
});
});