diff --git a/src/commands/auth-choice-options.test.ts b/src/commands/auth-choice-options.test.ts index 5e99e111bf8..c0c719a70ee 100644 --- a/src/commands/auth-choice-options.test.ts +++ b/src/commands/auth-choice-options.test.ts @@ -16,41 +16,32 @@ function getOptions(includeSkip = false) { } describe("buildAuthChoiceOptions", () => { - it("includes GitHub Copilot", () => { + it("includes core and provider-specific auth choices", () => { const options = getOptions(); - expect(options.find((opt) => opt.value === "github-copilot")).toBeDefined(); - }); - - it("includes setup-token option for Anthropic", () => { - const options = getOptions(); - - expect(options.some((opt) => opt.value === "token")).toBe(true); - }); - - it.each([ - ["Z.AI (GLM) auth choice", ["zai-api-key"]], - ["Xiaomi auth choice", ["xiaomi-api-key"]], - ["MiniMax auth choice", ["minimax-api", "minimax-api-key-cn", "minimax-api-lightning"]], - [ - "Moonshot auth choice", - ["moonshot-api-key", "moonshot-api-key-cn", "kimi-code-api-key", "together-api-key"], - ], - ["Vercel AI Gateway auth choice", ["ai-gateway-api-key"]], - ["Cloudflare AI Gateway auth choice", ["cloudflare-ai-gateway-api-key"]], - ["Together AI auth choice", ["together-api-key"]], - ["Synthetic auth choice", ["synthetic-api-key"]], - ["Chutes OAuth auth choice", ["chutes"]], - ["Qwen auth choice", ["qwen-portal"]], - ["xAI auth choice", ["xai-api-key"]], - ["Mistral auth choice", ["mistral-api-key"]], - ["Volcano Engine auth choice", ["volcengine-api-key"]], - ["BytePlus auth choice", ["byteplus-api-key"]], - ["vLLM auth choice", ["vllm"]], - ])("includes %s", (_label, expectedValues) => { - const options = getOptions(); - - for (const value of expectedValues) { + for (const value of [ + "github-copilot", + "token", + "zai-api-key", + "xiaomi-api-key", + "minimax-api", + "minimax-api-key-cn", + "minimax-api-lightning", + "moonshot-api-key", + "moonshot-api-key-cn", + "kimi-code-api-key", + "together-api-key", + "ai-gateway-api-key", + "cloudflare-ai-gateway-api-key", + "synthetic-api-key", + "chutes", + "qwen-portal", + "xai-api-key", + "mistral-api-key", + "volcengine-api-key", + "byteplus-api-key", + "vllm", + ]) { expect(options.some((opt) => opt.value === value)).toBe(true); } }); diff --git a/src/commands/models.auth.provider-resolution.test.ts b/src/commands/models.auth.provider-resolution.test.ts index f03a99bb4cb..19302e2ae1e 100644 --- a/src/commands/models.auth.provider-resolution.test.ts +++ b/src/commands/models.auth.provider-resolution.test.ts @@ -12,35 +12,31 @@ function makeProvider(params: { id: string; label?: string; aliases?: string[] } } describe("resolveRequestedLoginProviderOrThrow", () => { - it("returns null when no provider was requested", () => { - const providers = [makeProvider({ id: "google-gemini-cli" })]; - const result = resolveRequestedLoginProviderOrThrow(providers, undefined); - expect(result).toBeNull(); - }); - - it("resolves requested provider by id", () => { + it("returns null and resolves provider by id/alias", () => { const providers = [ - makeProvider({ id: "google-gemini-cli" }), + makeProvider({ id: "google-gemini-cli", aliases: ["gemini-cli"] }), makeProvider({ id: "qwen-portal" }), ]; - const result = resolveRequestedLoginProviderOrThrow(providers, "google-gemini-cli"); - expect(result?.id).toBe("google-gemini-cli"); - }); + const scenarios = [ + { requested: undefined, expectedId: null }, + { requested: "google-gemini-cli", expectedId: "google-gemini-cli" }, + { requested: "gemini-cli", expectedId: "google-gemini-cli" }, + ] as const; - it("resolves requested provider by alias", () => { - const providers = [makeProvider({ id: "google-gemini-cli", aliases: ["gemini-cli"] })]; - const result = resolveRequestedLoginProviderOrThrow(providers, "gemini-cli"); - expect(result?.id).toBe("google-gemini-cli"); + for (const scenario of scenarios) { + const result = resolveRequestedLoginProviderOrThrow(providers, scenario.requested); + expect(result?.id ?? null).toBe(scenario.expectedId); + } }); it("throws when requested provider is not loaded", () => { - const providers = [ + const loadedProviders = [ makeProvider({ id: "google-gemini-cli" }), makeProvider({ id: "qwen-portal" }), ]; expect(() => - resolveRequestedLoginProviderOrThrow(providers, "google-antigravity"), + resolveRequestedLoginProviderOrThrow(loadedProviders, "google-antigravity"), ).toThrowError( 'Unknown provider "google-antigravity". Loaded providers: google-gemini-cli, qwen-portal. Verify plugins via `openclaw plugins list --json`.', ); diff --git a/src/commands/zai-endpoint-detect.test.ts b/src/commands/zai-endpoint-detect.test.ts index f1a16eaaaaa..ce2d45fc044 100644 --- a/src/commands/zai-endpoint-detect.test.ts +++ b/src/commands/zai-endpoint-detect.test.ts @@ -16,51 +16,58 @@ function makeFetch(map: Record) { } describe("detectZaiEndpoint", () => { - it("prefers global glm-5 when it works", async () => { - const fetchFn = makeFetch({ - "https://api.z.ai/api/paas/v4/chat/completions": { status: 200 }, - }); - - const detected = await detectZaiEndpoint({ apiKey: "sk-test", fetchFn }); - expect(detected?.endpoint).toBe("global"); - expect(detected?.modelId).toBe("glm-5"); - }); - - it("falls back to cn glm-5 when global fails", async () => { - const fetchFn = makeFetch({ - "https://api.z.ai/api/paas/v4/chat/completions": { - status: 404, - body: { error: { message: "not found" } }, + it("resolves preferred/fallback endpoints and null when probes fail", async () => { + const scenarios: Array<{ + responses: Record; + expected: { endpoint: string; modelId: string } | null; + }> = [ + { + responses: { + "https://api.z.ai/api/paas/v4/chat/completions": { status: 200 }, + }, + expected: { endpoint: "global", modelId: "glm-5" }, }, - "https://open.bigmodel.cn/api/paas/v4/chat/completions": { status: 200 }, - }); + { + responses: { + "https://api.z.ai/api/paas/v4/chat/completions": { + status: 404, + body: { error: { message: "not found" } }, + }, + "https://open.bigmodel.cn/api/paas/v4/chat/completions": { status: 200 }, + }, + expected: { endpoint: "cn", modelId: "glm-5" }, + }, + { + responses: { + "https://api.z.ai/api/paas/v4/chat/completions": { status: 404 }, + "https://open.bigmodel.cn/api/paas/v4/chat/completions": { status: 404 }, + "https://api.z.ai/api/coding/paas/v4/chat/completions": { status: 200 }, + }, + expected: { endpoint: "coding-global", modelId: "glm-4.7" }, + }, + { + responses: { + "https://api.z.ai/api/paas/v4/chat/completions": { status: 401 }, + "https://open.bigmodel.cn/api/paas/v4/chat/completions": { status: 401 }, + "https://api.z.ai/api/coding/paas/v4/chat/completions": { status: 401 }, + "https://open.bigmodel.cn/api/coding/paas/v4/chat/completions": { status: 401 }, + }, + expected: null, + }, + ]; - const detected = await detectZaiEndpoint({ apiKey: "sk-test", fetchFn }); - expect(detected?.endpoint).toBe("cn"); - expect(detected?.modelId).toBe("glm-5"); - }); + for (const scenario of scenarios) { + const detected = await detectZaiEndpoint({ + apiKey: "sk-test", + fetchFn: makeFetch(scenario.responses), + }); - it("falls back to coding endpoint with glm-4.7", async () => { - const fetchFn = makeFetch({ - "https://api.z.ai/api/paas/v4/chat/completions": { status: 404 }, - "https://open.bigmodel.cn/api/paas/v4/chat/completions": { status: 404 }, - "https://api.z.ai/api/coding/paas/v4/chat/completions": { status: 200 }, - }); - - const detected = await detectZaiEndpoint({ apiKey: "sk-test", fetchFn }); - expect(detected?.endpoint).toBe("coding-global"); - expect(detected?.modelId).toBe("glm-4.7"); - }); - - it("returns null when nothing works", async () => { - const fetchFn = makeFetch({ - "https://api.z.ai/api/paas/v4/chat/completions": { status: 401 }, - "https://open.bigmodel.cn/api/paas/v4/chat/completions": { status: 401 }, - "https://api.z.ai/api/coding/paas/v4/chat/completions": { status: 401 }, - "https://open.bigmodel.cn/api/coding/paas/v4/chat/completions": { status: 401 }, - }); - - const detected = await detectZaiEndpoint({ apiKey: "sk-test", fetchFn }); - expect(detected).toBe(null); + if (scenario.expected === null) { + expect(detected).toBeNull(); + } else { + expect(detected?.endpoint).toBe(scenario.expected.endpoint); + expect(detected?.modelId).toBe(scenario.expected.modelId); + } + } }); });