diff --git a/src/commands/models.list.e2e.test.ts b/src/commands/models.list.e2e.test.ts index f4562c60088..441c0675a79 100644 --- a/src/commands/models.list.e2e.test.ts +++ b/src/commands/models.list.e2e.test.ts @@ -27,6 +27,7 @@ const modelRegistryState = { available: [] as Array>, getAllError: undefined as unknown, getAvailableError: undefined as unknown, + findError: undefined as unknown, }; let previousExitCode: typeof process.exitCode; @@ -46,6 +47,9 @@ vi.mock("./models/load-config.js", () => ({ vi.mock("./models/list.runtime.js", () => { class MockModelRegistry { find(provider: string, id: string) { + if (modelRegistryState.findError !== undefined) { + throw modelRegistryState.findError; + } return ( modelRegistryState.models.find((model) => model.provider === provider && model.id === id) ?? null @@ -65,6 +69,12 @@ vi.mock("./models/list.runtime.js", () => { } return modelRegistryState.available; } + + hasConfiguredAuth(model: { provider: string; id: string }) { + return modelRegistryState.available.some( + (available) => available.provider === model.provider && available.id === model.id, + ); + } } return { @@ -133,6 +143,7 @@ beforeEach(() => { process.exitCode = undefined; modelRegistryState.getAllError = undefined; modelRegistryState.getAvailableError = undefined; + modelRegistryState.findError = undefined; getRuntimeConfig.mockReset(); getRuntimeConfig.mockReturnValue({}); listProfilesForProvider.mockReturnValue([]); @@ -390,22 +401,27 @@ describe("models list/status", () => { expect(loadProviderCatalogModelsForList).not.toHaveBeenCalled(); }); - it("models list does not treat availability-unavailable code as discovery fallback", async () => { + it("models list default does not enumerate all registry models", async () => { configureGoogleAntigravityModel("claude-opus-4-6-thinking"); + modelRegistryState.models = [ + makeGoogleAntigravityTemplate("claude-opus-4-6-thinking", "Claude Opus 4.6 Thinking"), + ]; + modelRegistryState.available = modelRegistryState.models; modelRegistryState.getAllError = Object.assign(new Error("model discovery failed"), { code: "MODEL_AVAILABILITY_UNAVAILABLE", }); const runtime = makeRuntime(); await modelsListCommand({ json: true }, runtime); - expectModelRegistryUnavailable(runtime, "model discovery failed"); - expect(runtime.error.mock.calls[0]?.[0]).not.toContain("configured models may appear missing"); + expect(runtime.error).not.toHaveBeenCalled(); + const payload = parseJsonLog(runtime); + expect(payload.models[0]?.key).toBe("google-antigravity/claude-opus-4-6-thinking"); }); - it("models list fails fast when registry model discovery is unavailable", async () => { + it("models list fails fast when configured registry lookup is unavailable", async () => { configureGoogleAntigravityModel("claude-opus-4-6-thinking"); enableGoogleAntigravityAuthProfile(); - modelRegistryState.getAllError = Object.assign(new Error("model discovery unavailable"), { + modelRegistryState.findError = Object.assign(new Error("model discovery unavailable"), { code: "MODEL_DISCOVERY_UNAVAILABLE", }); const runtime = makeRuntime(); diff --git a/src/commands/models/list.list-command.forward-compat.test.ts b/src/commands/models/list.list-command.forward-compat.test.ts index 9f43ba24d1a..73cb279c225 100644 --- a/src/commands/models/list.list-command.forward-compat.test.ts +++ b/src/commands/models/list.list-command.forward-compat.test.ts @@ -141,6 +141,43 @@ function installModelsListCommandForwardCompatMocks() { printModelTable: mocks.printModelTable, })); + vi.doMock("./list.registry-load.js", () => ({ + loadListModelRegistry: async ( + cfg: unknown, + opts?: { providerFilter?: string }, + ): Promise<{ + models: Array<{ provider: string; id: string }>; + availableKeys?: Set; + registry?: unknown; + discoveredKeys: Set; + }> => { + const loaded = await mocks.loadModelRegistry(cfg, opts); + return { + ...loaded, + discoveredKeys: new Set( + loaded.models.map( + (model: { provider: string; id: string }) => `${model.provider}/${model.id}`, + ), + ), + }; + }, + loadConfiguredListModelRegistry: ( + _cfg: unknown, + _entries: unknown, + opts?: { providerFilter?: string }, + ) => { + mocks.loadModelRegistry(mocks.resolvedConfig, opts); + return { + registry: { + find: () => undefined, + hasConfiguredAuth: () => false, + }, + discoveredKeys: new Set(), + availableKeys: new Set(), + }; + }, + })); + vi.doMock("./list.runtime.js", () => ({ ensureOpenClawModelsJson: mocks.ensureOpenClawModelsJson, ensureAuthProfileStore: mocks.ensureAuthProfileStore, @@ -363,7 +400,7 @@ describe("modelsListCommand forward-compat", () => { ); }); - it("exits with an error when configured-mode listing has no model registry", async () => { + it("does not require the all-model registry result for configured-mode listing", async () => { const previousExitCode = process.exitCode; process.exitCode = undefined; mocks.loadModelRegistry.mockResolvedValueOnce({ @@ -381,9 +418,9 @@ describe("modelsListCommand forward-compat", () => { process.exitCode = previousExitCode; } - expect(runtime.error).toHaveBeenCalledWith("Model registry unavailable."); - expect(observedExitCode).toBe(1); - expect(mocks.printModelTable).not.toHaveBeenCalled(); + expect(runtime.error).not.toHaveBeenCalled(); + expect(observedExitCode).toBeUndefined(); + expect(mocks.printModelTable).toHaveBeenCalled(); }); });