mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
test(extensions): repair remaining response fixtures
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user