test: speed up matrix channel seam tests

This commit is contained in:
Peter Steinberger
2026-04-07 13:58:16 +01:00
parent 60199fbee3
commit 4c67991f43
5 changed files with 172 additions and 78 deletions

View File

@@ -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<MatrixProbe>;
type SendMessageMatrix = (
to: string,
message: string,
options?: { accountId?: string },
) => Promise<unknown>;
export function createMatrixProbeAccount(params: {
resolveMatrixAuth: ResolveMatrixAuth;
probeMatrix: ProbeMatrix;
}) {
return async ({
account,
timeoutMs,
cfg,
}: {
account: { accountId?: string };
timeoutMs?: number;
cfg: unknown;
}): Promise<MatrixProbe> => {
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 } : {});
},
};
}

View File

@@ -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: {

View File

@@ -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: {

View File

@@ -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<typeof resolveMatrixAccountConfig>,
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<ResolvedMatrixAccount>({
channelKey: "matrix",
resolvePolicy: (account) => account.config.dm?.policy,
@@ -465,32 +432,20 @@ export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount, MatrixProbe> =
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<ResolvedMatrixAccount, MatrixProbe> =
),
},
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<

View File

@@ -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<typeof resolveMatrixAccountConfig>,
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),
});