From 4b151593e20e329592c054f648320b463ff87571 Mon Sep 17 00:00:00 2001 From: Shakker Date: Fri, 5 Jun 2026 02:18:51 +0100 Subject: [PATCH] test: scope model scan key env --- src/commands/models/scan.test.ts | 97 +++++++++++++++++--------------- 1 file changed, 51 insertions(+), 46 deletions(-) diff --git a/src/commands/models/scan.test.ts b/src/commands/models/scan.test.ts index 22ed61fad477..d9431d2f3a66 100644 --- a/src/commands/models/scan.test.ts +++ b/src/commands/models/scan.test.ts @@ -1,7 +1,8 @@ // Model scan tests cover provider scan behavior and discovered model output. -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import type { ModelScanResult } from "../../agents/model-scan.js"; import type { RuntimeEnv } from "../../runtime.js"; +import { withEnvAsync } from "../../test-utils/env.js"; const mocks = vi.hoisted(() => ({ loadModelsConfig: vi.fn(), @@ -64,15 +65,15 @@ function firstScanRequest(): { apiKey?: string; probe?: boolean } { return call[0] as { apiKey?: string; probe?: boolean }; } +function withOpenRouterApiKey(apiKey: string | undefined, fn: () => Promise): Promise { + return withEnvAsync({ OPENROUTER_API_KEY: apiKey }, fn); +} + describe("models scan command", () => { beforeEach(() => { vi.clearAllMocks(); }); - afterEach(() => { - vi.unstubAllEnvs(); - }); - it("does not load config or resolve secrets for metadata-only scans", async () => { const runtime = createRuntime(); mocks.scanOpenRouterModels.mockResolvedValue([scanResult()]); @@ -89,52 +90,55 @@ describe("models scan command", () => { }); it("downgrades to metadata-only scan when no OpenRouter key is configured", async () => { - const runtime = createRuntime(); - vi.stubEnv("OPENROUTER_API_KEY", undefined); - mocks.loadModelsConfig.mockResolvedValue({}); - mocks.resolveApiKeyForProvider.mockResolvedValue({ apiKey: "" }); - mocks.scanOpenRouterModels.mockResolvedValue([scanResult()]); + await withOpenRouterApiKey(undefined, async () => { + const runtime = createRuntime(); + mocks.loadModelsConfig.mockResolvedValue({}); + mocks.resolveApiKeyForProvider.mockResolvedValue({ apiKey: "" }); + mocks.scanOpenRouterModels.mockResolvedValue([scanResult()]); - await modelsScanCommand({}, runtime); + await modelsScanCommand({}, runtime); - expect(mocks.loadModelsConfig).toHaveBeenCalledTimes(1); - expect(mocks.resolveApiKeyForProvider).toHaveBeenCalledWith({ - provider: "openrouter", - cfg: {}, + expect(mocks.loadModelsConfig).toHaveBeenCalledTimes(1); + expect(mocks.resolveApiKeyForProvider).toHaveBeenCalledWith({ + provider: "openrouter", + cfg: {}, + }); + expect(mocks.scanOpenRouterModels).toHaveBeenCalledTimes(1); + expect(firstScanRequest().probe).toBe(false); + expect(runtime.lines.join("\n")).toContain("still require OPENROUTER_API_KEY"); }); - expect(mocks.scanOpenRouterModels).toHaveBeenCalledTimes(1); - expect(firstScanRequest().probe).toBe(false); - expect(runtime.lines.join("\n")).toContain("still require OPENROUTER_API_KEY"); }); it("uses OPENROUTER_API_KEY directly without loading model config", async () => { - const runtime = createRuntime(); - vi.stubEnv("OPENROUTER_API_KEY", "sk-or-test"); - mocks.scanOpenRouterModels.mockResolvedValue([ - scanResult({ tool: { ok: false, latencyMs: null, skipped: false } }), - ]); + await withOpenRouterApiKey("sk-or-test", async () => { + const runtime = createRuntime(); + mocks.scanOpenRouterModels.mockResolvedValue([ + scanResult({ tool: { ok: false, latencyMs: null, skipped: false } }), + ]); - await expect(modelsScanCommand({ json: true }, runtime)).rejects.toThrow( - /No tool-capable OpenRouter free models found/, - ); + await expect(modelsScanCommand({ json: true }, runtime)).rejects.toThrow( + /No tool-capable OpenRouter free models found/, + ); - expect(mocks.loadModelsConfig).not.toHaveBeenCalled(); - expect(mocks.resolveApiKeyForProvider).not.toHaveBeenCalled(); - expect(mocks.scanOpenRouterModels).toHaveBeenCalledTimes(1); - const scanRequest = firstScanRequest(); - expect(scanRequest?.apiKey).toBe("sk-or-test"); - expect(scanRequest?.probe).toBe(true); + expect(mocks.loadModelsConfig).not.toHaveBeenCalled(); + expect(mocks.resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(mocks.scanOpenRouterModels).toHaveBeenCalledTimes(1); + const scanRequest = firstScanRequest(); + expect(scanRequest?.apiKey).toBe("sk-or-test"); + expect(scanRequest?.probe).toBe(true); + }); }); it("rejects applying metadata-only scan results", async () => { - const runtime = createRuntime(); - vi.stubEnv("OPENROUTER_API_KEY", undefined); + await withOpenRouterApiKey(undefined, async () => { + const runtime = createRuntime(); - await expect(modelsScanCommand({ probe: false, setDefault: true }, runtime)).rejects.toThrow( - /Cannot apply metadata-only OpenRouter scan results/, - ); + await expect(modelsScanCommand({ probe: false, setDefault: true }, runtime)).rejects.toThrow( + /Cannot apply metadata-only OpenRouter scan results/, + ); - expect(mocks.scanOpenRouterModels).not.toHaveBeenCalled(); + expect(mocks.scanOpenRouterModels).not.toHaveBeenCalled(); + }); }); it.each([ @@ -152,15 +156,16 @@ describe("models scan command", () => { }); it("rejects applying auto-downgraded metadata-only scan results before scanning", async () => { - const runtime = createRuntime(); - vi.stubEnv("OPENROUTER_API_KEY", undefined); - mocks.loadModelsConfig.mockResolvedValue({}); - mocks.resolveApiKeyForProvider.mockResolvedValue({ apiKey: "" }); + await withOpenRouterApiKey(undefined, async () => { + const runtime = createRuntime(); + mocks.loadModelsConfig.mockResolvedValue({}); + mocks.resolveApiKeyForProvider.mockResolvedValue({ apiKey: "" }); - await expect(modelsScanCommand({ setDefault: true }, runtime)).rejects.toThrow( - /Cannot apply metadata-only OpenRouter scan results/, - ); + await expect(modelsScanCommand({ setDefault: true }, runtime)).rejects.toThrow( + /Cannot apply metadata-only OpenRouter scan results/, + ); - expect(mocks.scanOpenRouterModels).not.toHaveBeenCalled(); + expect(mocks.scanOpenRouterModels).not.toHaveBeenCalled(); + }); }); });