test(providers): add tests for Z.AI/Zhipu multi-configuration
Update existing test files to cover all four GLM provider variants: - auth-choice-options.test.ts: Group test for zai, zai-coding, zhipu, zhipu-coding auth choice menu presence (follows MiniMax/Moonshot pattern) - program.smoke.test.ts: Add CLI flag wiring tests for --zai-coding-api-key, --zhipu-api-key, --zhipu-coding-api-key - model-auth.test.ts: Add env var resolution tests for: - zai-coding with fallback to ZAI_API_KEY - zhipu with ZHIPU_API_KEY - zhipu-coding with fallback to ZHIPU_API_KEY - provider-usage.test.ts: Add test for zhipu bigmodel.cn endpoint (will fail until fetchZhipuUsage is implemented) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
parent
61d9798b6b
commit
17a4345004
@ -233,6 +233,132 @@ describe("getApiKeyForModel", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves zai-coding API key with fallback to ZAI_API_KEY", async () => {
|
||||
const previous = {
|
||||
coding: process.env.ZAI_CODING_API_KEY,
|
||||
zai: process.env.ZAI_API_KEY,
|
||||
legacy: process.env.Z_AI_API_KEY,
|
||||
};
|
||||
|
||||
try {
|
||||
delete process.env.ZAI_CODING_API_KEY;
|
||||
process.env.ZAI_API_KEY = "zai-fallback-key";
|
||||
delete process.env.Z_AI_API_KEY;
|
||||
|
||||
vi.resetModules();
|
||||
const { resolveApiKeyForProvider } = await import("./model-auth.js");
|
||||
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "zai-coding",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
expect(resolved.apiKey).toBe("zai-fallback-key");
|
||||
expect(resolved.source).toContain("ZAI_API_KEY");
|
||||
} finally {
|
||||
if (previous.coding === undefined) {
|
||||
delete process.env.ZAI_CODING_API_KEY;
|
||||
} else {
|
||||
process.env.ZAI_CODING_API_KEY = previous.coding;
|
||||
}
|
||||
if (previous.zai === undefined) {
|
||||
delete process.env.ZAI_API_KEY;
|
||||
} else {
|
||||
process.env.ZAI_API_KEY = previous.zai;
|
||||
}
|
||||
if (previous.legacy === undefined) {
|
||||
delete process.env.Z_AI_API_KEY;
|
||||
} else {
|
||||
process.env.Z_AI_API_KEY = previous.legacy;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("throws when zhipu API key is missing", async () => {
|
||||
const previous = process.env.ZHIPU_API_KEY;
|
||||
|
||||
try {
|
||||
delete process.env.ZHIPU_API_KEY;
|
||||
|
||||
vi.resetModules();
|
||||
const { resolveApiKeyForProvider } = await import("./model-auth.js");
|
||||
|
||||
let error: unknown = null;
|
||||
try {
|
||||
await resolveApiKeyForProvider({
|
||||
provider: "zhipu",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
expect(String(error)).toContain('No API key found for provider "zhipu".');
|
||||
} finally {
|
||||
if (previous === undefined) {
|
||||
delete process.env.ZHIPU_API_KEY;
|
||||
} else {
|
||||
process.env.ZHIPU_API_KEY = previous;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves zhipu API key from ZHIPU_API_KEY", async () => {
|
||||
const previous = process.env.ZHIPU_API_KEY;
|
||||
|
||||
try {
|
||||
process.env.ZHIPU_API_KEY = "zhipu-test-key";
|
||||
|
||||
vi.resetModules();
|
||||
const { resolveApiKeyForProvider } = await import("./model-auth.js");
|
||||
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "zhipu",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
expect(resolved.apiKey).toBe("zhipu-test-key");
|
||||
expect(resolved.source).toContain("ZHIPU_API_KEY");
|
||||
} finally {
|
||||
if (previous === undefined) {
|
||||
delete process.env.ZHIPU_API_KEY;
|
||||
} else {
|
||||
process.env.ZHIPU_API_KEY = previous;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves zhipu-coding API key with fallback to ZHIPU_API_KEY", async () => {
|
||||
const previous = {
|
||||
coding: process.env.ZHIPU_CODING_API_KEY,
|
||||
zhipu: process.env.ZHIPU_API_KEY,
|
||||
};
|
||||
|
||||
try {
|
||||
delete process.env.ZHIPU_CODING_API_KEY;
|
||||
process.env.ZHIPU_API_KEY = "zhipu-fallback-key";
|
||||
|
||||
vi.resetModules();
|
||||
const { resolveApiKeyForProvider } = await import("./model-auth.js");
|
||||
|
||||
const resolved = await resolveApiKeyForProvider({
|
||||
provider: "zhipu-coding",
|
||||
store: { version: 1, profiles: {} },
|
||||
});
|
||||
expect(resolved.apiKey).toBe("zhipu-fallback-key");
|
||||
expect(resolved.source).toContain("ZHIPU_API_KEY");
|
||||
} finally {
|
||||
if (previous.coding === undefined) {
|
||||
delete process.env.ZHIPU_CODING_API_KEY;
|
||||
} else {
|
||||
process.env.ZHIPU_CODING_API_KEY = previous.coding;
|
||||
}
|
||||
if (previous.zhipu === undefined) {
|
||||
delete process.env.ZHIPU_API_KEY;
|
||||
} else {
|
||||
process.env.ZHIPU_API_KEY = previous.zhipu;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves Synthetic API key from env", async () => {
|
||||
const previousSynthetic = process.env.SYNTHETIC_API_KEY;
|
||||
|
||||
|
||||
@ -182,6 +182,24 @@ describe("cli program (smoke)", () => {
|
||||
key: "sk-zai-test",
|
||||
field: "zaiApiKey",
|
||||
},
|
||||
{
|
||||
authChoice: "zai-coding-api-key",
|
||||
flag: "--zai-coding-api-key",
|
||||
key: "sk-zai-coding-test",
|
||||
field: "zaiCodingApiKey",
|
||||
},
|
||||
{
|
||||
authChoice: "zhipu-api-key",
|
||||
flag: "--zhipu-api-key",
|
||||
key: "sk-zhipu-test",
|
||||
field: "zhipuApiKey",
|
||||
},
|
||||
{
|
||||
authChoice: "zhipu-coding-api-key",
|
||||
flag: "--zhipu-coding-api-key",
|
||||
key: "sk-zhipu-coding-test",
|
||||
field: "zhipuCodingApiKey",
|
||||
},
|
||||
] as const;
|
||||
|
||||
for (const entry of cases) {
|
||||
|
||||
@ -23,14 +23,19 @@ describe("buildAuthChoiceOptions", () => {
|
||||
expect(options.some((opt) => opt.value === "token")).toBe(true);
|
||||
});
|
||||
|
||||
it("includes Z.AI (GLM) auth choice", () => {
|
||||
it("includes Z.AI / Zhipu (GLM) auth choices", () => {
|
||||
const store: AuthProfileStore = { version: 1, profiles: {} };
|
||||
const options = buildAuthChoiceOptions({
|
||||
store,
|
||||
includeSkip: false,
|
||||
});
|
||||
|
||||
// International variants (api.z.ai)
|
||||
expect(options.some((opt) => opt.value === "zai-api-key")).toBe(true);
|
||||
expect(options.some((opt) => opt.value === "zai-coding-api-key")).toBe(true);
|
||||
// China variants (bigmodel.cn)
|
||||
expect(options.some((opt) => opt.value === "zhipu-api-key")).toBe(true);
|
||||
expect(options.some((opt) => opt.value === "zhipu-coding-api-key")).toBe(true);
|
||||
});
|
||||
|
||||
it("includes MiniMax auth choice", () => {
|
||||
|
||||
@ -137,6 +137,49 @@ describe("provider usage loading", () => {
|
||||
expect(mockFetch).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// TODO: Implement fetchZhipuUsage in provider-usage.load.ts
|
||||
it("loads zhipu usage from bigmodel.cn endpoint", async () => {
|
||||
const makeResponse = (status: number, body: unknown): Response => {
|
||||
const payload = typeof body === "string" ? body : JSON.stringify(body);
|
||||
const headers = typeof body === "string" ? undefined : { "Content-Type": "application/json" };
|
||||
return new Response(payload, { status, headers });
|
||||
};
|
||||
|
||||
const mockFetch = vi.fn<Parameters<typeof fetch>, ReturnType<typeof fetch>>(async (input) => {
|
||||
const url =
|
||||
typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
||||
if (url.includes("open.bigmodel.cn")) {
|
||||
return makeResponse(200, {
|
||||
success: true,
|
||||
code: 200,
|
||||
data: {
|
||||
planName: "Basic",
|
||||
limits: [
|
||||
{
|
||||
type: "TOKENS_LIMIT",
|
||||
percentage: 30,
|
||||
unit: 3,
|
||||
number: 6,
|
||||
nextResetTime: "2026-01-07T06:00:00Z",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
return makeResponse(404, "not found");
|
||||
});
|
||||
|
||||
const summary = await loadProviderUsageSummary({
|
||||
now: Date.UTC(2026, 0, 7, 0, 0, 0),
|
||||
auth: [{ provider: "zhipu", token: "token-1" }],
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
||||
const zhipu = summary.providers.find((p) => p.provider === "zhipu");
|
||||
expect(zhipu?.plan).toBe("Basic");
|
||||
expect(zhipu?.windows[0]?.usedPercent).toBe(30);
|
||||
});
|
||||
|
||||
it("handles nested MiniMax usage payloads", async () => {
|
||||
const makeResponse = (status: number, body: unknown): Response => {
|
||||
const payload = typeof body === "string" ? body : JSON.stringify(body);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user