test(matrix): isolate IndexedDB persistence fixtures

This commit is contained in:
Vincent Koc
2026-05-03 03:18:00 -07:00
parent 4545a0ed61
commit ae87f7800b
3 changed files with 37 additions and 24 deletions

View File

@@ -29,6 +29,8 @@ let persistIdbToDisk: typeof import("./idb-persistence.js").persistIdbToDisk;
let restoreIdbFromDisk: typeof import("./idb-persistence.js").restoreIdbFromDisk;
type CapturedLockOptions =
typeof import("./idb-persistence-lock.js").MATRIX_IDB_SNAPSHOT_LOCK_OPTIONS;
const DATABASE_PREFIX = "openclaw-matrix-lock-order-test";
const cryptoDatabaseName = `${DATABASE_PREFIX}::matrix-sdk-crypto`;
beforeAll(async () => {
({ persistIdbToDisk, restoreIdbFromDisk } = await import("./idb-persistence.js"));
@@ -43,33 +45,32 @@ describe("Matrix IndexedDB persistence lock ordering", () => {
withFileLockMock.mockImplementation(
async <T>(_filePath: string, _options: unknown, fn: () => Promise<T>) => await fn(),
);
await clearAllIndexedDbState();
await clearAllIndexedDbState({ databasePrefix: DATABASE_PREFIX });
});
afterEach(async () => {
await clearAllIndexedDbState();
await clearAllIndexedDbState({ databasePrefix: DATABASE_PREFIX });
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it("captures the snapshot after the file lock is acquired", async () => {
const snapshotPath = path.join(tmpDir, "crypto-idb-snapshot.json");
const dbName = "openclaw-matrix-test::matrix-sdk-crypto";
await seedDatabase({
name: dbName,
name: cryptoDatabaseName,
storeName: "sessions",
records: [{ key: "room-1", value: { session: "old-session" } }],
});
withFileLockMock.mockImplementationOnce(async (_filePath, _options, fn) => {
await seedDatabase({
name: dbName,
name: cryptoDatabaseName,
storeName: "sessions",
records: [{ key: "room-1", value: { session: "new-session" } }],
});
return await fn();
});
await persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" });
await persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX });
const data = JSON.parse(fs.readFileSync(snapshotPath, "utf8")) as Array<{
stores: Array<{
@@ -89,7 +90,7 @@ describe("Matrix IndexedDB persistence lock ordering", () => {
capturedOptions.push(options as CapturedLockOptions);
return 0;
});
await persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" });
await persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX });
fs.writeFileSync(snapshotPath, "[]", "utf8");
withFileLockMock.mockImplementationOnce(async (_filePath, options) => {

View File

@@ -1,9 +1,11 @@
export async function clearAllIndexedDbState(): Promise<void> {
export async function clearAllIndexedDbState(params?: { databasePrefix?: string }): Promise<void> {
const databases = await indexedDB.databases();
const expectedPrefix = params?.databasePrefix ? `${params.databasePrefix}::` : null;
await Promise.all(
databases
.map((entry) => entry.name)
.filter((name): name is string => Boolean(name))
.filter((name) => !expectedPrefix || name.startsWith(expectedPrefix))
.map(
(name) =>
new Promise<void>((resolve, reject) => {

View File

@@ -15,6 +15,16 @@ import {
} from "./idb-persistence.test-helpers.js";
import { LogService } from "./logger.js";
const DATABASE_PREFIX = "openclaw-matrix-persistence-test";
const OTHER_DATABASE_PREFIX = "openclaw-matrix-persistence-other-test";
const cryptoDatabaseName = `${DATABASE_PREFIX}::matrix-sdk-crypto`;
const otherCryptoDatabaseName = `${OTHER_DATABASE_PREFIX}::matrix-sdk-crypto`;
async function clearTestIndexedDbState(): Promise<void> {
await clearAllIndexedDbState({ databasePrefix: DATABASE_PREFIX });
await clearAllIndexedDbState({ databasePrefix: OTHER_DATABASE_PREFIX });
}
describe("Matrix IndexedDB persistence", () => {
let tmpDir: string;
let warnSpy: ReturnType<typeof vi.spyOn>;
@@ -22,12 +32,12 @@ describe("Matrix IndexedDB persistence", () => {
beforeEach(async () => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "matrix-idb-persist-"));
warnSpy = vi.spyOn(LogService, "warn").mockImplementation(() => {});
await clearAllIndexedDbState();
await clearTestIndexedDbState();
});
afterEach(async () => {
warnSpy.mockRestore();
await clearAllIndexedDbState();
await clearTestIndexedDbState();
resetFileLockStateForTest();
fs.rmSync(tmpDir, { recursive: true, force: true });
});
@@ -35,38 +45,38 @@ describe("Matrix IndexedDB persistence", () => {
it("persists and restores database contents for the selected prefix", async () => {
const snapshotPath = path.join(tmpDir, "crypto-idb-snapshot.json");
await seedDatabase({
name: "openclaw-matrix-test::matrix-sdk-crypto",
name: cryptoDatabaseName,
storeName: "sessions",
records: [{ key: "room-1", value: { session: "abc123" } }],
});
await seedDatabase({
name: "other-prefix::matrix-sdk-crypto",
name: otherCryptoDatabaseName,
storeName: "sessions",
records: [{ key: "room-2", value: { session: "should-not-restore" } }],
});
await persistIdbToDisk({
snapshotPath,
databasePrefix: "openclaw-matrix-test",
databasePrefix: DATABASE_PREFIX,
});
expect(fs.existsSync(snapshotPath)).toBe(true);
const mode = fs.statSync(snapshotPath).mode & 0o777;
expect(mode).toBe(0o600);
await clearAllIndexedDbState();
await clearTestIndexedDbState();
const restored = await restoreIdbFromDisk(snapshotPath);
expect(restored).toBe(true);
const restoredRecords = await readDatabaseRecords({
name: "openclaw-matrix-test::matrix-sdk-crypto",
name: cryptoDatabaseName,
storeName: "sessions",
});
expect(restoredRecords).toEqual([{ key: "room-1", value: { session: "abc123" } }]);
const dbs = await indexedDB.databases();
expect(dbs.some((entry) => entry.name === "other-prefix::matrix-sdk-crypto")).toBe(false);
expect(dbs.some((entry) => entry.name === otherCryptoDatabaseName)).toBe(false);
});
it("returns false and logs a warning for malformed snapshots", async () => {
@@ -103,14 +113,14 @@ describe("Matrix IndexedDB persistence", () => {
it("serializes concurrent persist operations via file lock", async () => {
const snapshotPath = path.join(tmpDir, "concurrent-persist.json");
await seedDatabase({
name: "openclaw-matrix-test::matrix-sdk-crypto",
name: cryptoDatabaseName,
storeName: "sessions",
records: [{ key: "room-1", value: { session: "abc123" } }],
});
await Promise.all([
persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" }),
persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" }),
persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX }),
persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX }),
]);
expect(fs.existsSync(snapshotPath)).toBe(true);
@@ -123,12 +133,12 @@ describe("Matrix IndexedDB persistence", () => {
it("releases lock after persist completes", async () => {
const snapshotPath = path.join(tmpDir, "lock-release.json");
await seedDatabase({
name: "openclaw-matrix-test::matrix-sdk-crypto",
name: cryptoDatabaseName,
storeName: "sessions",
records: [{ key: "room-1", value: { session: "abc123" } }],
});
await persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" });
await persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX });
const lockPath = `${snapshotPath}.lock`;
expect(fs.existsSync(lockPath)).toBe(false);
@@ -138,13 +148,13 @@ describe("Matrix IndexedDB persistence", () => {
it("releases lock after restore completes", async () => {
const snapshotPath = path.join(tmpDir, "lock-release-restore.json");
await seedDatabase({
name: "openclaw-matrix-test::matrix-sdk-crypto",
name: cryptoDatabaseName,
storeName: "sessions",
records: [{ key: "room-1", value: { session: "abc123" } }],
});
await persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" });
await clearAllIndexedDbState();
await persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX });
await clearTestIndexedDbState();
await drainFileLockStateForTest();
await restoreIdbFromDisk(snapshotPath);