test: collapse setup and monitor channel suites

This commit is contained in:
Peter Steinberger
2026-03-25 04:24:16 +00:00
parent cb76ba2406
commit 43058c021e
7 changed files with 402 additions and 411 deletions

View File

@@ -1,71 +0,0 @@
import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { loadRuntimeApiExportTypesViaJiti } from "../../test/helpers/extensions/jiti-runtime-api.ts";
const setMatrixRuntimeMock = vi.hoisted(() => vi.fn());
const registerChannelMock = vi.hoisted(() => vi.fn());
vi.mock("./src/runtime.js", () => ({
setMatrixRuntime: setMatrixRuntimeMock,
}));
const { default: matrixPlugin } = await import("./index.js");
describe("matrix plugin registration", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("loads the matrix runtime api through Jiti", () => {
const runtimeApiPath = path.join(process.cwd(), "extensions", "matrix", "runtime-api.ts");
expect(
loadRuntimeApiExportTypesViaJiti({
modulePath: runtimeApiPath,
exportNames: [
"requiresExplicitMatrixDefaultAccount",
"resolveMatrixDefaultOrOnlyAccountId",
],
realPluginSdkSpecifiers: [],
}),
).toEqual({
requiresExplicitMatrixDefaultAccount: "function",
resolveMatrixDefaultOrOnlyAccountId: "function",
});
}, 240_000);
it("loads the matrix src runtime api through Jiti without duplicate export errors", () => {
const runtimeApiPath = path.join(
process.cwd(),
"extensions",
"matrix",
"src",
"runtime-api.ts",
);
expect(
loadRuntimeApiExportTypesViaJiti({
modulePath: runtimeApiPath,
exportNames: ["resolveMatrixAccountStringValues"],
realPluginSdkSpecifiers: ["openclaw/plugin-sdk/matrix"],
}),
).toEqual({
resolveMatrixAccountStringValues: "function",
});
}, 240_000);
it("registers the channel without bootstrapping crypto runtime", () => {
const runtime = {} as never;
matrixPlugin.register({
runtime,
logger: {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
registerChannel: registerChannelMock,
} as never);
expect(setMatrixRuntimeMock).toHaveBeenCalledWith(runtime);
expect(registerChannelMock).toHaveBeenCalledWith({ plugin: expect.any(Object) });
});
});

View File

@@ -1,4 +1,6 @@
import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { loadRuntimeApiExportTypesViaJiti } from "../../../../../test/helpers/extensions/jiti-runtime-api.ts";
const hoisted = vi.hoisted(() => {
const callOrder: string[] = [];
@@ -31,6 +33,7 @@ const hoisted = vi.hoisted(() => {
const stopThreadBindingManager = vi.fn();
const releaseSharedClientInstance = vi.fn(async () => true);
const setActiveMatrixClient = vi.fn();
const setMatrixRuntime = vi.fn();
return {
callOrder,
client,
@@ -41,29 +44,34 @@ const hoisted = vi.hoisted(() => {
releaseSharedClientInstance,
resolveTextChunkLimit,
setActiveMatrixClient,
setMatrixRuntime,
state,
stopThreadBindingManager,
};
});
vi.mock("../../runtime-api.js", () => ({
GROUP_POLICY_BLOCKED_LABEL: {
room: "room",
},
mergeAllowlist: ({ existing, additions }: { existing: string[]; additions: string[] }) => [
...existing,
...additions,
],
resolveThreadBindingIdleTimeoutMsForChannel: () => 24 * 60 * 60 * 1000,
resolveThreadBindingMaxAgeMsForChannel: () => 0,
resolveAllowlistProviderRuntimeGroupPolicy: () => ({
groupPolicy: "allowlist",
providerMissingFallbackApplied: false,
}),
resolveDefaultGroupPolicy: () => "allowlist",
summarizeMapping: vi.fn(),
warnMissingProviderGroupPolicyFallbackOnce: vi.fn(),
}));
vi.mock("../../runtime-api.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../runtime-api.js")>();
return {
...actual,
GROUP_POLICY_BLOCKED_LABEL: {
room: "room",
},
mergeAllowlist: ({ existing, additions }: { existing: string[]; additions: string[] }) => [
...existing,
...additions,
],
resolveThreadBindingIdleTimeoutMsForChannel: () => 24 * 60 * 60 * 1000,
resolveThreadBindingMaxAgeMsForChannel: () => 0,
resolveAllowlistProviderRuntimeGroupPolicy: () => ({
groupPolicy: "allowlist",
providerMissingFallbackApplied: false,
}),
resolveDefaultGroupPolicy: () => "allowlist",
summarizeMapping: vi.fn(),
warnMissingProviderGroupPolicyFallbackOnce: vi.fn(),
};
});
vi.mock("../../resolve-targets.js", () => ({
resolveMatrixTargets: vi.fn(async () => []),
@@ -99,17 +107,22 @@ vi.mock("../../runtime.js", () => ({
loadWebMedia: vi.fn(),
},
}),
setMatrixRuntime: hoisted.setMatrixRuntime,
}));
vi.mock("../accounts.js", () => ({
resolveConfiguredMatrixBotUserIds: vi.fn(() => new Set<string>()),
resolveMatrixAccount: () => ({
accountId: "default",
config: {
dm: {},
},
}),
}));
vi.mock("../accounts.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../accounts.js")>();
return {
...actual,
resolveConfiguredMatrixBotUserIds: vi.fn(() => new Set<string>()),
resolveMatrixAccount: () => ({
accountId: "default",
config: {
dm: {},
},
}),
};
});
vi.mock("../active-client.js", () => ({
setActiveMatrixClient: hoisted.setActiveMatrixClient,
@@ -378,3 +391,64 @@ describe("monitorMatrixProvider", () => {
);
});
});
describe("matrix plugin registration", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("loads the matrix runtime api through Jiti", () => {
const runtimeApiPath = path.join(process.cwd(), "extensions", "matrix", "runtime-api.ts");
expect(
loadRuntimeApiExportTypesViaJiti({
modulePath: runtimeApiPath,
exportNames: [
"requiresExplicitMatrixDefaultAccount",
"resolveMatrixDefaultOrOnlyAccountId",
],
realPluginSdkSpecifiers: [],
}),
).toEqual({
requiresExplicitMatrixDefaultAccount: "function",
resolveMatrixDefaultOrOnlyAccountId: "function",
});
}, 240_000);
it("loads the matrix src runtime api through Jiti without duplicate export errors", () => {
const runtimeApiPath = path.join(
process.cwd(),
"extensions",
"matrix",
"src",
"runtime-api.ts",
);
expect(
loadRuntimeApiExportTypesViaJiti({
modulePath: runtimeApiPath,
exportNames: ["resolveMatrixAccountStringValues"],
realPluginSdkSpecifiers: ["openclaw/plugin-sdk/matrix"],
}),
).toEqual({
resolveMatrixAccountStringValues: "function",
});
}, 240_000);
it("registers the channel without bootstrapping crypto runtime", async () => {
const { default: matrixPlugin } = await import("../../../index.js");
const runtime = {} as never;
const registerChannel = vi.fn();
matrixPlugin.register({
runtime,
logger: {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
registerChannel,
} as never);
expect(hoisted.setMatrixRuntime).toHaveBeenCalledWith(runtime);
expect(registerChannel).toHaveBeenCalledWith({ plugin: expect.any(Object) });
});
});

View File

@@ -1,43 +0,0 @@
import { describe, expect, it, vi } from "vitest";
import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js";
import plugin from "./index.js";
import type { OpenClawPluginApi } from "./runtime-api.js";
function createApi(
registrationMode: OpenClawPluginApi["registrationMode"],
registerHttpRoute = vi.fn(),
): OpenClawPluginApi {
return createTestPluginApi({
id: "mattermost",
name: "Mattermost",
source: "test",
config: {},
runtime: {} as OpenClawPluginApi["runtime"],
registrationMode,
registerHttpRoute,
});
}
describe("mattermost plugin register", () => {
it("skips slash callback registration in setup-only mode", () => {
const registerHttpRoute = vi.fn();
plugin.register(createApi("setup-only", registerHttpRoute));
expect(registerHttpRoute).not.toHaveBeenCalled();
});
it("registers slash callback routes in full mode", () => {
const registerHttpRoute = vi.fn();
plugin.register(createApi("full", registerHttpRoute));
expect(registerHttpRoute).toHaveBeenCalledTimes(1);
expect(registerHttpRoute).toHaveBeenCalledWith(
expect.objectContaining({
path: "/api/channels/mattermost/command",
auth: "plugin",
}),
);
});
});

View File

@@ -1,153 +0,0 @@
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
import { describe, expect, it, vi } from "vitest";
const resolveMattermostAccount = vi.hoisted(() => vi.fn());
const normalizeMattermostBaseUrl = vi.hoisted(() => vi.fn((value: string | undefined) => value));
const hasConfiguredSecretInput = vi.hoisted(() => vi.fn((value: unknown) => Boolean(value)));
vi.mock("./mattermost/accounts.js", () => ({
resolveMattermostAccount,
}));
vi.mock("./mattermost/client.js", () => ({
normalizeMattermostBaseUrl,
}));
vi.mock("./secret-input.js", () => ({
hasConfiguredSecretInput,
}));
describe("mattermost setup core", () => {
it("reports configuration only when token and base url are both present", async () => {
const { isMattermostConfigured } = await import("./setup-core.js");
expect(
isMattermostConfigured({
botToken: "bot-token",
baseUrl: "https://chat.example.com",
config: {},
} as never),
).toBe(true);
expect(
isMattermostConfigured({
botToken: "",
baseUrl: "https://chat.example.com",
config: { botToken: "secret-ref" },
} as never),
).toBe(true);
expect(
isMattermostConfigured({
botToken: "",
baseUrl: "",
config: {},
} as never),
).toBe(false);
});
it("resolves accounts with unresolved secret refs allowed", async () => {
resolveMattermostAccount.mockReturnValue({ accountId: "default" });
const { resolveMattermostAccountWithSecrets } = await import("./setup-core.js");
const cfg = { channels: { mattermost: {} } };
expect(resolveMattermostAccountWithSecrets(cfg as never, "default")).toEqual({
accountId: "default",
});
expect(resolveMattermostAccount).toHaveBeenCalledWith({
cfg,
accountId: "default",
allowUnresolvedSecretRef: true,
});
});
it("validates env and explicit credential requirements", async () => {
const { mattermostSetupAdapter } = await import("./setup-core.js");
const validateInput = mattermostSetupAdapter.validateInput;
expect(validateInput).toBeTypeOf("function");
expect(
validateInput!({
accountId: "secondary",
input: { useEnv: true },
} as never),
).toBe("Mattermost env vars can only be used for the default account.");
normalizeMattermostBaseUrl.mockReturnValue(undefined);
expect(
validateInput!({
accountId: DEFAULT_ACCOUNT_ID,
input: { useEnv: false, botToken: "tok", httpUrl: "not-a-url" },
} as never),
).toBe("Mattermost requires --bot-token and --http-url (or --use-env).");
normalizeMattermostBaseUrl.mockReturnValue("https://chat.example.com");
expect(
validateInput!({
accountId: DEFAULT_ACCOUNT_ID,
input: { useEnv: false, botToken: "tok", httpUrl: "https://chat.example.com" },
} as never),
).toBeNull();
});
it("applies normalized config for default and named accounts", async () => {
normalizeMattermostBaseUrl.mockReturnValue("https://chat.example.com");
const { mattermostSetupAdapter } = await import("./setup-core.js");
const applyAccountConfig = mattermostSetupAdapter.applyAccountConfig;
expect(applyAccountConfig).toBeTypeOf("function");
expect(
applyAccountConfig!({
cfg: { channels: { mattermost: {} } },
accountId: DEFAULT_ACCOUNT_ID,
input: {
name: "Default",
botToken: "tok",
httpUrl: "https://chat.example.com",
},
} as never),
).toEqual({
channels: {
mattermost: {
enabled: true,
name: "Default",
botToken: "tok",
baseUrl: "https://chat.example.com",
},
},
});
expect(
applyAccountConfig!({
cfg: {
channels: {
mattermost: {
name: "Legacy",
},
},
},
accountId: "Work Team",
input: {
name: "Work",
botToken: "tok2",
httpUrl: "https://chat.example.com",
},
} as never),
).toMatchObject({
channels: {
mattermost: {
accounts: {
default: { name: "Legacy" },
"work-team": {
enabled: true,
name: "Work",
botToken: "tok2",
baseUrl: "https://chat.example.com",
},
},
},
},
});
});
});

View File

@@ -1,24 +0,0 @@
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../runtime-api.js";
import { mattermostSetupWizard } from "./setup-surface.js";
describe("mattermost setup status", () => {
it("treats SecretRef botToken as configured when baseUrl is present", async () => {
const configured = await mattermostSetupWizard.status.resolveConfigured({
cfg: {
channels: {
mattermost: {
baseUrl: "https://chat.example.test",
botToken: {
source: "env",
provider: "default",
id: "MATTERMOST_BOT_TOKEN",
},
},
},
} as OpenClawConfig,
});
expect(configured).toBe(true);
});
});

View File

@@ -1,93 +0,0 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../runtime-api.js";
import { mattermostSetupWizard } from "./setup-surface.js";
describe("mattermost setup surface", () => {
afterEach(() => {
vi.unstubAllEnvs();
});
it("treats secret-ref tokens plus base url as configured", async () => {
const configured = await mattermostSetupWizard.status.resolveConfigured({
cfg: {
channels: {
mattermost: {
baseUrl: "https://chat.example.com",
botToken: {
source: "env",
provider: "default",
id: "MATTERMOST_BOT_TOKEN",
},
},
},
} as OpenClawConfig,
});
expect(configured).toBe(true);
});
it("shows intro note only when the target account is not configured", () => {
expect(
mattermostSetupWizard.introNote?.shouldShow?.({
cfg: {
channels: {
mattermost: {},
},
} as OpenClawConfig,
accountId: "default",
} as never),
).toBe(true);
expect(
mattermostSetupWizard.introNote?.shouldShow?.({
cfg: {
channels: {
mattermost: {
baseUrl: "https://chat.example.com",
botToken: {
source: "env",
provider: "default",
id: "MATTERMOST_BOT_TOKEN",
},
},
},
} as OpenClawConfig,
accountId: "default",
} as never),
).toBe(false);
});
it("offers env shortcut only for the default account when env is present and config is empty", () => {
vi.stubEnv("MATTERMOST_BOT_TOKEN", "bot-token");
vi.stubEnv("MATTERMOST_URL", "https://chat.example.com");
expect(
mattermostSetupWizard.envShortcut?.isAvailable?.({
cfg: { channels: { mattermost: {} } } as OpenClawConfig,
accountId: "default",
} as never),
).toBe(true);
expect(
mattermostSetupWizard.envShortcut?.isAvailable?.({
cfg: { channels: { mattermost: {} } } as OpenClawConfig,
accountId: "work",
} as never),
).toBe(false);
});
it("keeps env shortcut as a no-op patch for the selected account", () => {
expect(
mattermostSetupWizard.envShortcut?.apply?.({
cfg: { channels: { mattermost: { enabled: false } } } as OpenClawConfig,
accountId: "default",
} as never),
).toEqual({
channels: {
mattermost: {
enabled: true,
},
},
});
});
});

View File

@@ -0,0 +1,301 @@
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createTestPluginApi } from "../../../test/helpers/extensions/plugin-api.js";
import plugin from "../index.js";
import type { OpenClawConfig, OpenClawPluginApi } from "../runtime-api.js";
import { mattermostSetupWizard } from "./setup-surface.js";
const resolveMattermostAccount = vi.hoisted(() => vi.fn());
const normalizeMattermostBaseUrl = vi.hoisted(() => vi.fn((value: string | undefined) => value));
const hasConfiguredSecretInput = vi.hoisted(() => vi.fn((value: unknown) => Boolean(value)));
vi.mock("./mattermost/accounts.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./mattermost/accounts.js")>();
return {
...actual,
resolveMattermostAccount: (...args: Parameters<typeof actual.resolveMattermostAccount>) => {
const mocked = resolveMattermostAccount(...args);
return mocked === undefined ? actual.resolveMattermostAccount(...args) : mocked;
},
};
});
vi.mock("./mattermost/client.js", () => ({
normalizeMattermostBaseUrl,
}));
vi.mock("./secret-input.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./secret-input.js")>();
return {
...actual,
hasConfiguredSecretInput,
};
});
function createApi(
registrationMode: OpenClawPluginApi["registrationMode"],
registerHttpRoute = vi.fn(),
): OpenClawPluginApi {
return createTestPluginApi({
id: "mattermost",
name: "Mattermost",
source: "test",
config: {},
runtime: {} as OpenClawPluginApi["runtime"],
registrationMode,
registerHttpRoute,
});
}
describe("mattermost setup", () => {
afterEach(() => {
resolveMattermostAccount.mockReset();
normalizeMattermostBaseUrl.mockReset();
normalizeMattermostBaseUrl.mockImplementation((value: string | undefined) => value);
hasConfiguredSecretInput.mockReset();
hasConfiguredSecretInput.mockImplementation((value: unknown) => Boolean(value));
vi.unstubAllEnvs();
});
it("reports configuration only when token and base url are both present", async () => {
const { isMattermostConfigured } = await import("./setup-core.js");
expect(
isMattermostConfigured({
botToken: "bot-token",
baseUrl: "https://chat.example.com",
config: {},
} as never),
).toBe(true);
expect(
isMattermostConfigured({
botToken: "",
baseUrl: "https://chat.example.com",
config: { botToken: "secret-ref" },
} as never),
).toBe(true);
expect(
isMattermostConfigured({
botToken: "",
baseUrl: "",
config: {},
} as never),
).toBe(false);
});
it("resolves accounts with unresolved secret refs allowed", async () => {
resolveMattermostAccount.mockReturnValue({ accountId: "default" });
const { resolveMattermostAccountWithSecrets } = await import("./setup-core.js");
const cfg = { channels: { mattermost: {} } };
expect(resolveMattermostAccountWithSecrets(cfg as never, "default")).toEqual({
accountId: "default",
});
expect(resolveMattermostAccount).toHaveBeenCalledWith({
cfg,
accountId: "default",
allowUnresolvedSecretRef: true,
});
});
it("validates env and explicit credential requirements", async () => {
const { mattermostSetupAdapter } = await import("./setup-core.js");
const validateInput = mattermostSetupAdapter.validateInput;
expect(validateInput).toBeTypeOf("function");
expect(
validateInput!({
accountId: "secondary",
input: { useEnv: true },
} as never),
).toBe("Mattermost env vars can only be used for the default account.");
normalizeMattermostBaseUrl.mockReturnValue(undefined);
expect(
validateInput!({
accountId: DEFAULT_ACCOUNT_ID,
input: { useEnv: false, botToken: "tok", httpUrl: "not-a-url" },
} as never),
).toBe("Mattermost requires --bot-token and --http-url (or --use-env).");
normalizeMattermostBaseUrl.mockReturnValue("https://chat.example.com");
expect(
validateInput!({
accountId: DEFAULT_ACCOUNT_ID,
input: { useEnv: false, botToken: "tok", httpUrl: "https://chat.example.com" },
} as never),
).toBeNull();
});
it("applies normalized config for default and named accounts", async () => {
normalizeMattermostBaseUrl.mockReturnValue("https://chat.example.com");
const { mattermostSetupAdapter } = await import("./setup-core.js");
const applyAccountConfig = mattermostSetupAdapter.applyAccountConfig;
expect(applyAccountConfig).toBeTypeOf("function");
expect(
applyAccountConfig!({
cfg: { channels: { mattermost: {} } },
accountId: DEFAULT_ACCOUNT_ID,
input: {
name: "Default",
botToken: "tok",
httpUrl: "https://chat.example.com",
},
} as never),
).toEqual({
channels: {
mattermost: {
enabled: true,
name: "Default",
botToken: "tok",
baseUrl: "https://chat.example.com",
},
},
});
expect(
applyAccountConfig!({
cfg: {
channels: {
mattermost: {
name: "Legacy",
},
},
},
accountId: "Work Team",
input: {
name: "Work",
botToken: "tok2",
httpUrl: "https://chat.example.com",
},
} as never),
).toMatchObject({
channels: {
mattermost: {
accounts: {
default: { name: "Legacy" },
"work-team": {
enabled: true,
name: "Work",
botToken: "tok2",
baseUrl: "https://chat.example.com",
},
},
},
},
});
});
it("skips slash callback registration in setup-only mode", () => {
const registerHttpRoute = vi.fn();
plugin.register(createApi("setup-only", registerHttpRoute));
expect(registerHttpRoute).not.toHaveBeenCalled();
});
it("registers slash callback routes in full mode", () => {
const registerHttpRoute = vi.fn();
plugin.register(createApi("full", registerHttpRoute));
expect(registerHttpRoute).toHaveBeenCalledTimes(1);
expect(registerHttpRoute).toHaveBeenCalledWith(
expect.objectContaining({
path: "/api/channels/mattermost/command",
auth: "plugin",
}),
);
});
it.each(["https://chat.example.com", "https://chat.example.test"])(
"treats secret-ref tokens plus base url as configured: %s",
async (baseUrl) => {
const configured = await mattermostSetupWizard.status.resolveConfigured({
cfg: {
channels: {
mattermost: {
baseUrl,
botToken: {
source: "env",
provider: "default",
id: "MATTERMOST_BOT_TOKEN",
},
},
},
} as OpenClawConfig,
});
expect(configured).toBe(true);
},
);
it("shows intro note only when the target account is not configured", () => {
expect(
mattermostSetupWizard.introNote?.shouldShow?.({
cfg: {
channels: {
mattermost: {},
},
} as OpenClawConfig,
accountId: "default",
} as never),
).toBe(true);
expect(
mattermostSetupWizard.introNote?.shouldShow?.({
cfg: {
channels: {
mattermost: {
baseUrl: "https://chat.example.com",
botToken: {
source: "env",
provider: "default",
id: "MATTERMOST_BOT_TOKEN",
},
},
},
} as OpenClawConfig,
accountId: "default",
} as never),
).toBe(false);
});
it("offers env shortcut only for the default account when env is present and config is empty", () => {
vi.stubEnv("MATTERMOST_BOT_TOKEN", "bot-token");
vi.stubEnv("MATTERMOST_URL", "https://chat.example.com");
expect(
mattermostSetupWizard.envShortcut?.isAvailable?.({
cfg: { channels: { mattermost: {} } } as OpenClawConfig,
accountId: "default",
} as never),
).toBe(true);
expect(
mattermostSetupWizard.envShortcut?.isAvailable?.({
cfg: { channels: { mattermost: {} } } as OpenClawConfig,
accountId: "work",
} as never),
).toBe(false);
});
it("keeps env shortcut as a no-op patch for the selected account", () => {
expect(
mattermostSetupWizard.envShortcut?.apply?.({
cfg: { channels: { mattermost: { enabled: false } } } as OpenClawConfig,
accountId: "default",
} as never),
).toEqual({
channels: {
mattermost: {
enabled: true,
},
},
});
});
});