mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 19:52:55 +00:00
refactor: share models list test helper
This commit is contained in:
@@ -2,6 +2,7 @@ import { describe, expect, it, vi } from "vitest";
|
||||
import { ErrorCodes } from "../../../packages/gateway-protocol/src/index.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { modelsHandlers } from "./models.js";
|
||||
import type { RespondFn } from "./types.js";
|
||||
|
||||
type Deferred<T> = {
|
||||
promise: Promise<T>;
|
||||
@@ -22,44 +23,58 @@ function createDeferred<T>(): Deferred<T> {
|
||||
return { promise, resolve, reject };
|
||||
}
|
||||
|
||||
function requestModelsList(params: {
|
||||
view: "configured" | "all";
|
||||
respond?: ReturnType<typeof vi.fn>;
|
||||
runtimeConfig?: OpenClawConfig;
|
||||
loadGatewayModelCatalog: () => Promise<Array<Record<string, unknown>>>;
|
||||
reqId?: string;
|
||||
}) {
|
||||
const respond = params.respond ?? vi.fn();
|
||||
const request = modelsHandlers["models.list"]({
|
||||
req: {
|
||||
type: "req",
|
||||
id: params.reqId ?? `req-models-list-${params.view}`,
|
||||
method: "models.list",
|
||||
params: { view: params.view },
|
||||
},
|
||||
params: { view: params.view },
|
||||
respond: respond as RespondFn,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
context: {
|
||||
getRuntimeConfig: () => params.runtimeConfig ?? ({} as OpenClawConfig),
|
||||
loadGatewayModelCatalog: params.loadGatewayModelCatalog,
|
||||
logGateway: {
|
||||
debug: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
});
|
||||
return { request, respond };
|
||||
}
|
||||
|
||||
describe("models.list", () => {
|
||||
it("does not block the configured view on slow model catalog discovery", async () => {
|
||||
const catalog = createDeferred<never>();
|
||||
const respond = vi.fn();
|
||||
const loadGatewayModelCatalog = vi.fn(() => catalog.promise);
|
||||
const runtimeConfig = {
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://openai.example.com",
|
||||
models: [{ id: "gpt-test", name: "GPT Test" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const request = modelsHandlers["models.list"]({
|
||||
req: {
|
||||
type: "req",
|
||||
id: "req-models-list-slow-catalog",
|
||||
method: "models.list",
|
||||
params: { view: "configured" },
|
||||
},
|
||||
params: { view: "configured" },
|
||||
respond,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
context: {
|
||||
getRuntimeConfig: () => {
|
||||
const config = {
|
||||
models: {
|
||||
providers: {
|
||||
openai: {
|
||||
baseUrl: "https://openai.example.com",
|
||||
models: [{ id: "gpt-test", name: "GPT Test" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
return config as unknown as OpenClawConfig;
|
||||
},
|
||||
loadGatewayModelCatalog,
|
||||
logGateway: {
|
||||
debug: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
const { request, respond } = requestModelsList({
|
||||
view: "configured",
|
||||
runtimeConfig,
|
||||
loadGatewayModelCatalog,
|
||||
reqId: "req-models-list-slow-catalog",
|
||||
});
|
||||
|
||||
await vi.advanceTimersByTimeAsync(800);
|
||||
@@ -86,29 +101,14 @@ describe("models.list", () => {
|
||||
|
||||
it("keeps the all view exact instead of timing out to a partial catalog", async () => {
|
||||
const catalog = createDeferred<[{ id: string; name: string; provider: string }]>();
|
||||
const respond = vi.fn();
|
||||
const loadGatewayModelCatalog = vi.fn(() => catalog.promise);
|
||||
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const request = modelsHandlers["models.list"]({
|
||||
req: {
|
||||
type: "req",
|
||||
id: "req-models-list-all-slow-catalog",
|
||||
method: "models.list",
|
||||
params: { view: "all" },
|
||||
},
|
||||
params: { view: "all" },
|
||||
respond,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
context: {
|
||||
getRuntimeConfig: () => ({}) as OpenClawConfig,
|
||||
loadGatewayModelCatalog,
|
||||
logGateway: {
|
||||
debug: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
const { request, respond } = requestModelsList({
|
||||
view: "all",
|
||||
loadGatewayModelCatalog,
|
||||
reqId: "req-models-list-all-slow-catalog",
|
||||
});
|
||||
|
||||
await vi.advanceTimersByTimeAsync(800);
|
||||
@@ -129,35 +129,21 @@ describe("models.list", () => {
|
||||
});
|
||||
|
||||
it("does not expose runtime params from catalog rows", async () => {
|
||||
const respond = vi.fn();
|
||||
await modelsHandlers["models.list"]({
|
||||
req: {
|
||||
type: "req",
|
||||
id: "req-models-list-redact-params",
|
||||
method: "models.list",
|
||||
params: { view: "all" },
|
||||
},
|
||||
params: { view: "all" },
|
||||
respond,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
context: {
|
||||
getRuntimeConfig: () => ({}) as OpenClawConfig,
|
||||
loadGatewayModelCatalog: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
id: "qwen-local",
|
||||
name: "Qwen Local",
|
||||
provider: "vllm",
|
||||
params: { qwenThinkingFormat: "chat-template" },
|
||||
},
|
||||
]),
|
||||
),
|
||||
logGateway: {
|
||||
debug: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
const { request, respond } = requestModelsList({
|
||||
view: "all",
|
||||
loadGatewayModelCatalog: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
id: "qwen-local",
|
||||
name: "Qwen Local",
|
||||
provider: "vllm",
|
||||
params: { qwenThinkingFormat: "chat-template" },
|
||||
},
|
||||
]),
|
||||
),
|
||||
reqId: "req-models-list-redact-params",
|
||||
});
|
||||
await request;
|
||||
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
true,
|
||||
@@ -191,27 +177,14 @@ describe("models.list", () => {
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
const configuredRespond = vi.fn();
|
||||
const loadConfiguredCatalog = vi.fn(() => Promise.resolve(catalog));
|
||||
await modelsHandlers["models.list"]({
|
||||
req: {
|
||||
type: "req",
|
||||
id: "req-models-list-provider-allowlist",
|
||||
method: "models.list",
|
||||
params: { view: "configured" },
|
||||
},
|
||||
params: { view: "configured" },
|
||||
respond: configuredRespond,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
context: {
|
||||
getRuntimeConfig: () => cfg,
|
||||
loadGatewayModelCatalog: loadConfiguredCatalog,
|
||||
logGateway: {
|
||||
debug: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
const { request: configuredRequest, respond: configuredRespond } = requestModelsList({
|
||||
view: "configured",
|
||||
runtimeConfig: cfg,
|
||||
loadGatewayModelCatalog: loadConfiguredCatalog,
|
||||
reqId: "req-models-list-provider-allowlist",
|
||||
});
|
||||
await configuredRequest;
|
||||
|
||||
expect(configuredRespond).toHaveBeenCalledWith(
|
||||
true,
|
||||
@@ -227,52 +200,24 @@ describe("models.list", () => {
|
||||
);
|
||||
expect(loadConfiguredCatalog).toHaveBeenCalledWith({ readOnly: false });
|
||||
|
||||
const allRespond = vi.fn();
|
||||
await modelsHandlers["models.list"]({
|
||||
req: {
|
||||
type: "req",
|
||||
id: "req-models-list-provider-allowlist-all",
|
||||
method: "models.list",
|
||||
params: { view: "all" },
|
||||
},
|
||||
params: { view: "all" },
|
||||
respond: allRespond,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
context: {
|
||||
getRuntimeConfig: () => cfg,
|
||||
loadGatewayModelCatalog: vi.fn(() => Promise.resolve(catalog)),
|
||||
logGateway: {
|
||||
debug: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
const { request: allRequest, respond: allRespond } = requestModelsList({
|
||||
view: "all",
|
||||
runtimeConfig: cfg,
|
||||
loadGatewayModelCatalog: vi.fn(() => Promise.resolve(catalog)),
|
||||
reqId: "req-models-list-provider-allowlist-all",
|
||||
});
|
||||
await allRequest;
|
||||
|
||||
expect(allRespond).toHaveBeenCalledWith(true, { models: catalog }, undefined);
|
||||
});
|
||||
|
||||
it("preserves catalog load errors before the timeout fallback wins", async () => {
|
||||
const respond = vi.fn();
|
||||
|
||||
await modelsHandlers["models.list"]({
|
||||
req: {
|
||||
type: "req",
|
||||
id: "req-models-list-catalog-error",
|
||||
method: "models.list",
|
||||
params: { view: "configured" },
|
||||
},
|
||||
params: { view: "configured" },
|
||||
respond,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
context: {
|
||||
getRuntimeConfig: () => ({}) as OpenClawConfig,
|
||||
loadGatewayModelCatalog: vi.fn(() => Promise.reject(new Error("catalog failed"))),
|
||||
logGateway: {
|
||||
debug: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
const { request, respond } = requestModelsList({
|
||||
view: "configured",
|
||||
loadGatewayModelCatalog: vi.fn(() => Promise.reject(new Error("catalog failed"))),
|
||||
reqId: "req-models-list-catalog-error",
|
||||
});
|
||||
await request;
|
||||
|
||||
const call = respond.mock.calls.at(0) as
|
||||
| [boolean, unknown, { code?: number; message?: string }]
|
||||
|
||||
Reference in New Issue
Block a user