|
1 | 1 | // Qa Matrix plugin module implements scenario runtime e2ee destructive behavior. |
2 | 2 | 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"; |
4 | 4 | import path from "node:path"; |
5 | 5 | import { setTimeout as sleep } from "node:timers/promises"; |
6 | 6 | import type { OpenKeyedStoreOptions } from "openclaw/plugin-sdk/plugin-state-runtime"; |
@@ -47,6 +47,11 @@ import type { MatrixQaScenarioExecution } from "./scenario-types.js"; |
47 | 47 |
|
48 | 48 | type MatrixQaCliRuntime = Awaited<ReturnType<typeof createMatrixQaOpenClawCliRuntime>>; |
49 | 49 |
|
| 50 | +type MatrixQaStorageMetadataRuntime = Pick< |
| 51 | + Awaited<ReturnType<typeof loadMatrixQaE2eeRuntime>>, |
| 52 | + "normalizeMatrixStorageMetadata" | "openMatrixStorageMetaStoreOptions" |
| 53 | +>; |
| 54 | + |
50 | 55 | type MatrixQaCliBackupStatus = { |
51 | 56 | backup?: { |
52 | 57 | decryptionKeyCached?: boolean | null; |
@@ -503,24 +508,59 @@ async function findFilesByName(params: { filename: string; rootDir: string }): P |
503 | 508 |
|
504 | 509 | async function findMatrixQaCliAccountRoot(params: { |
505 | 510 | deviceId: string; |
506 | | - runtime: MatrixQaCliRuntime; |
| 511 | + runtime: Pick<MatrixQaCliRuntime, "stateDir">; |
| 512 | + storageMetadataRuntime?: MatrixQaStorageMetadataRuntime; |
507 | 513 | userId: string; |
508 | 514 | }) { |
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({ |
510 | 521 | filename: "storage-meta.json", |
511 | 522 | rootDir: params.runtime.stateDir, |
512 | 523 | }); |
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; |
514 | 536 | 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(); |
521 | 546 | } |
522 | 547 | } 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; |
524 | 564 | } |
525 | 565 | } |
526 | 566 | throw new Error(`Matrix CLI account storage root was not created for ${params.userId}`); |
@@ -1685,3 +1725,7 @@ export async function runMatrixQaE2eeHistoryExistsBackupEmptyScenario( |
1685 | 1725 | await cleanupMatrixQaTempDevices(setup.owner, [device.deviceId]); |
1686 | 1726 | } |
1687 | 1727 | } |
| 1728 | + |
| 1729 | +export const testing = { |
| 1730 | + findMatrixQaCliAccountRoot, |
| 1731 | +}; |
0 commit comments