test(extensions): repair remaining response fixtures

This commit is contained in:
Dallin Romney
2026-07-10 01:32:23 -07:00
parent ffb7bed65b
commit cf4385e6f6
2 changed files with 85 additions and 120 deletions
+43 -61
View File
@@ -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<unknown>;
};
type MockKilocodeFetch = ((
url: string,
init?: RequestInit,
) => Promise<MockKilocodeFetchResponse>) & {
type MockKilocodeFetch = ((url: string, init?: RequestInit) => Promise<Response>) & {
mock: { calls: unknown[][] };
};
@@ -115,6 +106,14 @@ function makeAutoModel(overrides: Record<string, unknown> = {}) {
});
}
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<void>) {
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");
+42 -59
View File
@@ -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(