Skip to content

Commit 6b55642

Browse files
authored
test(agents): satisfy custom API model types
1 parent 831ba25 commit 6b55642

10 files changed

Lines changed: 452 additions & 90 deletions

extensions/matrix/src/matrix/monitor/inbound-dedupe.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,21 @@ function pruneSeenEvents(params: {
116116
}
117117
}
118118

119-
function createInboundDedupeStore(params: { env?: NodeJS.ProcessEnv; stateDir?: string }) {
120-
return getMatrixRuntime().state.openKeyedStore<StoredMatrixInboundDedupeEntry>({
119+
export function openMatrixInboundDedupeStoreOptions(params: {
120+
env?: NodeJS.ProcessEnv;
121+
stateDir?: string;
122+
}) {
123+
return {
121124
namespace: INBOUND_DEDUPE_NAMESPACE,
122125
maxEntries: DEFAULT_MAX_ENTRIES,
123126
env: resolveMatrixSqliteStateEnv(params),
124-
});
127+
};
128+
}
129+
130+
function createInboundDedupeStore(params: { env?: NodeJS.ProcessEnv; stateDir?: string }) {
131+
return getMatrixRuntime().state.openKeyedStore<StoredMatrixInboundDedupeEntry>(
132+
openMatrixInboundDedupeStoreOptions(params),
133+
);
125134
}
126135

127136
function createInboundDedupeMigrationStore(params: { env?: NodeJS.ProcessEnv; stateDir?: string }) {

extensions/matrix/test-api.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ export {
55
openMatrixIdbSnapshotStoreOptions,
66
openMatrixRecoveryKeyStoreOptions,
77
} from "./src/matrix/crypto-state-store.js";
8+
export {
9+
normalizeMatrixStorageMetadata,
10+
openMatrixStorageMetaStoreOptions,
11+
} from "./src/matrix/client/storage.js";
12+
export type { MatrixStorageMetadata } from "./src/matrix/client/storage.js";
13+
export { openMatrixInboundDedupeStoreOptions } from "./src/matrix/monitor/inbound-dedupe.js";
814
export type {
915
EncryptedFile,
1016
MatrixDeviceVerificationStatus,
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { mkdtemp, rm } from "node:fs/promises";
2+
import os from "node:os";
3+
import path from "node:path";
4+
import {
5+
createPluginStateSyncKeyedStoreForTests,
6+
resetPluginStateStoreForTests,
7+
} from "openclaw/plugin-sdk/plugin-state-test-runtime";
8+
import { afterEach, describe, expect, it } from "vitest";
9+
import { testing } from "./scenario-runtime-e2ee-destructive.js";
10+
11+
const storageMetadataRuntime = {
12+
normalizeMatrixStorageMetadata(value: unknown) {
13+
if (!value || typeof value !== "object") {
14+
return null;
15+
}
16+
const metadata = value as { deviceId?: unknown; userId?: unknown };
17+
return {
18+
...(typeof metadata.deviceId === "string" ? { deviceId: metadata.deviceId } : {}),
19+
...(typeof metadata.userId === "string" ? { userId: metadata.userId } : {}),
20+
};
21+
},
22+
openMatrixStorageMetaStoreOptions(storageRootDir: string) {
23+
return {
24+
namespace: "storage-meta",
25+
maxEntries: 10,
26+
env: { ...process.env, OPENCLAW_STATE_DIR: storageRootDir },
27+
};
28+
},
29+
};
30+
31+
describe("Matrix destructive E2EE storage discovery", () => {
32+
const tempDirs: string[] = [];
33+
34+
afterEach(async () => {
35+
resetPluginStateStoreForTests();
36+
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { force: true, recursive: true })));
37+
});
38+
39+
it("finds account metadata stored in account-local SQLite", async () => {
40+
const stateDir = await mkdtemp(path.join(os.tmpdir(), "matrix-qa-storage-"));
41+
tempDirs.push(stateDir);
42+
const accountRoot = path.join(stateDir, "matrix", "accounts", "stored-key", "server", "token");
43+
createPluginStateSyncKeyedStoreForTests(
44+
"matrix",
45+
storageMetadataRuntime.openMatrixStorageMetaStoreOptions(accountRoot),
46+
).register("current", {
47+
deviceId: "DEVICE",
48+
userId: "@owner:matrix-qa.test",
49+
});
50+
resetPluginStateStoreForTests();
51+
52+
await expect(
53+
testing.findMatrixQaCliAccountRoot({
54+
deviceId: "DEVICE",
55+
runtime: { stateDir },
56+
storageMetadataRuntime,
57+
userId: "@owner:matrix-qa.test",
58+
}),
59+
).resolves.toBe(accountRoot);
60+
});
61+
});

extensions/qa-matrix/src/runners/contract/scenario-runtime-e2ee-destructive.ts

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Qa Matrix plugin module implements scenario runtime e2ee destructive behavior.
22
import { randomUUID } from "node:crypto";
3-
import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
3+
import { access, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
44
import path from "node:path";
55
import { setTimeout as sleep } from "node:timers/promises";
66
import type { OpenKeyedStoreOptions } from "openclaw/plugin-sdk/plugin-state-runtime";
@@ -47,6 +47,11 @@ import type { MatrixQaScenarioExecution } from "./scenario-types.js";
4747

4848
type MatrixQaCliRuntime = Awaited<ReturnType<typeof createMatrixQaOpenClawCliRuntime>>;
4949

50+
type MatrixQaStorageMetadataRuntime = Pick<
51+
Awaited<ReturnType<typeof loadMatrixQaE2eeRuntime>>,
52+
"normalizeMatrixStorageMetadata" | "openMatrixStorageMetaStoreOptions"
53+
>;
54+
5055
type MatrixQaCliBackupStatus = {
5156
backup?: {
5257
decryptionKeyCached?: boolean | null;
@@ -503,24 +508,59 @@ async function findFilesByName(params: { filename: string; rootDir: string }): P
503508

504509
async function findMatrixQaCliAccountRoot(params: {
505510
deviceId: string;
506-
runtime: MatrixQaCliRuntime;
511+
runtime: Pick<MatrixQaCliRuntime, "stateDir">;
512+
storageMetadataRuntime?: MatrixQaStorageMetadataRuntime;
507513
userId: string;
508514
}) {
509-
const metadataPaths = await findFilesByName({
515+
const storageMetadataRuntime = params.storageMetadataRuntime ?? (await loadMatrixQaE2eeRuntime());
516+
const sqlitePaths = await findFilesByName({
517+
filename: "openclaw.sqlite",
518+
rootDir: params.runtime.stateDir,
519+
});
520+
const legacyMetadataPaths = await findFilesByName({
510521
filename: "storage-meta.json",
511522
rootDir: params.runtime.stateDir,
512523
});
513-
for (const metadataPath of metadataPaths) {
524+
// Current account metadata lives in account-local SQLite. Keep legacy JSON
525+
// discovery for older tagged fixtures without making it the canonical path.
526+
const accountRoots = new Set(
527+
sqlitePaths
528+
.filter((sqlitePath) => path.basename(path.dirname(sqlitePath)) === "state")
529+
.map((sqlitePath) => path.dirname(path.dirname(sqlitePath))),
530+
);
531+
for (const metadataPath of legacyMetadataPaths) {
532+
accountRoots.add(path.dirname(metadataPath));
533+
}
534+
for (const accountRoot of [...accountRoots].toSorted()) {
535+
let metadata: { deviceId?: unknown; userId?: unknown } | null = null;
514536
try {
515-
const metadata = JSON.parse(await readFile(metadataPath, "utf8")) as {
516-
deviceId?: unknown;
517-
userId?: unknown;
518-
};
519-
if (metadata.userId === params.userId && metadata.deviceId === params.deviceId) {
520-
return path.dirname(metadataPath);
537+
await access(path.join(accountRoot, "state", "openclaw.sqlite"));
538+
try {
539+
const store = createPluginStateSyncKeyedStoreForTests<unknown>(
540+
"matrix",
541+
storageMetadataRuntime.openMatrixStorageMetaStoreOptions(accountRoot),
542+
);
543+
metadata = storageMetadataRuntime.normalizeMatrixStorageMetadata(store.lookup("current"));
544+
} finally {
545+
resetPluginStateStoreForTests();
521546
}
522547
} catch {
523-
continue;
548+
// Fall through to the legacy sidecar for pre-SQLite fixtures.
549+
}
550+
if (!metadata) {
551+
try {
552+
metadata = JSON.parse(
553+
await readFile(path.join(accountRoot, "storage-meta.json"), "utf8"),
554+
) as {
555+
deviceId?: unknown;
556+
userId?: unknown;
557+
};
558+
} catch {
559+
continue;
560+
}
561+
}
562+
if (metadata.userId === params.userId && metadata.deviceId === params.deviceId) {
563+
return accountRoot;
524564
}
525565
}
526566
throw new Error(`Matrix CLI account storage root was not created for ${params.userId}`);
@@ -1685,3 +1725,7 @@ export async function runMatrixQaE2eeHistoryExistsBackupEmptyScenario(
16851725
await cleanupMatrixQaTempDevices(setup.owner, [device.deviceId]);
16861726
}
16871727
}
1728+
1729+
export const testing = {
1730+
findMatrixQaCliAccountRoot,
1731+
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { createHash } from "node:crypto";
2+
import { mkdtemp, rm } from "node:fs/promises";
3+
import os from "node:os";
4+
import path from "node:path";
5+
import {
6+
createPluginStateSyncKeyedStoreForTests,
7+
resetPluginStateStoreForTests,
8+
} from "openclaw/plugin-sdk/plugin-state-test-runtime";
9+
import { afterEach, describe, expect, it } from "vitest";
10+
import type { MatrixQaScenarioContext } from "./scenario-runtime-shared.js";
11+
import { waitForMatrixInboundDedupeEntry } from "./scenario-runtime-state-files.js";
12+
13+
const dedupeStoreRuntime = {
14+
openMatrixInboundDedupeStoreOptions(params: { stateDir?: string }) {
15+
return {
16+
namespace: "inbound-dedupe",
17+
maxEntries: 20_000,
18+
env: { ...process.env, OPENCLAW_STATE_DIR: params.stateDir },
19+
};
20+
},
21+
};
22+
23+
function buildDedupeKey(params: { accountId: string; eventId: string; roomId: string }) {
24+
return `${params.accountId}:${createHash("sha256")
25+
.update(params.accountId)
26+
.update("\0")
27+
.update(params.roomId)
28+
.update("\0")
29+
.update(params.eventId)
30+
.digest("hex")}`;
31+
}
32+
33+
describe("Matrix QA persisted state probes", () => {
34+
const tempDirs: string[] = [];
35+
36+
afterEach(async () => {
37+
resetPluginStateStoreForTests();
38+
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { force: true, recursive: true })));
39+
});
40+
41+
it("observes inbound dedupe entries through the canonical plugin-state store", async () => {
42+
const stateDir = await mkdtemp(path.join(os.tmpdir(), "matrix-qa-dedupe-"));
43+
tempDirs.push(stateDir);
44+
const accountRoot = path.join(stateDir, "matrix", "accounts", "sut", "server", "token");
45+
const accountId = "sut";
46+
const eventId = "$event";
47+
const roomId = "!room:matrix-qa.test";
48+
const options = dedupeStoreRuntime.openMatrixInboundDedupeStoreOptions({
49+
stateDir: accountRoot,
50+
});
51+
const runtimeAccountId = "runtime-default";
52+
createPluginStateSyncKeyedStoreForTests("matrix", options).register(
53+
buildDedupeKey({ accountId: runtimeAccountId, eventId, roomId }),
54+
{ eventId, roomId, ts: Date.now() },
55+
);
56+
resetPluginStateStoreForTests();
57+
58+
await expect(
59+
waitForMatrixInboundDedupeEntry({
60+
context: { sutAccountId: accountId } as MatrixQaScenarioContext,
61+
dedupeStoreRuntime,
62+
eventId,
63+
roomId,
64+
stateDir,
65+
timeoutMs: 1_000,
66+
}),
67+
).resolves.toBe(path.join(accountRoot, "state", "openclaw.sqlite"));
68+
});
69+
});

0 commit comments

Comments
 (0)