refactor(agents): remove catalog browse timer test hooks

This commit is contained in:
Vincent Koc
2026-06-17 09:55:06 +08:00
parent fbfaba09fd
commit e5ff835c01
2 changed files with 12 additions and 52 deletions

View File

@@ -6,13 +6,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { MAX_TIMER_TIMEOUT_MS } from "../shared/number-coercion.js";
import {
DEFAULT_MODEL_CATALOG_BROWSE_TIMEOUT_MS,
loadModelCatalogForBrowse,
restoreModelCatalogBrowseTestDeps,
setModelCatalogBrowseTestDeps,
} from "./model-catalog-browse.js";
import type { ModelCatalogEntry } from "./model-catalog.types.js";
const DEFAULT_MODEL_CATALOG_BROWSE_TIMEOUT_MS = 750;
const readOnlyCatalog: ModelCatalogEntry[] = [
{ id: "gpt-readonly", name: "GPT Readonly", provider: "openai" },
];
@@ -41,7 +39,6 @@ describe("loadModelCatalogForBrowse", () => {
vi.clearAllTimers();
vi.restoreAllMocks();
vi.useRealTimers();
restoreModelCatalogBrowseTestDeps();
});
it("uses the read-only catalog for default browse views", async () => {
@@ -97,16 +94,8 @@ describe("loadModelCatalogForBrowse", () => {
});
it("returns an empty catalog when read-only catalog loading times out with provider wildcards", async () => {
vi.useFakeTimers();
const onTimeout = vi.fn();
const timeoutHandle = { unref: vi.fn() } as unknown as NodeJS.Timeout;
const clearTimeout = vi.fn();
setModelCatalogBrowseTestDeps({
setTimeout: vi.fn((callback: () => void) => {
queueMicrotask(callback);
return timeoutHandle;
}) as unknown as typeof globalThis.setTimeout,
clearTimeout: clearTimeout as unknown as typeof globalThis.clearTimeout,
});
const loadCatalog = vi.fn(() => new Promise<ModelCatalogEntry[]>(() => {}));
const resultPromise = loadModelCatalogForBrowse({
@@ -116,21 +105,15 @@ describe("loadModelCatalogForBrowse", () => {
onTimeout,
});
await vi.advanceTimersByTimeAsync(5);
await expect(resultPromise).resolves.toEqual([]);
expect(onTimeout).toHaveBeenCalledExactlyOnceWith(5);
expect(timeoutHandle["unref"]).toHaveBeenCalledOnce();
expect(clearTimeout).toHaveBeenCalledExactlyOnceWith(timeoutHandle);
});
it("uses the default timeout when timeoutMs is non-finite", async () => {
const onTimeout = vi.fn();
const timeoutHandle = { unref: vi.fn() } as unknown as NodeJS.Timeout;
const setTimeout = vi.fn(() => timeoutHandle);
const clearTimeout = vi.fn();
setModelCatalogBrowseTestDeps({
setTimeout: setTimeout as unknown as typeof globalThis.setTimeout,
clearTimeout: clearTimeout as unknown as typeof globalThis.clearTimeout,
});
const setTimeout = vi.spyOn(globalThis, "setTimeout");
const clearTimeout = vi.spyOn(globalThis, "clearTimeout");
const loadCatalog = vi.fn(async () => readOnlyCatalog);
const resultPromise = loadModelCatalogForBrowse({
@@ -145,18 +128,13 @@ describe("loadModelCatalogForBrowse", () => {
expect.any(Function),
DEFAULT_MODEL_CATALOG_BROWSE_TIMEOUT_MS,
);
expect(clearTimeout).toHaveBeenCalledExactlyOnceWith(timeoutHandle);
expect(clearTimeout).toHaveBeenCalledOnce();
expect(onTimeout).not.toHaveBeenCalled();
});
it("caps oversized browse timeouts before scheduling the fallback timer", async () => {
const timeoutHandle = { unref: vi.fn() } as unknown as NodeJS.Timeout;
const setTimeout = vi.fn(() => timeoutHandle);
const clearTimeout = vi.fn();
setModelCatalogBrowseTestDeps({
setTimeout: setTimeout as unknown as typeof globalThis.setTimeout,
clearTimeout: clearTimeout as unknown as typeof globalThis.clearTimeout,
});
const setTimeout = vi.spyOn(globalThis, "setTimeout");
const clearTimeout = vi.spyOn(globalThis, "clearTimeout");
const loadCatalog = vi.fn(async () => readOnlyCatalog);
const resultPromise = loadModelCatalogForBrowse({
@@ -167,6 +145,6 @@ describe("loadModelCatalogForBrowse", () => {
await expect(resultPromise).resolves.toBe(readOnlyCatalog);
expect(setTimeout).toHaveBeenCalledExactlyOnceWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS);
expect(clearTimeout).toHaveBeenCalledExactlyOnceWith(timeoutHandle);
expect(clearTimeout).toHaveBeenCalledOnce();
});
});

View File

@@ -13,29 +13,11 @@ import { parseConfiguredModelVisibilityEntries } from "./model-selection-shared.
* Loads the model catalog shape used by browse/list commands without letting optional
* provider discovery stall the CLI path.
*/
export const DEFAULT_MODEL_CATALOG_BROWSE_TIMEOUT_MS = 750;
const DEFAULT_MODEL_CATALOG_BROWSE_TIMEOUT_MS = 750;
/** Visible model subset requested by model browse callers. */
export type ModelCatalogBrowseView = "default" | "configured" | "all";
const modelCatalogBrowseDeps = {
setTimeout: globalThis.setTimeout,
clearTimeout: globalThis.clearTimeout,
};
/** Replaces timer hooks for deterministic timeout tests. */
export function setModelCatalogBrowseTestDeps(
overrides: Partial<typeof modelCatalogBrowseDeps>,
): void {
Object.assign(modelCatalogBrowseDeps, overrides);
}
/** Restores global timer hooks after catalog browse timeout tests. */
export function restoreModelCatalogBrowseTestDeps(): void {
modelCatalogBrowseDeps.setTimeout = globalThis.setTimeout;
modelCatalogBrowseDeps.clearTimeout = globalThis.clearTimeout;
}
/** True when a browse view cannot be answered from read-only cached catalog entries. */
export function modelCatalogBrowseRequiresFullDiscovery(params: {
cfg: OpenClawConfig;
@@ -74,7 +56,7 @@ export async function loadModelCatalogForBrowse(params: {
const timedOut = Symbol("model-catalog-browse-timeout");
const catalogPromise = params.loadCatalog({ readOnly: true });
const timeoutPromise = new Promise<typeof timedOut>((resolve) => {
timeout = modelCatalogBrowseDeps.setTimeout(() => resolve(timedOut), timeoutMs);
timeout = globalThis.setTimeout(() => resolve(timedOut), timeoutMs);
timeout.unref?.();
});
@@ -89,7 +71,7 @@ export async function loadModelCatalogForBrowse(params: {
return result;
} finally {
if (timeout) {
modelCatalogBrowseDeps.clearTimeout(timeout);
globalThis.clearTimeout(timeout);
}
}
}