refactor: share model catalog test helpers

This commit is contained in:
Vincent Koc
2026-06-01 21:52:53 +02:00
parent 5a67c5c556
commit eb58c88598

View File

@@ -35,6 +35,39 @@ function model(id: string): GatewayModelChoice {
const getConfig = () => ({}) as OpenClawConfig;
function createRefreshingCatalogLoader(
firstCatalog: GatewayModelChoice[],
secondCatalog: GatewayModelChoice[],
) {
return vi
.fn<LoadModelCatalogForTest>()
.mockResolvedValueOnce(firstCatalog)
.mockResolvedValueOnce(secondCatalog);
}
async function expectCatalog(
loadModelCatalog: LoadModelCatalogForTest,
catalog: GatewayModelChoice[],
readOnly = true,
) {
await expect(
loadGatewayModelCatalog({
getConfig,
loadModelCatalog,
...(readOnly ? {} : { readOnly: false }),
}),
).resolves.toBe(catalog);
}
async function markStaleAndExpectPreviousCatalog(
loadModelCatalog: LoadModelCatalogForTest,
catalog: GatewayModelChoice[],
) {
markGatewayModelCatalogStaleForReload();
await expectCatalog(loadModelCatalog, catalog);
await vi.waitFor(() => expect(loadModelCatalog).toHaveBeenCalledTimes(2));
}
describe("loadGatewayModelCatalog", () => {
beforeEach(async () => {
await resetModelCatalogCacheForTest();
@@ -82,46 +115,26 @@ describe("loadGatewayModelCatalog", () => {
it("caches an empty read-only catalog until reload marks it stale", async () => {
const emptyCatalog: GatewayModelChoice[] = [];
const freshCatalog = [model("gpt-5.5")];
const loadModelCatalog = vi
.fn<LoadModelCatalogForTest>()
.mockResolvedValueOnce(emptyCatalog)
.mockResolvedValueOnce(freshCatalog);
const loadModelCatalog = createRefreshingCatalogLoader(emptyCatalog, freshCatalog);
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
emptyCatalog,
);
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
emptyCatalog,
);
await expectCatalog(loadModelCatalog, emptyCatalog);
await expectCatalog(loadModelCatalog, emptyCatalog);
expect(loadModelCatalog).toHaveBeenCalledTimes(1);
markGatewayModelCatalogStaleForReload();
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
emptyCatalog,
);
await vi.waitFor(() => expect(loadModelCatalog).toHaveBeenCalledTimes(2));
await markStaleAndExpectPreviousCatalog(loadModelCatalog, emptyCatalog);
await vi.waitFor(async () => {
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
freshCatalog,
);
await expectCatalog(loadModelCatalog, freshCatalog);
});
});
it("does not cache an empty full catalog so the next all-model request retries", async () => {
const emptyCatalog: GatewayModelChoice[] = [];
const freshCatalog = [model("gpt-5.5")];
const loadModelCatalog = vi
.fn<LoadModelCatalogForTest>()
.mockResolvedValueOnce(emptyCatalog)
.mockResolvedValueOnce(freshCatalog);
const loadModelCatalog = createRefreshingCatalogLoader(emptyCatalog, freshCatalog);
await expect(
loadGatewayModelCatalog({ getConfig, loadModelCatalog, readOnly: false }),
).resolves.toBe(emptyCatalog);
await expect(
loadGatewayModelCatalog({ getConfig, loadModelCatalog, readOnly: false }),
).resolves.toBe(freshCatalog);
await expectCatalog(loadModelCatalog, emptyCatalog, false);
await expectCatalog(loadModelCatalog, freshCatalog, false);
expect(loadModelCatalog).toHaveBeenCalledTimes(2);
});
@@ -135,21 +148,13 @@ describe("loadGatewayModelCatalog", () => {
.mockResolvedValueOnce(staleCatalog)
.mockReturnValueOnce(refresh.promise);
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
staleCatalog,
);
await expectCatalog(loadModelCatalog, staleCatalog);
markGatewayModelCatalogStaleForReload();
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
staleCatalog,
);
await vi.waitFor(() => expect(loadModelCatalog).toHaveBeenCalledTimes(2));
await markStaleAndExpectPreviousCatalog(loadModelCatalog, staleCatalog);
refresh.resolve(freshCatalog);
await vi.waitFor(async () => {
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
freshCatalog,
);
await expectCatalog(loadModelCatalog, freshCatalog);
});
});
@@ -162,25 +167,15 @@ describe("loadGatewayModelCatalog", () => {
.mockRejectedValueOnce(new Error("provider offline"))
.mockResolvedValueOnce(freshCatalog);
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
staleCatalog,
);
await expectCatalog(loadModelCatalog, staleCatalog);
markGatewayModelCatalogStaleForReload();
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
staleCatalog,
);
await vi.waitFor(() => expect(loadModelCatalog).toHaveBeenCalledTimes(2));
await markStaleAndExpectPreviousCatalog(loadModelCatalog, staleCatalog);
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
staleCatalog,
);
await expectCatalog(loadModelCatalog, staleCatalog);
await vi.waitFor(() => expect(loadModelCatalog).toHaveBeenCalledTimes(3));
await vi.waitFor(async () => {
await expect(loadGatewayModelCatalog({ getConfig, loadModelCatalog })).resolves.toBe(
freshCatalog,
);
await expectCatalog(loadModelCatalog, freshCatalog);
});
});
});