test: share matrix runtime fixtures

This commit is contained in:
Peter Steinberger
2026-04-20 20:43:26 +01:00
parent 8595e6c872
commit 1bd92975c2
3 changed files with 118 additions and 208 deletions

View File

@@ -35,6 +35,23 @@ describe("matrix doctor", () => {
vi.clearAllMocks();
});
function normalizeMatrixDmConfig(dm: Record<string, unknown>) {
const normalize = matrixDoctor.normalizeCompatibilityConfig;
expect(normalize).toBeDefined();
if (!normalize) {
throw new Error("expected Matrix doctor compatibility normalizer");
}
return normalize({
cfg: {
channels: {
matrix: {
dm,
},
},
} as never,
});
}
it("formats state and crypto previews", () => {
expect(
formatMatrixLegacyStatePreview({
@@ -283,24 +300,10 @@ describe("matrix doctor", () => {
// so they must not count toward the allowFrom population check — otherwise
// the migration would emit policy="allowlist" with an effectively empty
// allowlist, silently blocking all DMs.
const normalize = matrixDoctor.normalizeCompatibilityConfig;
expect(normalize).toBeDefined();
if (!normalize) {
return;
}
const result = normalize({
cfg: {
channels: {
matrix: {
dm: {
enabled: true,
policy: "trusted",
allowFrom: [" ", "\t", ""],
},
},
},
} as never,
const result = normalizeMatrixDmConfig({
enabled: true,
policy: "trusted",
allowFrom: [" ", "\t", ""],
});
const matrixDm = (result.config.channels?.matrix as { dm?: { policy?: string } })?.dm;
@@ -313,23 +316,9 @@ describe("matrix doctor", () => {
});
it("migrates legacy channels.matrix.dm.policy 'trusted' without allowFrom to 'pairing'", () => {
const normalize = matrixDoctor.normalizeCompatibilityConfig;
expect(normalize).toBeDefined();
if (!normalize) {
return;
}
const result = normalize({
cfg: {
channels: {
matrix: {
dm: {
enabled: true,
policy: "trusted",
},
},
},
} as never,
const result = normalizeMatrixDmConfig({
enabled: true,
policy: "trusted",
});
const matrixDm = (result.config.channels?.matrix as { dm?: { policy?: string } })?.dm;

View File

@@ -22,6 +22,34 @@ import { resolveMatrixAccountStorageRoot } from "./storage-paths.js";
const createBackupArchiveMock = vi.hoisted(() => vi.fn());
const MATRIX_CREDENTIALS = {
homeserver: "https://matrix.example.org",
userId: "@bot:example.org",
accessToken: "tok-123",
} as const;
function makeMatrixMigrationConfig() {
return {
channels: {
matrix: MATRIX_CREDENTIALS,
},
} as never;
}
function seedLegacyMatrixCrypto(home: string) {
const stateDir = path.join(home, ".openclaw");
const { rootDir } = resolveMatrixAccountStorageRoot({
stateDir,
...MATRIX_CREDENTIALS,
});
fs.mkdirSync(path.join(rootDir, "crypto"), { recursive: true });
fs.writeFileSync(
path.join(rootDir, "crypto", "bot-sdk.json"),
JSON.stringify({ deviceId: "DEVICE123" }),
"utf8",
);
}
describe("matrix migration snapshots", () => {
beforeEach(() => {
createBackupArchiveMock.mockReset();
@@ -96,29 +124,8 @@ describe("matrix migration snapshots", () => {
it("treats legacy Matrix crypto as actionable when the extension inspector is present", async () => {
await withTempHome(async (home) => {
const stateDir = path.join(home, ".openclaw");
const { rootDir } = resolveMatrixAccountStorageRoot({
stateDir,
homeserver: "https://matrix.example.org",
userId: "@bot:example.org",
accessToken: "tok-123",
});
fs.mkdirSync(path.join(rootDir, "crypto"), { recursive: true });
fs.writeFileSync(
path.join(rootDir, "crypto", "bot-sdk.json"),
JSON.stringify({ deviceId: "DEVICE123" }),
"utf8",
);
const cfg = {
channels: {
matrix: {
homeserver: "https://matrix.example.org",
userId: "@bot:example.org",
accessToken: "tok-123",
},
},
} as never;
seedLegacyMatrixCrypto(home);
const cfg = makeMatrixMigrationConfig();
const detection = detectLegacyMatrixCrypto({
cfg,
@@ -140,29 +147,8 @@ describe("matrix migration snapshots", () => {
legacyCryptoInspectorAvailability.available = false;
await withTempHome(async (home) => {
const stateDir = path.join(home, ".openclaw");
const { rootDir } = resolveMatrixAccountStorageRoot({
stateDir,
homeserver: "https://matrix.example.org",
userId: "@bot:example.org",
accessToken: "tok-123",
});
fs.mkdirSync(path.join(rootDir, "crypto"), { recursive: true });
fs.writeFileSync(
path.join(rootDir, "crypto", "bot-sdk.json"),
JSON.stringify({ deviceId: "DEVICE123" }),
"utf8",
);
const cfg = {
channels: {
matrix: {
homeserver: "https://matrix.example.org",
userId: "@bot:example.org",
accessToken: "tok-123",
},
},
} as never;
seedLegacyMatrixCrypto(home);
const cfg = makeMatrixMigrationConfig();
const detection = detectLegacyMatrixCrypto({
cfg,

View File

@@ -143,6 +143,51 @@ function writeTrustedOpenClawBinFixture(
writeFixtureFile(fixtureRoot, "dist/plugin-sdk/group-access.js", "export {};\n");
}
function writeSourceRuntimeWrapperFixture(fixtureRoot: string) {
writeFixtureFile(
fixtureRoot,
"extensions/matrix/src/plugin-entry.runtime.js",
MATRIX_RUNTIME_WRAPPER_SOURCE,
);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/plugin-entry.handlers.runtime.js",
PACKAGED_RUNTIME_STUB,
);
}
function importFixtureModule(fixtureRoot: string, relativePath: string) {
const wrapperUrl = pathToFileURL(path.join(fixtureRoot, relativePath));
return import(`${wrapperUrl.href}?t=${Date.now()}`);
}
function expectRuntimeWrapperExports(mod: unknown) {
expect(mod).toMatchObject({
ensureMatrixCryptoRuntime: expect.any(Function),
handleVerifyRecoveryKey: expect.any(Function),
handleVerificationBootstrap: expect.any(Function),
handleVerificationStatus: expect.any(Function),
});
}
function writeCapturingSourceRuntimeWrapperFixture(fixtureRoot: string) {
delete matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions;
writeOpenClawAliasFixture(fixtureRoot);
writeCapturingJitiFixture(fixtureRoot);
writeSourceRuntimeWrapperFixture(fixtureRoot);
}
function expectSourcePluginSdkAliases(fixtureRoot: string) {
expect(matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions).toMatchObject({
alias: {
[PLUGIN_SDK_ROOT]: path.join(fixtureRoot, "src", "plugin-sdk", "root-alias.cjs"),
[SCOPED_PLUGIN_SDK_ROOT]: path.join(fixtureRoot, "src", "plugin-sdk", "root-alias.cjs"),
[GROUP_ACCESS_SUBPATH]: path.join(fixtureRoot, "src", "plugin-sdk", "group-access.ts"),
[SCOPED_GROUP_ACCESS_SUBPATH]: path.join(fixtureRoot, "src", "plugin-sdk", "group-access.ts"),
},
});
}
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
fs.rmSync(dir, { recursive: true, force: true });
@@ -154,28 +199,11 @@ it("loads the source-checkout runtime wrapper through native ESM import", async
writeOpenClawPackageFixture(fixtureRoot);
writeJitiFixture(fixtureRoot);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/src/plugin-entry.runtime.js",
MATRIX_RUNTIME_WRAPPER_SOURCE,
);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/plugin-entry.handlers.runtime.js",
PACKAGED_RUNTIME_STUB,
);
writeSourceRuntimeWrapperFixture(fixtureRoot);
const wrapperUrl = pathToFileURL(
path.join(fixtureRoot, "extensions", "matrix", "src", "plugin-entry.runtime.js"),
expectRuntimeWrapperExports(
await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js"),
);
const mod = await import(`${wrapperUrl.href}?t=${Date.now()}`);
expect(mod).toMatchObject({
ensureMatrixCryptoRuntime: expect.any(Function),
handleVerifyRecoveryKey: expect.any(Function),
handleVerificationBootstrap: expect.any(Function),
handleVerificationStatus: expect.any(Function),
});
}, 240_000);
it("loads the packaged runtime wrapper without recursing through the stable root alias", async () => {
@@ -199,73 +227,26 @@ it("loads the packaged runtime wrapper without recursing through the stable root
PACKAGED_RUNTIME_STUB,
);
const wrapperUrl = pathToFileURL(
path.join(fixtureRoot, "dist", "plugin-entry.runtime-C88YIa_v.js"),
expectRuntimeWrapperExports(
await importFixtureModule(fixtureRoot, "dist/plugin-entry.runtime-C88YIa_v.js"),
);
const mod = await import(`${wrapperUrl.href}?t=${Date.now()}`);
expect(mod).toMatchObject({
ensureMatrixCryptoRuntime: expect.any(Function),
handleVerifyRecoveryKey: expect.any(Function),
handleVerificationBootstrap: expect.any(Function),
handleVerificationStatus: expect.any(Function),
});
}, 240_000);
it("builds scoped and unscoped plugin-sdk aliases for the wrapper jiti loader", async () => {
const fixtureRoot = makeFixtureRoot(".tmp-matrix-runtime-aliases-");
delete matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions;
writeOpenClawAliasFixture(fixtureRoot);
writeCapturingJitiFixture(fixtureRoot);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/src/plugin-entry.runtime.js",
MATRIX_RUNTIME_WRAPPER_SOURCE,
);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/plugin-entry.handlers.runtime.js",
PACKAGED_RUNTIME_STUB,
);
writeCapturingSourceRuntimeWrapperFixture(fixtureRoot);
await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js");
const wrapperUrl = pathToFileURL(
path.join(fixtureRoot, "extensions", "matrix", "src", "plugin-entry.runtime.js"),
);
await import(`${wrapperUrl.href}?t=${Date.now()}`);
expect(matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions).toMatchObject({
alias: {
[PLUGIN_SDK_ROOT]: path.join(fixtureRoot, "src", "plugin-sdk", "root-alias.cjs"),
[SCOPED_PLUGIN_SDK_ROOT]: path.join(fixtureRoot, "src", "plugin-sdk", "root-alias.cjs"),
[GROUP_ACCESS_SUBPATH]: path.join(fixtureRoot, "src", "plugin-sdk", "group-access.ts"),
[SCOPED_GROUP_ACCESS_SUBPATH]: path.join(fixtureRoot, "src", "plugin-sdk", "group-access.ts"),
},
});
expectSourcePluginSdkAliases(fixtureRoot);
}, 240_000);
it("resolves extension-api aliases through the same source extension family", async () => {
const fixtureRoot = makeFixtureRoot(".tmp-matrix-runtime-extension-api-");
delete matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions;
writeOpenClawAliasFixture(fixtureRoot);
writeFixtureFile(fixtureRoot, "src/extensionAPI.mts", "export {};\n");
writeCapturingJitiFixture(fixtureRoot);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/src/plugin-entry.runtime.js",
MATRIX_RUNTIME_WRAPPER_SOURCE,
);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/plugin-entry.handlers.runtime.js",
PACKAGED_RUNTIME_STUB,
);
const wrapperUrl = pathToFileURL(
path.join(fixtureRoot, "extensions", "matrix", "src", "plugin-entry.runtime.js"),
);
await import(`${wrapperUrl.href}?t=${Date.now()}`);
writeCapturingSourceRuntimeWrapperFixture(fixtureRoot);
await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js");
expect(matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions).toMatchObject({
alias: {
@@ -286,21 +267,8 @@ it("keeps wrapper plugin-sdk aliases deterministic and ignores unsafe subpaths",
writeFixtureFile(fixtureRoot, "src/plugin-sdk/alpha.ts", "export {};\n");
writeFixtureFile(fixtureRoot, "src/plugin-sdk/zeta.ts", "export {};\n");
writeCapturingJitiFixture(fixtureRoot);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/src/plugin-entry.runtime.js",
MATRIX_RUNTIME_WRAPPER_SOURCE,
);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/plugin-entry.handlers.runtime.js",
PACKAGED_RUNTIME_STUB,
);
const wrapperUrl = pathToFileURL(
path.join(fixtureRoot, "extensions", "matrix", "src", "plugin-entry.runtime.js"),
);
await import(`${wrapperUrl.href}?t=${Date.now()}`);
writeSourceRuntimeWrapperFixture(fixtureRoot);
await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js");
const aliasKeys = Object.keys(
(
@@ -349,30 +317,10 @@ it("ignores nearby untrusted openclaw package stubs when resolving the wrapper r
);
writeFixtureFile(fixtureRoot, "extensions/src/plugin-sdk/group-access.ts", "export {};\n");
writeCapturingJitiFixture(fixtureRoot);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/src/plugin-entry.runtime.js",
MATRIX_RUNTIME_WRAPPER_SOURCE,
);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/plugin-entry.handlers.runtime.js",
PACKAGED_RUNTIME_STUB,
);
writeSourceRuntimeWrapperFixture(fixtureRoot);
await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js");
const wrapperUrl = pathToFileURL(
path.join(fixtureRoot, "extensions", "matrix", "src", "plugin-entry.runtime.js"),
);
await import(`${wrapperUrl.href}?t=${Date.now()}`);
expect(matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions).toMatchObject({
alias: {
[PLUGIN_SDK_ROOT]: path.join(fixtureRoot, "src", "plugin-sdk", "root-alias.cjs"),
[SCOPED_PLUGIN_SDK_ROOT]: path.join(fixtureRoot, "src", "plugin-sdk", "root-alias.cjs"),
[GROUP_ACCESS_SUBPATH]: path.join(fixtureRoot, "src", "plugin-sdk", "group-access.ts"),
[SCOPED_GROUP_ACCESS_SUBPATH]: path.join(fixtureRoot, "src", "plugin-sdk", "group-access.ts"),
},
});
expectSourcePluginSdkAliases(fixtureRoot);
}, 240_000);
it("treats string bin hints case-insensitively when trusting wrapper package roots", async () => {
@@ -381,21 +329,8 @@ it("treats string bin hints case-insensitively when trusting wrapper package roo
delete matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions;
writeTrustedOpenClawBinFixture(fixtureRoot, "OpenClaw.MJS");
writeCapturingJitiFixture(fixtureRoot);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/src/plugin-entry.runtime.js",
MATRIX_RUNTIME_WRAPPER_SOURCE,
);
writeFixtureFile(
fixtureRoot,
"extensions/matrix/plugin-entry.handlers.runtime.js",
PACKAGED_RUNTIME_STUB,
);
const wrapperUrl = pathToFileURL(
path.join(fixtureRoot, "extensions", "matrix", "src", "plugin-entry.runtime.js"),
);
await import(`${wrapperUrl.href}?t=${Date.now()}`);
writeSourceRuntimeWrapperFixture(fixtureRoot);
await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js");
expect(matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions).toMatchObject({
alias: {