From 4c67991f43f4d232ed0732ca8cd2897ce6e83b01 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 7 Apr 2026 13:58:16 +0100 Subject: [PATCH] test: speed up matrix channel seam tests --- .../matrix/src/channel-account-paths.ts | 91 ++++++++++++++++++ .../matrix/src/channel.account-paths.test.ts | 20 +++- extensions/matrix/src/channel.setup.test.ts | 4 +- extensions/matrix/src/channel.ts | 92 +++++-------------- extensions/matrix/src/config-adapter.ts | 43 +++++++++ 5 files changed, 172 insertions(+), 78 deletions(-) create mode 100644 extensions/matrix/src/channel-account-paths.ts create mode 100644 extensions/matrix/src/config-adapter.ts diff --git a/extensions/matrix/src/channel-account-paths.ts b/extensions/matrix/src/channel-account-paths.ts new file mode 100644 index 00000000000..9823d79809f --- /dev/null +++ b/extensions/matrix/src/channel-account-paths.ts @@ -0,0 +1,91 @@ +import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing"; +import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status"; +import { formatMatrixErrorMessage } from "./matrix/errors.js"; +import type { MatrixProbe } from "./matrix/probe.js"; +import type { CoreConfig } from "./types.js"; + +type ResolveMatrixAuth = (params: { cfg: CoreConfig; accountId?: string }) => Promise<{ + homeserver: string; + accessToken: string; + userId: string; + deviceId?: string; + allowPrivateNetwork?: boolean; + ssrfPolicy?: unknown; + dispatcherPolicy?: unknown; +}>; + +type ProbeMatrix = (params: { + homeserver: string; + accessToken: string; + userId: string; + deviceId?: string; + timeoutMs?: number; + accountId?: string; + allowPrivateNetwork?: boolean; + ssrfPolicy?: unknown; + dispatcherPolicy?: unknown; +}) => Promise; + +type SendMessageMatrix = ( + to: string, + message: string, + options?: { accountId?: string }, +) => Promise; + +export function createMatrixProbeAccount(params: { + resolveMatrixAuth: ResolveMatrixAuth; + probeMatrix: ProbeMatrix; +}) { + return async ({ + account, + timeoutMs, + cfg, + }: { + account: { accountId?: string }; + timeoutMs?: number; + cfg: unknown; + }): Promise => { + try { + const auth = await params.resolveMatrixAuth({ + cfg: cfg as CoreConfig, + accountId: account.accountId, + }); + return await params.probeMatrix({ + homeserver: auth.homeserver, + accessToken: auth.accessToken, + userId: auth.userId, + deviceId: auth.deviceId, + timeoutMs, + accountId: account.accountId, + allowPrivateNetwork: auth.allowPrivateNetwork, + ssrfPolicy: auth.ssrfPolicy, + dispatcherPolicy: auth.dispatcherPolicy, + }); + } catch (err) { + return { + ok: false, + error: formatMatrixErrorMessage(err), + elapsedMs: 0, + }; + } + }; +} + +export function createMatrixPairingText(sendMessageMatrix: SendMessageMatrix) { + return { + idLabel: "matrixUserId", + message: PAIRING_APPROVED_MESSAGE, + normalizeAllowEntry: createPairingPrefixStripper(/^matrix:/i), + notify: async ({ + id, + message, + accountId, + }: { + id: string; + message: string; + accountId?: string; + }) => { + await sendMessageMatrix(`user:${id}`, message, accountId ? { accountId } : {}); + }, + }; +} diff --git a/extensions/matrix/src/channel.account-paths.test.ts b/extensions/matrix/src/channel.account-paths.test.ts index 6e9681f3567..ebca5661bef 100644 --- a/extensions/matrix/src/channel.account-paths.test.ts +++ b/extensions/matrix/src/channel.account-paths.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import { createMatrixPairingText, createMatrixProbeAccount } from "./channel-account-paths.js"; const sendMessageMatrixMock = vi.hoisted(() => vi.fn()); const probeMatrixMock = vi.hoisted(() => vi.fn()); @@ -28,8 +29,6 @@ vi.mock("./matrix/client.js", async () => { }; }); -const { matrixPlugin } = await import("./channel.js"); - describe("matrix account path propagation", () => { beforeEach(() => { vi.clearAllMocks(); @@ -54,9 +53,15 @@ describe("matrix account path propagation", () => { }); it("forwards accountId when notifying pairing approval", async () => { - await matrixPlugin.pairing!.notifyApproval?.({ - cfg: {}, + const pairingText = createMatrixPairingText(sendMessageMatrixMock); + + expect(pairingText.normalizeAllowEntry(" matrix:@user:example.org ")).toBe( + "@user:example.org", + ); + + await pairingText.notify({ id: "@user:example.org", + message: pairingText.message, accountId: "poe", }); @@ -68,7 +73,12 @@ describe("matrix account path propagation", () => { }); it("forwards accountId and deviceId to matrix probes", async () => { - await matrixPlugin.status!.probeAccount?.({ + const probeAccount = createMatrixProbeAccount({ + resolveMatrixAuth: resolveMatrixAuthMock, + probeMatrix: probeMatrixMock, + }); + + await probeAccount({ cfg: {} as never, timeoutMs: 500, account: { diff --git a/extensions/matrix/src/channel.setup.test.ts b/extensions/matrix/src/channel.setup.test.ts index 5c9bd306dd2..18e9bdc6e10 100644 --- a/extensions/matrix/src/channel.setup.test.ts +++ b/extensions/matrix/src/channel.setup.test.ts @@ -9,7 +9,7 @@ vi.mock("./matrix/actions/verification.js", () => ({ bootstrapMatrixVerification: verificationMocks.bootstrapMatrixVerification, })); -import { matrixPlugin } from "./channel.js"; +import { matrixConfigAdapter } from "./config-adapter.js"; import { runMatrixSetupBootstrapAfterConfigWrite } from "./setup-bootstrap.js"; import { matrixSetupAdapter } from "./setup-core.js"; import { installMatrixTestRuntime } from "./test-runtime.js"; @@ -238,7 +238,7 @@ describe("matrix setup post-write bootstrap", () => { }); it("clears allowPrivateNetwork and proxy when deleting the default Matrix account config", () => { - const updated = matrixPlugin.config.deleteAccount?.({ + const updated = matrixConfigAdapter.deleteAccount?.({ cfg: { channels: { matrix: { diff --git a/extensions/matrix/src/channel.ts b/extensions/matrix/src/channel.ts index 88dad0dc10b..5bb483d178a 100644 --- a/extensions/matrix/src/channel.ts +++ b/extensions/matrix/src/channel.ts @@ -1,18 +1,14 @@ import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers"; -import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id"; import { adaptScopedAccountAccessor, - createScopedChannelConfigAdapter, createScopedDmSecurityResolver, } from "openclaw/plugin-sdk/channel-config-helpers"; import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-primitives"; import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/channel-core"; -import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing"; import { createAllowlistProviderOpenWarningCollector, projectAccountConfigWarningCollector, } from "openclaw/plugin-sdk/channel-policy"; -import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status"; import { createScopedAccountReplyToModeResolver } from "openclaw/plugin-sdk/conversation-runtime"; import { createChannelDirectoryAdapter, @@ -32,6 +28,8 @@ import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking"; import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { matrixMessageActions } from "./actions.js"; import { matrixApprovalCapability } from "./approval-native.js"; +import { createMatrixPairingText, createMatrixProbeAccount } from "./channel-account-paths.js"; +import { DEFAULT_ACCOUNT_ID, matrixConfigAdapter } from "./config-adapter.js"; import { MatrixConfigSchema } from "./config-schema.js"; import { matrixDoctor } from "./doctor.js"; import { shouldSuppressLocalMatrixExecApprovalPrompt } from "./exec-approvals.js"; @@ -40,14 +38,11 @@ import { resolveMatrixGroupToolPolicy, } from "./group-mentions.js"; import { - listMatrixAccountIds, - resolveDefaultMatrixAccountId, resolveMatrixAccount, resolveMatrixAccountConfig, type ResolvedMatrixAccount, } from "./matrix/accounts.js"; -import { formatMatrixErrorMessage } from "./matrix/errors.js"; -import { normalizeMatrixAllowList, normalizeMatrixUserId } from "./matrix/monitor/allowlist.js"; +import { normalizeMatrixUserId } from "./matrix/monitor/allowlist.js"; import type { MatrixProbe } from "./matrix/probe.js"; import { normalizeMatrixMessagingTarget, @@ -152,34 +147,6 @@ function projectMatrixConversationBinding(binding: { }; } -const matrixConfigAdapter = createScopedChannelConfigAdapter< - ResolvedMatrixAccount, - ReturnType, - CoreConfig ->({ - sectionKey: "matrix", - listAccountIds: listMatrixAccountIds, - resolveAccount: adaptScopedAccountAccessor(resolveMatrixAccount), - resolveAccessorAccount: ({ cfg, accountId }) => - resolveMatrixAccountConfig({ cfg: cfg, accountId }), - defaultAccountId: resolveDefaultMatrixAccountId, - clearBaseFields: [ - "name", - "homeserver", - "network", - "proxy", - "userId", - "accessToken", - "password", - "deviceId", - "deviceName", - "avatarUrl", - "initialSyncLimit", - ], - resolveAllowFrom: (account) => account.dm?.allowFrom, - formatAllowFrom: (allowFrom) => normalizeMatrixAllowList(allowFrom), -}); - const resolveMatrixDmPolicy = createScopedDmSecurityResolver({ channelKey: "matrix", resolvePolicy: (account) => account.config.dm?.policy, @@ -465,32 +432,20 @@ export const matrixPlugin: ChannelPlugin = collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("matrix", accounts), buildChannelSummary: ({ snapshot }) => buildProbeChannelStatusSummary(snapshot, { baseUrl: snapshot.baseUrl ?? null }), - probeAccount: async ({ account, timeoutMs, cfg }) => { - try { - const { probeMatrix, resolveMatrixAuth } = await loadMatrixChannelRuntime(); - const auth = await resolveMatrixAuth({ - cfg: cfg as CoreConfig, - accountId: account.accountId, - }); - return await probeMatrix({ - homeserver: auth.homeserver, - accessToken: auth.accessToken, - userId: auth.userId, - deviceId: auth.deviceId, - timeoutMs, - accountId: account.accountId, - allowPrivateNetwork: auth.allowPrivateNetwork, - ssrfPolicy: auth.ssrfPolicy, - dispatcherPolicy: auth.dispatcherPolicy, - }); - } catch (err) { - return { - ok: false, - error: formatMatrixErrorMessage(err), - elapsedMs: 0, - }; - } - }, + probeAccount: async ({ account, timeoutMs, cfg }) => + await createMatrixProbeAccount({ + resolveMatrixAuth: async ({ cfg, accountId }) => + (await loadMatrixChannelRuntime()).resolveMatrixAuth({ + cfg, + accountId, + }), + probeMatrix: async (params) => + await (await loadMatrixChannelRuntime()).probeMatrix(params), + })({ + account, + timeoutMs, + cfg, + }), resolveAccountSnapshot: ({ account, runtime }) => ({ accountId: account.accountId, name: account.name, @@ -562,15 +517,10 @@ export const matrixPlugin: ChannelPlugin = ), }, pairing: { - text: { - idLabel: "matrixUserId", - message: PAIRING_APPROVED_MESSAGE, - normalizeAllowEntry: createPairingPrefixStripper(/^matrix:/i), - notify: async ({ id, message, accountId }) => { - const { sendMessageMatrix } = await loadMatrixChannelRuntime(); - await sendMessageMatrix(`user:${id}`, message, accountId ? { accountId } : {}); - }, - }, + text: createMatrixPairingText( + async (to, message, options) => + await (await loadMatrixChannelRuntime()).sendMessageMatrix(to, message, options), + ), }, threading: { resolveReplyToMode: createScopedAccountReplyToModeResolver< diff --git a/extensions/matrix/src/config-adapter.ts b/extensions/matrix/src/config-adapter.ts new file mode 100644 index 00000000000..3d28e77fabc --- /dev/null +++ b/extensions/matrix/src/config-adapter.ts @@ -0,0 +1,43 @@ +import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id"; +import { + adaptScopedAccountAccessor, + createScopedChannelConfigAdapter, +} from "openclaw/plugin-sdk/channel-config-helpers"; +import { + listMatrixAccountIds, + resolveDefaultMatrixAccountId, + resolveMatrixAccount, + resolveMatrixAccountConfig, + type ResolvedMatrixAccount, +} from "./matrix/accounts.js"; +import { normalizeMatrixAllowList } from "./matrix/monitor/allowlist.js"; +import type { CoreConfig } from "./types.js"; + +export { DEFAULT_ACCOUNT_ID }; + +export const matrixConfigAdapter = createScopedChannelConfigAdapter< + ResolvedMatrixAccount, + ReturnType, + CoreConfig +>({ + sectionKey: "matrix", + listAccountIds: listMatrixAccountIds, + resolveAccount: adaptScopedAccountAccessor(resolveMatrixAccount), + resolveAccessorAccount: ({ cfg, accountId }) => resolveMatrixAccountConfig({ cfg, accountId }), + defaultAccountId: resolveDefaultMatrixAccountId, + clearBaseFields: [ + "name", + "homeserver", + "network", + "proxy", + "userId", + "accessToken", + "password", + "deviceId", + "deviceName", + "avatarUrl", + "initialSyncLimit", + ], + resolveAllowFrom: (account) => account.dm?.allowFrom, + formatAllowFrom: (allowFrom) => normalizeMatrixAllowList(allowFrom), +});