From dcc180f14ff04f36c55fe9316c12ef8320c33f0f Mon Sep 17 00:00:00 2001 From: Shakker Date: Fri, 24 Apr 2026 00:30:53 +0100 Subject: [PATCH] test: cover read-only models list --- src/commands/models.list.e2e.test.ts | 54 +++++++++++++++---- .../list.list-command.forward-compat.test.ts | 11 ++-- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/commands/models.list.e2e.test.ts b/src/commands/models.list.e2e.test.ts index 71bb10fab8c..d7abb2c4889 100644 --- a/src/commands/models.list.e2e.test.ts +++ b/src/commands/models.list.e2e.test.ts @@ -467,26 +467,62 @@ describe("models list/status", () => { expect(Array.from(loaded.availableKeys ?? [])).toEqual(["openai/gpt-4.1-mini"]); }); - it("modelsListCommand persists using the source snapshot config when provided", async () => { - modelRegistryState.models = [OPENAI_MODEL]; - modelRegistryState.available = [OPENAI_MODEL]; + it("modelsListCommand lists source snapshot provider models without persisting models.json", async () => { + modelRegistryState.models = []; + modelRegistryState.available = []; const sourceConfig = { - models: { providers: { openai: { apiKey: "$OPENAI_API_KEY" } } }, // pragma: allowlist secret + models: { + providers: { + "custom-proxy": { + baseUrl: "https://custom.example/v1", + models: [ + { + id: "custom-model", + name: "Custom Model", + input: ["text"], + contextWindow: 128000, + }, + ], + }, + }, + }, }; const resolvedConfig = { - models: { providers: { openai: { apiKey: "sk-resolved-runtime-value" } } }, // pragma: allowlist secret + models: { + providers: { + "custom-proxy": { + baseUrl: "https://custom.example/v1", + apiKey: "sk-resolved-runtime-value", // pragma: allowlist secret + models: [ + { + id: "custom-model", + name: "Custom Model", + input: ["text"], + contextWindow: 128000, + }, + ], + }, + }, + }, }; readConfigFileSnapshotForWrite.mockResolvedValue({ snapshot: { valid: true, resolved: resolvedConfig, sourceConfig }, writeOptions: {}, }); - setDefaultModel("openai/gpt-4.1-mini"); + getRuntimeConfig.mockReturnValue(resolvedConfig); const runtime = makeRuntime(); - await modelsListCommand({ all: true, json: true }, runtime); + await modelsListCommand({ all: true, provider: "custom-proxy", json: true }, runtime); - expect(ensureOpenClawModelsJson).toHaveBeenCalled(); - expect(ensureOpenClawModelsJson.mock.calls[0]?.[0]).toEqual(sourceConfig); + expect(ensureOpenClawModelsJson).not.toHaveBeenCalled(); + const payload = parseJsonLog(runtime); + expect(payload.models).toEqual([ + expect.objectContaining({ + key: "custom-proxy/custom-model", + name: "Custom Model", + missing: false, + }), + ]); }); it("toModelRow does not crash without cfg/authStore when availability is undefined", async () => { 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 a486477b3dd..9f43ba24d1a 100644 --- a/src/commands/models/list.list-command.forward-compat.test.ts +++ b/src/commands/models/list.list-command.forward-compat.test.ts @@ -295,14 +295,17 @@ describe("modelsListCommand forward-compat", () => { expect(codexPro?.tags).not.toContain("missing"); }); - it("passes source config to model registry loading for persistence safety", async () => { + it("loads model registry without source config persistence input", async () => { const runtime = createRuntime(); await modelsListCommand({ json: true }, runtime as never); - expect(mocks.loadModelRegistry).toHaveBeenCalledWith(mocks.resolvedConfig, { - sourceConfig: mocks.sourceConfig, - }); + expect(mocks.loadModelRegistry).toHaveBeenCalledWith( + mocks.resolvedConfig, + expect.not.objectContaining({ + sourceConfig: expect.anything(), + }), + ); }); it("keeps configured local openai gpt-5.4 entries visible in --local output", async () => {