From cf4385e6f6aa265f40bf93a33af0be708bead74d Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 10 Jul 2026 01:32:23 -0700 Subject: [PATCH] test(extensions): repair remaining response fixtures --- extensions/kilocode/provider-models.test.ts | 104 ++++++++------------ extensions/lmstudio/src/models.test.ts | 101 ++++++++----------- 2 files changed, 85 insertions(+), 120 deletions(-) diff --git a/extensions/kilocode/provider-models.test.ts b/extensions/kilocode/provider-models.test.ts index dcb77a16ca2e..fef63e1b917c 100644 --- a/extensions/kilocode/provider-models.test.ts +++ b/extensions/kilocode/provider-models.test.ts @@ -14,16 +14,7 @@ vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ({ import { discoverKilocodeModels, KILOCODE_MODELS_URL } from "./provider-models.js"; -type MockKilocodeFetchResponse = { - ok: boolean; - status?: number; - json?: () => Promise; -}; - -type MockKilocodeFetch = (( - url: string, - init?: RequestInit, -) => Promise) & { +type MockKilocodeFetch = ((url: string, init?: RequestInit) => Promise) & { mock: { calls: unknown[][] }; }; @@ -115,6 +106,14 @@ function makeAutoModel(overrides: Record = {}) { }); } +function jsonResponse(payload: unknown, init: ResponseInit = {}): Response { + return new Response(JSON.stringify(payload), { + status: 200, + headers: { "Content-Type": "application/json" }, + ...init, + }); +} + async function withFetchPathTest(mockFetch: MockKilocodeFetch, runAssertions: () => Promise) { const release = vi.fn(async () => {}); vi.stubEnv("NODE_ENV", ""); @@ -165,13 +164,11 @@ describe("discoverKilocodeModels", () => { describe("discoverKilocodeModels (fetch path)", () => { it("parses gateway models with correct pricing conversion", async () => { - const mockFetch = vi.fn().mockResolvedValue({ - ok: true, - json: () => - Promise.resolve({ - data: [makeAutoModel(), makeGatewayModel()], - }), - }); + const mockFetch = vi.fn().mockResolvedValue( + jsonResponse({ + data: [makeAutoModel(), makeGatewayModel()], + }), + ); await withFetchPathTest(mockFetch, async () => { const models = await discoverKilocodeModels(); @@ -217,10 +214,7 @@ describe("discoverKilocodeModels (fetch path)", () => { }); it("falls back to static catalog on HTTP error", async () => { - const mockFetch = vi.fn().mockResolvedValue({ - ok: false, - status: 500, - }); + const mockFetch = vi.fn().mockResolvedValue(new Response("", { status: 500 })); await withFetchPathTest(mockFetch, async () => { const models = await discoverKilocodeModels(); expect(models).toStrictEqual(EXPECTED_STATIC_KILOCODE_MODELS); @@ -229,10 +223,7 @@ describe("discoverKilocodeModels (fetch path)", () => { it("falls back to static catalog for malformed successful model list payloads", async () => { for (const payload of [[], { data: {} }, { data: [null] }]) { - const mockFetch = vi.fn().mockResolvedValue({ - ok: true, - json: () => Promise.resolve(payload), - }); + const mockFetch = vi.fn().mockResolvedValue(jsonResponse(payload)); await withFetchPathTest(mockFetch, async () => { const models = await discoverKilocodeModels(); expect(models).toStrictEqual(EXPECTED_STATIC_KILOCODE_MODELS); @@ -241,24 +232,22 @@ describe("discoverKilocodeModels (fetch path)", () => { }); it("falls back from malformed live token metadata", async () => { - const mockFetch = vi.fn().mockResolvedValue({ - ok: true, - json: () => - Promise.resolve({ - data: [ - makeGatewayModel({ - id: "some/bad-window", - context_length: -1, - top_provider: { max_completion_tokens: 8192.5 }, - }), - makeGatewayModel({ - id: "some/bad-output", - context_length: Number.POSITIVE_INFINITY, - top_provider: { max_completion_tokens: 0 }, - }), - ], - }), - }); + const mockFetch = vi.fn().mockResolvedValue( + jsonResponse({ + data: [ + makeGatewayModel({ + id: "some/bad-window", + context_length: -1, + top_provider: { max_completion_tokens: 8192.5 }, + }), + makeGatewayModel({ + id: "some/bad-output", + context_length: Number.POSITIVE_INFINITY, + top_provider: { max_completion_tokens: 0 }, + }), + ], + }), + ); await withFetchPathTest(mockFetch, async () => { const models = await discoverKilocodeModels(); @@ -275,13 +264,11 @@ describe("discoverKilocodeModels (fetch path)", () => { }); it("ensures kilo/auto is present even when API doesn't return it", async () => { - const mockFetch = vi.fn().mockResolvedValue({ - ok: true, - json: () => - Promise.resolve({ - data: [makeGatewayModel()], - }), - }); + const mockFetch = vi.fn().mockResolvedValue( + jsonResponse({ + data: [makeGatewayModel()], + }), + ); await withFetchPathTest(mockFetch, async () => { const models = await discoverKilocodeModels(); expect(requireModelById(models, "kilo/auto").id).toBe("kilo/auto"); @@ -301,10 +288,7 @@ describe("discoverKilocodeModels (fetch path)", () => { supported_parameters: ["max_tokens", "temperature"], }); - const mockFetch = vi.fn().mockResolvedValue({ - ok: true, - json: () => Promise.resolve({ data: [textOnlyModel] }), - }); + const mockFetch = vi.fn().mockResolvedValue(jsonResponse({ data: [textOnlyModel] })); await withFetchPathTest(mockFetch, async () => { const models = await discoverKilocodeModels(); const textModel = requireModelById(models, "some/text-model"); @@ -319,13 +303,11 @@ describe("discoverKilocodeModels (fetch path)", () => { pricing: undefined, }); - const mockFetch = vi.fn().mockResolvedValue({ - ok: true, - json: () => - Promise.resolve({ - data: [malformedAutoModel, makeAutoModel(), makeGatewayModel()], - }), - }); + const mockFetch = vi.fn().mockResolvedValue( + jsonResponse({ + data: [malformedAutoModel, makeAutoModel(), makeGatewayModel()], + }), + ); await withFetchPathTest(mockFetch, async () => { const models = await discoverKilocodeModels(); const auto = requireModelById(models, "kilo/auto"); diff --git a/extensions/lmstudio/src/models.test.ts b/extensions/lmstudio/src/models.test.ts index 5de8dfd3b6ca..79f6bb673cab 100644 --- a/extensions/lmstudio/src/models.test.ts +++ b/extensions/lmstudio/src/models.test.ts @@ -31,6 +31,21 @@ vi.mock("openclaw/plugin-sdk/ssrf-runtime", async (importOriginal) => { }; }); +function jsonResponse(payload: unknown, init?: ResponseInit): Response { + return new Response(JSON.stringify(payload), { + status: 200, + headers: { "content-type": "application/json" }, + ...init, + }); +} + +function malformedJsonResponse(): Response { + return new Response("{ nope", { + status: 200, + headers: { "content-type": "application/json" }, + }); +} + afterAll(() => { vi.doUnmock("openclaw/plugin-sdk/ssrf-runtime"); vi.resetModules(); @@ -48,30 +63,23 @@ describe("lmstudio-models", () => { loadedContextLength?: number; maxContextLength?: number; }) => - vi.fn(async (url: string | URL, init?: RequestInit) => { + vi.fn(async (url: string | URL, _init?: RequestInit) => { if (String(url).endsWith("/api/v1/models")) { - return { - ok: true, - json: async () => ({ - models: [ - { - type: "llm", - key: "qwen3-8b-instruct", - max_context_length: params?.maxContextLength, - loaded_instances: params?.loadedContextLength - ? [{ id: "inst-1", config: { context_length: params.loadedContextLength } }] - : [], - }, - ], - }), - }; + return jsonResponse({ + models: [ + { + type: "llm", + key: "qwen3-8b-instruct", + max_context_length: params?.maxContextLength, + loaded_instances: params?.loadedContextLength + ? [{ id: "inst-1", config: { context_length: params.loadedContextLength } }] + : [], + }, + ], + }); } if (String(url).endsWith("/api/v1/models/load")) { - return { - ok: true, - json: async () => ({ status: "loaded" }), - requestInit: init, - }; + return jsonResponse({ status: "loaded" }); } throw new Error(`Unexpected fetch URL: ${String(url)}`); }); @@ -257,9 +265,8 @@ describe("lmstudio-models", () => { }); it("discovers llm models and maps metadata", async () => { - const fetchMock = vi.fn(async (_url: string | URL, _init?: RequestInit) => ({ - ok: true, - json: async () => ({ + const fetchMock = vi.fn(async (_url: string | URL, _init?: RequestInit) => + jsonResponse({ models: [ { type: "llm", @@ -291,7 +298,7 @@ describe("lmstudio-models", () => { }, ], }), - })); + ); const models = await discoverLmstudioModels({ baseUrl: "http://localhost:1234/v1", @@ -347,13 +354,7 @@ describe("lmstudio-models", () => { }); it("reports malformed model list JSON with an owned error", async () => { - const fetchMock = vi.fn(async () => ({ - ok: true, - status: 200, - json: async () => { - throw new SyntaxError("bad json"); - }, - })); + const fetchMock = vi.fn(async () => malformedJsonResponse()); const result = await fetchLmstudioModels({ baseUrl: "http://localhost:1234/v1", @@ -366,11 +367,7 @@ describe("lmstudio-models", () => { it("reports wrong-shaped model list payloads with owned errors", async () => { for (const payload of [[], { models: {} }, { models: [null] }]) { - const fetchMock = vi.fn(async () => ({ - ok: true, - status: 200, - json: async () => payload, - })); + const fetchMock = vi.fn(async () => jsonResponse(payload)); const result = await fetchLmstudioModels({ baseUrl: "http://localhost:1234/v1", @@ -385,12 +382,9 @@ describe("lmstudio-models", () => { it("caps oversized direct fetch timeouts before discovering models", async () => { const timeoutController = new AbortController(); const timeoutSpy = vi.spyOn(AbortSignal, "timeout").mockReturnValue(timeoutController.signal); - const fetchMock = vi.fn(async (_url: string | URL, init?: RequestInit) => ({ - ok: true, - status: 200, - requestInit: init, - json: async () => ({ models: [] }), - })); + const fetchMock = vi.fn(async (_url: string | URL, _init?: RequestInit) => + jsonResponse({ models: [] }), + ); const result = await fetchLmstudioModels({ baseUrl: "http://localhost:1234/v1", @@ -459,20 +453,12 @@ describe("lmstudio-models", () => { it("reports malformed model load JSON with an owned error", async () => { const fetchMock = vi.fn(async (url: string | URL) => { if (String(url).endsWith("/api/v1/models")) { - return { - ok: true, - json: async () => ({ - models: [{ type: "llm", key: "qwen3-8b-instruct", loaded_instances: [] }], - }), - }; + return jsonResponse({ + models: [{ type: "llm", key: "qwen3-8b-instruct", loaded_instances: [] }], + }); } if (String(url).endsWith("/api/v1/models/load")) { - return { - ok: true, - json: async () => { - throw new SyntaxError("bad json"); - }, - }; + return malformedJsonResponse(); } throw new Error(`Unexpected fetch URL: ${String(url)}`); }); @@ -578,10 +564,7 @@ describe("lmstudio-models", () => { }); it("throws when model discovery fails", async () => { - const fetchMock = vi.fn(async () => ({ - ok: false, - status: 401, - })); + const fetchMock = vi.fn(async () => new Response("", { status: 401 })); vi.stubGlobal("fetch", asFetch(fetchMock)); await expect(