test(release): harden channel add setup fallback

This commit is contained in:
Peter Steinberger
2026-05-01 18:19:22 +01:00
parent 11dc38cd55
commit 1ff2d747dc
3 changed files with 66 additions and 2 deletions

View File

@@ -125,7 +125,7 @@ describeLive("xai live", () => {
: [];
const firstFunction = payloadTools[0]?.function;
if (firstFunction && typeof firstFunction === "object") {
expect((firstFunction as Record<string, unknown>).strict).toBeUndefined();
expect([undefined, false]).toContain((firstFunction as Record<string, unknown>).strict);
}
}, 45_000);

View File

@@ -1,4 +1,5 @@
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { getBundledChannelSetupPlugin } from "../channels/plugins/bundled.js";
import type { ChannelPluginCatalogEntry } from "../channels/plugins/catalog.js";
import type { ChannelPlugin } from "../channels/plugins/types.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
@@ -41,6 +42,11 @@ const pluginInstallRecordCommitMocks = vi.hoisted(() => ({
commitConfigWithPendingPluginInstalls: vi.fn(),
}));
const bundledMocks = vi.hoisted(() => ({
getBundledChannelPlugin: vi.fn(() => undefined),
getBundledChannelSetupPlugin: vi.fn(() => undefined),
}));
vi.mock("../channels/plugins/catalog.js", () => ({
getChannelPluginCatalogEntry: catalogMocks.getChannelPluginCatalogEntry,
listChannelPluginCatalogEntries: catalogMocks.listChannelPluginCatalogEntries,
@@ -56,7 +62,8 @@ vi.mock("../channels/plugins/bundled.js", async () => {
);
return {
...actual,
getBundledChannelPlugin: vi.fn(() => undefined),
getBundledChannelPlugin: bundledMocks.getBundledChannelPlugin,
getBundledChannelSetupPlugin: bundledMocks.getBundledChannelSetupPlugin,
};
});
@@ -287,6 +294,10 @@ describe("channelsAddCommand", () => {
catalogMocks.listChannelPluginCatalogEntries.mockReturnValue([]);
discoveryMocks.isCatalogChannelInstalled.mockClear();
discoveryMocks.isCatalogChannelInstalled.mockReturnValue(false);
bundledMocks.getBundledChannelPlugin.mockReset();
bundledMocks.getBundledChannelPlugin.mockReturnValue(undefined);
bundledMocks.getBundledChannelSetupPlugin.mockReset();
bundledMocks.getBundledChannelSetupPlugin.mockReturnValue(undefined);
vi.mocked(ensureChannelSetupPluginInstalled).mockReset();
vi.mocked(ensureChannelSetupPluginInstalled).mockImplementation(async ({ cfg }) => ({
cfg,
@@ -607,6 +618,57 @@ describe("channelsAddCommand", () => {
expect(runtime.exit).not.toHaveBeenCalled();
});
it("uses the bundled setup fallback when snapshots only see a runtime plugin", async () => {
configMocks.readConfigFileSnapshot.mockResolvedValue({ ...baseConfigSnapshot });
setActivePluginRegistry(
createTestRegistry([
{
pluginId: "telegram",
plugin: createChannelTestPluginBase({ id: "telegram", label: "Telegram" }),
source: "test",
},
]),
);
vi.mocked(getBundledChannelSetupPlugin).mockReturnValue({
...createChannelTestPluginBase({ id: "telegram", label: "Telegram" }),
setup: {
applyAccountConfig: ({ cfg, input }: ApplyAccountConfigParams) => ({
...cfg,
channels: {
...cfg.channels,
telegram: {
enabled: true,
botToken: input.token,
},
},
}),
},
});
await channelsAddCommand(
{
channel: "telegram",
token: "123456:token",
},
runtime,
{ hasFlags: true },
);
expect(getBundledChannelSetupPlugin).toHaveBeenCalledWith("telegram");
expect(configMocks.writeConfigFile).toHaveBeenCalledWith(
expect.objectContaining({
channels: expect.objectContaining({
telegram: expect.objectContaining({
enabled: true,
botToken: "123456:token",
}),
}),
}),
);
expect(runtime.error).not.toHaveBeenCalledWith("Channel telegram does not support add.");
expect(runtime.exit).not.toHaveBeenCalled();
});
it("falls back from untrusted workspace catalog shadows when adding by alias", async () => {
configMocks.readConfigFileSnapshot.mockResolvedValue({ ...baseConfigSnapshot });
setActivePluginRegistry(createTestRegistry());

View File

@@ -1,4 +1,5 @@
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
import { getBundledChannelSetupPlugin } from "../../channels/plugins/bundled.js";
import { parseOptionalDelimitedEntries } from "../../channels/plugins/helpers.js";
import { getLoadedChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
import { moveSingleAccountChannelSectionToDefaultAccount } from "../../channels/plugins/setup-helpers.js";
@@ -299,6 +300,7 @@ export async function channelsAddCommand(
});
return (
snapshot.channelSetups.find((entry) => entry.plugin.id === channelId)?.plugin ??
getBundledChannelSetupPlugin(channelId) ??
snapshot.channels.find((entry) => entry.plugin.id === channelId)?.plugin ??
existing
);