test: 添加模型路由单元测试 (测试计划 05)
覆盖 isModelAlias、isModelFamilyAlias、getAPIProvider、 isFirstPartyAnthropicBaseUrl、firstPartyNameToCanonical,共 40 个测试用例。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
583d04331b
commit
25839ab454
70
src/utils/model/__tests__/aliases.test.ts
Normal file
70
src/utils/model/__tests__/aliases.test.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { describe, expect, test } from "bun:test";
|
||||||
|
import { isModelAlias, isModelFamilyAlias } from "../aliases";
|
||||||
|
|
||||||
|
describe("isModelAlias", () => {
|
||||||
|
test('returns true for "sonnet"', () => {
|
||||||
|
expect(isModelAlias("sonnet")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns true for "opus"', () => {
|
||||||
|
expect(isModelAlias("opus")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns true for "haiku"', () => {
|
||||||
|
expect(isModelAlias("haiku")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns true for "best"', () => {
|
||||||
|
expect(isModelAlias("best")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns true for "sonnet[1m]"', () => {
|
||||||
|
expect(isModelAlias("sonnet[1m]")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns true for "opus[1m]"', () => {
|
||||||
|
expect(isModelAlias("opus[1m]")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns true for "opusplan"', () => {
|
||||||
|
expect(isModelAlias("opusplan")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false for full model ID", () => {
|
||||||
|
expect(isModelAlias("claude-sonnet-4-6-20250514")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false for unknown string", () => {
|
||||||
|
expect(isModelAlias("gpt-4")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("is case-sensitive", () => {
|
||||||
|
expect(isModelAlias("Sonnet")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isModelFamilyAlias", () => {
|
||||||
|
test('returns true for "sonnet"', () => {
|
||||||
|
expect(isModelFamilyAlias("sonnet")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns true for "opus"', () => {
|
||||||
|
expect(isModelFamilyAlias("opus")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns true for "haiku"', () => {
|
||||||
|
expect(isModelFamilyAlias("haiku")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns false for "best"', () => {
|
||||||
|
expect(isModelFamilyAlias("best")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns false for "opusplan"', () => {
|
||||||
|
expect(isModelFamilyAlias("opusplan")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns false for "sonnet[1m]"', () => {
|
||||||
|
expect(isModelFamilyAlias("sonnet[1m]")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
92
src/utils/model/__tests__/model.test.ts
Normal file
92
src/utils/model/__tests__/model.test.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { describe, expect, test } from "bun:test";
|
||||||
|
import { firstPartyNameToCanonical } from "../model";
|
||||||
|
|
||||||
|
describe("firstPartyNameToCanonical", () => {
|
||||||
|
test("maps opus-4-6 full name to canonical", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-opus-4-6-20250514")).toBe(
|
||||||
|
"claude-opus-4-6"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps sonnet-4-6 full name", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-sonnet-4-6-20250514")).toBe(
|
||||||
|
"claude-sonnet-4-6"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps haiku-4-5", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-haiku-4-5-20251001")).toBe(
|
||||||
|
"claude-haiku-4-5"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps 3P provider format", () => {
|
||||||
|
expect(
|
||||||
|
firstPartyNameToCanonical("us.anthropic.claude-opus-4-6-v1:0")
|
||||||
|
).toBe("claude-opus-4-6");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps claude-3-7-sonnet", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-3-7-sonnet-20250219")).toBe(
|
||||||
|
"claude-3-7-sonnet"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps claude-3-5-sonnet", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-3-5-sonnet-20241022")).toBe(
|
||||||
|
"claude-3-5-sonnet"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps claude-3-5-haiku", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-3-5-haiku-20241022")).toBe(
|
||||||
|
"claude-3-5-haiku"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps claude-3-opus", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-3-opus-20240229")).toBe(
|
||||||
|
"claude-3-opus"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("is case insensitive", () => {
|
||||||
|
expect(firstPartyNameToCanonical("Claude-Opus-4-6-20250514")).toBe(
|
||||||
|
"claude-opus-4-6"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("falls back to input for unknown model", () => {
|
||||||
|
expect(firstPartyNameToCanonical("unknown-model")).toBe("unknown-model");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("differentiates opus-4 vs opus-4-5 vs opus-4-6", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-opus-4-20240101")).toBe(
|
||||||
|
"claude-opus-4"
|
||||||
|
);
|
||||||
|
expect(firstPartyNameToCanonical("claude-opus-4-5-20240101")).toBe(
|
||||||
|
"claude-opus-4-5"
|
||||||
|
);
|
||||||
|
expect(firstPartyNameToCanonical("claude-opus-4-6-20240101")).toBe(
|
||||||
|
"claude-opus-4-6"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps opus-4-1", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-opus-4-1-20240101")).toBe(
|
||||||
|
"claude-opus-4-1"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps sonnet-4-5", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-sonnet-4-5-20240101")).toBe(
|
||||||
|
"claude-sonnet-4-5"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps sonnet-4", () => {
|
||||||
|
expect(firstPartyNameToCanonical("claude-sonnet-4-20240101")).toBe(
|
||||||
|
"claude-sonnet-4"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
84
src/utils/model/__tests__/providers.test.ts
Normal file
84
src/utils/model/__tests__/providers.test.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { describe, expect, test, beforeEach, afterEach } from "bun:test";
|
||||||
|
import { getAPIProvider, isFirstPartyAnthropicBaseUrl } from "../providers";
|
||||||
|
|
||||||
|
describe("getAPIProvider", () => {
|
||||||
|
const originalEnv = { ...process.env };
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
delete process.env.CLAUDE_CODE_USE_BEDROCK;
|
||||||
|
delete process.env.CLAUDE_CODE_USE_VERTEX;
|
||||||
|
delete process.env.CLAUDE_CODE_USE_FOUNDRY;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns "firstParty" by default', () => {
|
||||||
|
delete process.env.CLAUDE_CODE_USE_BEDROCK;
|
||||||
|
delete process.env.CLAUDE_CODE_USE_VERTEX;
|
||||||
|
delete process.env.CLAUDE_CODE_USE_FOUNDRY;
|
||||||
|
expect(getAPIProvider()).toBe("firstParty");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns "bedrock" when CLAUDE_CODE_USE_BEDROCK is set', () => {
|
||||||
|
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||||
|
expect(getAPIProvider()).toBe("bedrock");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns "vertex" when CLAUDE_CODE_USE_VERTEX is set', () => {
|
||||||
|
process.env.CLAUDE_CODE_USE_VERTEX = "1";
|
||||||
|
expect(getAPIProvider()).toBe("vertex");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns "foundry" when CLAUDE_CODE_USE_FOUNDRY is set', () => {
|
||||||
|
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
|
||||||
|
expect(getAPIProvider()).toBe("foundry");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("bedrock takes precedence over vertex", () => {
|
||||||
|
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||||
|
process.env.CLAUDE_CODE_USE_VERTEX = "1";
|
||||||
|
expect(getAPIProvider()).toBe("bedrock");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isFirstPartyAnthropicBaseUrl", () => {
|
||||||
|
const originalBaseUrl = process.env.ANTHROPIC_BASE_URL;
|
||||||
|
const originalUserType = process.env.USER_TYPE;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (originalBaseUrl !== undefined) {
|
||||||
|
process.env.ANTHROPIC_BASE_URL = originalBaseUrl;
|
||||||
|
} else {
|
||||||
|
delete process.env.ANTHROPIC_BASE_URL;
|
||||||
|
}
|
||||||
|
if (originalUserType !== undefined) {
|
||||||
|
process.env.USER_TYPE = originalUserType;
|
||||||
|
} else {
|
||||||
|
delete process.env.USER_TYPE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true when ANTHROPIC_BASE_URL is not set", () => {
|
||||||
|
delete process.env.ANTHROPIC_BASE_URL;
|
||||||
|
expect(isFirstPartyAnthropicBaseUrl()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true for api.anthropic.com", () => {
|
||||||
|
process.env.ANTHROPIC_BASE_URL = "https://api.anthropic.com";
|
||||||
|
expect(isFirstPartyAnthropicBaseUrl()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false for custom URL", () => {
|
||||||
|
process.env.ANTHROPIC_BASE_URL = "https://my-proxy.com";
|
||||||
|
expect(isFirstPartyAnthropicBaseUrl()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns false for invalid URL", () => {
|
||||||
|
process.env.ANTHROPIC_BASE_URL = "not-a-url";
|
||||||
|
expect(isFirstPartyAnthropicBaseUrl()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns true for staging URL when USER_TYPE is ant", () => {
|
||||||
|
process.env.ANTHROPIC_BASE_URL = "https://api-staging.anthropic.com";
|
||||||
|
process.env.USER_TYPE = "ant";
|
||||||
|
expect(isFirstPartyAnthropicBaseUrl()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user