mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-12 02:50:42 +00:00
test: tighten model catalog assertions
This commit is contained in:
@@ -105,6 +105,45 @@ function emptyPluginMetadataSnapshot() {
|
||||
};
|
||||
}
|
||||
|
||||
type ModelCatalogEntry = Awaited<
|
||||
ReturnType<typeof import("./model-catalog.js").loadModelCatalog>
|
||||
>[number];
|
||||
|
||||
function findCatalogEntry(
|
||||
entries: readonly ModelCatalogEntry[],
|
||||
provider: string,
|
||||
id: string,
|
||||
): ModelCatalogEntry | undefined {
|
||||
return entries.find((entry) => entry.provider === provider && entry.id === id);
|
||||
}
|
||||
|
||||
function requireCatalogEntry(
|
||||
entries: readonly ModelCatalogEntry[],
|
||||
provider: string,
|
||||
id: string,
|
||||
): ModelCatalogEntry {
|
||||
const entry = findCatalogEntry(entries, provider, id);
|
||||
if (!entry) {
|
||||
throw new Error(`expected catalog entry ${provider}/${id}`);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
function expectNoCatalogEntry(entries: readonly ModelCatalogEntry[], provider: string, id: string) {
|
||||
expect(findCatalogEntry(entries, provider, id)).toBeUndefined();
|
||||
}
|
||||
|
||||
function requireMockCallParam(
|
||||
mock: ReturnType<typeof vi.fn>,
|
||||
label: string,
|
||||
): Record<string, unknown> {
|
||||
const first = mock.mock.calls[0]?.[0];
|
||||
if (!first || typeof first !== "object" || Array.isArray(first)) {
|
||||
throw new Error(`expected ${label}`);
|
||||
}
|
||||
return first as Record<string, unknown>;
|
||||
}
|
||||
|
||||
describe("loadModelCatalog", () => {
|
||||
beforeAll(async () => {
|
||||
readFileMock = vi.fn();
|
||||
@@ -278,9 +317,8 @@ describe("loadModelCatalog", () => {
|
||||
readOnly: true,
|
||||
});
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({ id: "gpt-test", name: "GPT Test", provider: "openai" }),
|
||||
);
|
||||
const entry = requireCatalogEntry(result, "openai", "gpt-test");
|
||||
expect(entry.name).toBe("GPT Test");
|
||||
expect(ensureOpenClawModelsJsonMock).not.toHaveBeenCalled();
|
||||
expect(importPiSdk).not.toHaveBeenCalled();
|
||||
expect(loadPluginMetadataSnapshotMock).not.toHaveBeenCalled();
|
||||
@@ -484,19 +522,9 @@ describe("loadModelCatalog", () => {
|
||||
]);
|
||||
|
||||
const result = await loadModelCatalog({ config: {} as OpenClawConfig });
|
||||
expect(result).not.toContainEqual(
|
||||
expect.objectContaining({
|
||||
provider: "openai-codex",
|
||||
id: "gpt-5.3-codex-spark",
|
||||
}),
|
||||
);
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
provider: "openai-codex",
|
||||
id: "gpt-5.4",
|
||||
name: "GPT-5.3 Codex",
|
||||
}),
|
||||
);
|
||||
expectNoCatalogEntry(result, "openai-codex", "gpt-5.3-codex-spark");
|
||||
const entry = requireCatalogEntry(result, "openai-codex", "gpt-5.4");
|
||||
expect(entry.name).toBe("GPT-5.3 Codex");
|
||||
});
|
||||
|
||||
it("filters stale gpt-5.3-codex-spark built-ins from the catalog", async () => {
|
||||
@@ -528,24 +556,9 @@ describe("loadModelCatalog", () => {
|
||||
]);
|
||||
|
||||
const result = await loadModelCatalog({ config: {} as OpenClawConfig });
|
||||
expect(result).not.toContainEqual(
|
||||
expect.objectContaining({
|
||||
provider: "openai",
|
||||
id: "gpt-5.3-codex-spark",
|
||||
}),
|
||||
);
|
||||
expect(result).not.toContainEqual(
|
||||
expect.objectContaining({
|
||||
provider: "azure-openai-responses",
|
||||
id: "gpt-5.3-codex-spark",
|
||||
}),
|
||||
);
|
||||
expect(result).not.toContainEqual(
|
||||
expect.objectContaining({
|
||||
provider: "openai-codex",
|
||||
id: "gpt-5.3-codex-spark",
|
||||
}),
|
||||
);
|
||||
expectNoCatalogEntry(result, "openai", "gpt-5.3-codex-spark");
|
||||
expectNoCatalogEntry(result, "azure-openai-responses", "gpt-5.3-codex-spark");
|
||||
expectNoCatalogEntry(result, "openai-codex", "gpt-5.3-codex-spark");
|
||||
});
|
||||
|
||||
it("filters stale openai-codex 5.1/5.2/5.3 built-ins from the catalog", async () => {
|
||||
@@ -585,18 +598,10 @@ describe("loadModelCatalog", () => {
|
||||
]);
|
||||
|
||||
const result = await loadModelCatalog({ config: {} as OpenClawConfig });
|
||||
expect(result).not.toContainEqual(
|
||||
expect.objectContaining({ provider: "openai-codex", id: "gpt-5.1-codex-mini" }),
|
||||
);
|
||||
expect(result).not.toContainEqual(
|
||||
expect.objectContaining({ provider: "openai-codex", id: "gpt-5.2-codex" }),
|
||||
);
|
||||
expect(result).not.toContainEqual(
|
||||
expect.objectContaining({ provider: "openai-codex", id: "gpt-5.3-codex" }),
|
||||
);
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({ provider: "openai-codex", id: "gpt-5.5" }),
|
||||
);
|
||||
expectNoCatalogEntry(result, "openai-codex", "gpt-5.1-codex-mini");
|
||||
expectNoCatalogEntry(result, "openai-codex", "gpt-5.2-codex");
|
||||
expectNoCatalogEntry(result, "openai-codex", "gpt-5.3-codex");
|
||||
expect(requireCatalogEntry(result, "openai-codex", "gpt-5.5").name).toBe("GPT-5.5");
|
||||
});
|
||||
|
||||
it("does not synthesize gpt-5.4 OpenAI forward-compat entries from template models", async () => {
|
||||
@@ -648,13 +653,8 @@ describe("loadModelCatalog", () => {
|
||||
expect(
|
||||
result.some((entry) => entry.provider === "openai" && entry.id.startsWith("gpt-5.4")),
|
||||
).toBe(false);
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
provider: "openai-codex",
|
||||
id: "gpt-5.4",
|
||||
name: "GPT-5.3 Codex",
|
||||
}),
|
||||
);
|
||||
const entry = requireCatalogEntry(result, "openai-codex", "gpt-5.4");
|
||||
expect(entry.name).toBe("GPT-5.3 Codex");
|
||||
expect(
|
||||
result.some((entry) => entry.provider === "openai-codex" && entry.id === "gpt-5.4-mini"),
|
||||
).toBe(false);
|
||||
@@ -675,13 +675,8 @@ describe("loadModelCatalog", () => {
|
||||
|
||||
const result = await loadModelCatalog({ config: {} as OpenClawConfig });
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
provider: "kilocode",
|
||||
id: "google/gemini-3.1-pro-preview",
|
||||
name: "Gemini 3 Pro Preview",
|
||||
}),
|
||||
);
|
||||
const entry = requireCatalogEntry(result, "kilocode", "google/gemini-3.1-pro-preview");
|
||||
expect(entry.name).toBe("Gemini 3 Pro Preview");
|
||||
});
|
||||
|
||||
it("loads manifest catalog rows from the current metadata snapshot without provider runtime", () => {
|
||||
@@ -743,11 +738,8 @@ describe("loadModelCatalog", () => {
|
||||
fallbackToMetadataScan: false,
|
||||
});
|
||||
|
||||
expect(currentPluginMetadataSnapshotMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
}),
|
||||
);
|
||||
const call = requireMockCallParam(currentPluginMetadataSnapshotMock, "current snapshot params");
|
||||
expect(call.allowWorkspaceScopedSnapshot).toBe(true);
|
||||
expect(loadPluginMetadataSnapshotMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -760,12 +752,9 @@ describe("loadModelCatalog", () => {
|
||||
fallbackToMetadataScan: false,
|
||||
});
|
||||
|
||||
expect(currentPluginMetadataSnapshotMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
env,
|
||||
allowWorkspaceScopedSnapshot: true,
|
||||
}),
|
||||
);
|
||||
const call = requireMockCallParam(currentPluginMetadataSnapshotMock, "current snapshot params");
|
||||
expect(call.env).toBe(env);
|
||||
expect(call.allowWorkspaceScopedSnapshot).toBe(true);
|
||||
expect(loadPluginMetadataSnapshotMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -789,9 +778,7 @@ describe("loadModelCatalog", () => {
|
||||
|
||||
const result = await loadModelCatalog({ config: {} as OpenClawConfig });
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({ provider: "ollama", id: "llama3.2", name: "Llama 3.2" }),
|
||||
);
|
||||
expect(requireCatalogEntry(result, "ollama", "llama3.2").name).toBe("Llama 3.2");
|
||||
expect(
|
||||
result.filter((entry) => entry.provider === "openai" && entry.id === "gpt-4.1"),
|
||||
).toHaveLength(1);
|
||||
@@ -823,16 +810,11 @@ describe("loadModelCatalog", () => {
|
||||
} as OpenClawConfig,
|
||||
});
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
provider: "modelscope",
|
||||
id: "Qwen/Qwen3.5-35B-A3B",
|
||||
name: "Qwen3.5 35B",
|
||||
input: ["text", "image"],
|
||||
reasoning: true,
|
||||
contextWindow: 128_000,
|
||||
}),
|
||||
);
|
||||
const entry = requireCatalogEntry(result, "modelscope", "Qwen/Qwen3.5-35B-A3B");
|
||||
expect(entry.name).toBe("Qwen3.5 35B");
|
||||
expect(entry.input).toEqual(["text", "image"]);
|
||||
expect(entry.reasoning).toBe(true);
|
||||
expect(entry.contextWindow).toBe(128_000);
|
||||
});
|
||||
|
||||
it("dedupes configured models against discovered provider aliases", async () => {
|
||||
@@ -863,7 +845,10 @@ describe("loadModelCatalog", () => {
|
||||
|
||||
const matches = result.filter((entry) => findModelInCatalog([entry], "z-ai", "glm-5"));
|
||||
expect(matches).toHaveLength(1);
|
||||
expect(matches[0]).toMatchObject({ provider: "z.ai", id: "glm-5", name: "GLM-5" });
|
||||
const match = matches[0];
|
||||
expect(match?.provider).toBe("z.ai");
|
||||
expect(match?.id).toBe("glm-5");
|
||||
expect(match?.name).toBe("GLM-5");
|
||||
});
|
||||
|
||||
it("does not add unrelated models when provider plugins return nothing", async () => {
|
||||
|
||||
Reference in New Issue
Block a user