test: tighten shared card schema coverage

This commit is contained in:
Peter Steinberger
2026-03-24 17:02:56 -07:00
parent 639706f298
commit 561acd1675
3 changed files with 64 additions and 49 deletions

View File

@@ -576,17 +576,6 @@ describe("feishuPlugin actions", () => {
).rejects.toThrow("Feishu thread-reply requires messageId.");
});
it("declares card as optional in the tool schema", () => {
const discovery = feishuPlugin.actions?.describeMessageTool?.({ cfg });
const schema = Array.isArray(discovery?.schema) ? discovery.schema[0] : discovery?.schema;
const cardSchema = schema?.properties?.card;
expect(cardSchema).toBeDefined();
// TypeBox marks Optional schemas with Symbol(TypeBox.Optional) = "Optional".
expect(
(cardSchema as unknown as Record<symbol, unknown>)?.[Symbol.for("TypeBox.Optional")],
).toBe("Optional");
});
it("sends media-only messages without requiring card", async () => {
feishuOutboundSendMediaMock.mockResolvedValueOnce({
channel: "feishu",

View File

@@ -294,7 +294,7 @@ describe("resolveModel", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
const result = resolveModelForTest("custom", "missing-model", "/tmp/agent", cfg);
@@ -314,7 +314,7 @@ describe("resolveModel", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
const result = resolveModelForTest("google-paid", "missing-model", "/tmp/agent", cfg);
@@ -362,7 +362,7 @@ describe("resolveModel", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
// Requesting a non-listed model forces the providerCfg fallback branch.
const result = resolveModelForTest("custom", "missing-model", "/tmp/agent", cfg);
@@ -388,7 +388,7 @@ describe("resolveModel", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
const result = resolveModelForTest("custom", "missing-model", "/tmp/agent", cfg);
@@ -442,7 +442,7 @@ describe("resolveModel", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
const result = resolveModelForTest("custom", "model-b", "/tmp/agent", cfg);
@@ -469,7 +469,7 @@ describe("resolveModel", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
const result = resolveModelForTest("custom", "model-b", "/tmp/agent", cfg);
@@ -495,7 +495,7 @@ describe("resolveModel", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
const models = buildInlineProviderModels(cfg.models?.providers ?? {});
expect(models).toEqual(
@@ -661,7 +661,7 @@ describe("resolveModel", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
const result = resolveModelForTest("onehub", "glm-5", "/tmp/agent", cfg);
@@ -720,7 +720,7 @@ describe("resolveModel", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
const result = resolveModelForTest("qwen", "qwen3-coder-plus", "/tmp/agent", cfg);
@@ -869,7 +869,7 @@ describe("resolveModel", () => {
},
},
},
} as OpenClawConfig;
} as unknown as OpenClawConfig;
const result = resolveModelForTest("github-copilot", "gpt-5.4-mini", "/tmp/agent", cfg);

View File

@@ -37,6 +37,24 @@ function createTelegramPollExtraToolSchemas() {
};
}
function createCardSchemaPlugin(params: {
id: string;
label: string;
docsPath: string;
blurb: string;
}) {
return createChannelPlugin({
...params,
actions: ["send"],
capabilities: ["cards"],
toolSchema: () => ({
properties: {
card: createMessageToolCardSchema(),
},
}),
});
}
const mocks = vi.hoisted(() => ({
runMessageAction: vi.fn(),
loadConfig: vi.fn(() => ({})),
@@ -421,37 +439,45 @@ describe("message tool schema scoping", () => {
expect(actionEnum).toContain("poll");
});
it("keeps provider card schema optional after merging into the message tool schema", () => {
const feishuPlugin = createChannelPlugin({
id: "feishu",
label: "Feishu",
docsPath: "/channels/feishu",
blurb: "Feishu test plugin.",
actions: ["send"],
capabilities: ["cards"],
toolSchema: () => ({
properties: {
card: createMessageToolCardSchema(),
},
it.each([
{
provider: "feishu",
plugin: createCardSchemaPlugin({
id: "feishu",
label: "Feishu",
docsPath: "/channels/feishu",
blurb: "Feishu test plugin.",
}),
});
},
{
provider: "msteams",
plugin: createCardSchemaPlugin({
id: "msteams",
label: "MSTeams",
docsPath: "/channels/msteams",
blurb: "MSTeams test plugin.",
}),
},
])(
"keeps $provider card schema optional after merging into the message tool schema",
({ plugin }) => {
setActivePluginRegistry(
createTestRegistry([{ pluginId: plugin.id, source: "test", plugin }]),
);
setActivePluginRegistry(
createTestRegistry([{ pluginId: "feishu", source: "test", plugin: feishuPlugin }]),
);
const tool = createMessageTool({
config: {} as never,
currentChannelProvider: plugin.id,
});
const schema = tool.parameters as {
properties?: Record<string, unknown>;
required?: string[];
};
const tool = createMessageTool({
config: {} as never,
currentChannelProvider: "feishu",
});
const schema = tool.parameters as {
properties?: Record<string, unknown>;
required?: string[];
};
expect(schema.properties?.card).toBeDefined();
expect(schema.required ?? []).not.toContain("card");
});
expect(schema.properties?.card).toBeDefined();
expect(schema.required ?? []).not.toContain("card");
},
);
it("hides telegram poll extras when telegram polls are disabled in scoped mode", () => {
const telegramPluginWithConfig = createChannelPlugin({