mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 20:40:43 +00:00
test(matrix): isolate IndexedDB persistence fixtures
This commit is contained in:
@@ -29,6 +29,8 @@ let persistIdbToDisk: typeof import("./idb-persistence.js").persistIdbToDisk;
|
|||||||
let restoreIdbFromDisk: typeof import("./idb-persistence.js").restoreIdbFromDisk;
|
let restoreIdbFromDisk: typeof import("./idb-persistence.js").restoreIdbFromDisk;
|
||||||
type CapturedLockOptions =
|
type CapturedLockOptions =
|
||||||
typeof import("./idb-persistence-lock.js").MATRIX_IDB_SNAPSHOT_LOCK_OPTIONS;
|
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 () => {
|
beforeAll(async () => {
|
||||||
({ persistIdbToDisk, restoreIdbFromDisk } = await import("./idb-persistence.js"));
|
({ persistIdbToDisk, restoreIdbFromDisk } = await import("./idb-persistence.js"));
|
||||||
@@ -43,33 +45,32 @@ describe("Matrix IndexedDB persistence lock ordering", () => {
|
|||||||
withFileLockMock.mockImplementation(
|
withFileLockMock.mockImplementation(
|
||||||
async <T>(_filePath: string, _options: unknown, fn: () => Promise<T>) => await fn(),
|
async <T>(_filePath: string, _options: unknown, fn: () => Promise<T>) => await fn(),
|
||||||
);
|
);
|
||||||
await clearAllIndexedDbState();
|
await clearAllIndexedDbState({ databasePrefix: DATABASE_PREFIX });
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await clearAllIndexedDbState();
|
await clearAllIndexedDbState({ databasePrefix: DATABASE_PREFIX });
|
||||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("captures the snapshot after the file lock is acquired", async () => {
|
it("captures the snapshot after the file lock is acquired", async () => {
|
||||||
const snapshotPath = path.join(tmpDir, "crypto-idb-snapshot.json");
|
const snapshotPath = path.join(tmpDir, "crypto-idb-snapshot.json");
|
||||||
const dbName = "openclaw-matrix-test::matrix-sdk-crypto";
|
|
||||||
await seedDatabase({
|
await seedDatabase({
|
||||||
name: dbName,
|
name: cryptoDatabaseName,
|
||||||
storeName: "sessions",
|
storeName: "sessions",
|
||||||
records: [{ key: "room-1", value: { session: "old-session" } }],
|
records: [{ key: "room-1", value: { session: "old-session" } }],
|
||||||
});
|
});
|
||||||
|
|
||||||
withFileLockMock.mockImplementationOnce(async (_filePath, _options, fn) => {
|
withFileLockMock.mockImplementationOnce(async (_filePath, _options, fn) => {
|
||||||
await seedDatabase({
|
await seedDatabase({
|
||||||
name: dbName,
|
name: cryptoDatabaseName,
|
||||||
storeName: "sessions",
|
storeName: "sessions",
|
||||||
records: [{ key: "room-1", value: { session: "new-session" } }],
|
records: [{ key: "room-1", value: { session: "new-session" } }],
|
||||||
});
|
});
|
||||||
return await fn();
|
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<{
|
const data = JSON.parse(fs.readFileSync(snapshotPath, "utf8")) as Array<{
|
||||||
stores: Array<{
|
stores: Array<{
|
||||||
@@ -89,7 +90,7 @@ describe("Matrix IndexedDB persistence lock ordering", () => {
|
|||||||
capturedOptions.push(options as CapturedLockOptions);
|
capturedOptions.push(options as CapturedLockOptions);
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
await persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" });
|
await persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX });
|
||||||
|
|
||||||
fs.writeFileSync(snapshotPath, "[]", "utf8");
|
fs.writeFileSync(snapshotPath, "[]", "utf8");
|
||||||
withFileLockMock.mockImplementationOnce(async (_filePath, options) => {
|
withFileLockMock.mockImplementationOnce(async (_filePath, options) => {
|
||||||
|
|||||||
@@ -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 databases = await indexedDB.databases();
|
||||||
|
const expectedPrefix = params?.databasePrefix ? `${params.databasePrefix}::` : null;
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
databases
|
databases
|
||||||
.map((entry) => entry.name)
|
.map((entry) => entry.name)
|
||||||
.filter((name): name is string => Boolean(name))
|
.filter((name): name is string => Boolean(name))
|
||||||
|
.filter((name) => !expectedPrefix || name.startsWith(expectedPrefix))
|
||||||
.map(
|
.map(
|
||||||
(name) =>
|
(name) =>
|
||||||
new Promise<void>((resolve, reject) => {
|
new Promise<void>((resolve, reject) => {
|
||||||
|
|||||||
@@ -15,6 +15,16 @@ import {
|
|||||||
} from "./idb-persistence.test-helpers.js";
|
} from "./idb-persistence.test-helpers.js";
|
||||||
import { LogService } from "./logger.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", () => {
|
describe("Matrix IndexedDB persistence", () => {
|
||||||
let tmpDir: string;
|
let tmpDir: string;
|
||||||
let warnSpy: ReturnType<typeof vi.spyOn>;
|
let warnSpy: ReturnType<typeof vi.spyOn>;
|
||||||
@@ -22,12 +32,12 @@ describe("Matrix IndexedDB persistence", () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "matrix-idb-persist-"));
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "matrix-idb-persist-"));
|
||||||
warnSpy = vi.spyOn(LogService, "warn").mockImplementation(() => {});
|
warnSpy = vi.spyOn(LogService, "warn").mockImplementation(() => {});
|
||||||
await clearAllIndexedDbState();
|
await clearTestIndexedDbState();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
warnSpy.mockRestore();
|
warnSpy.mockRestore();
|
||||||
await clearAllIndexedDbState();
|
await clearTestIndexedDbState();
|
||||||
resetFileLockStateForTest();
|
resetFileLockStateForTest();
|
||||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
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 () => {
|
it("persists and restores database contents for the selected prefix", async () => {
|
||||||
const snapshotPath = path.join(tmpDir, "crypto-idb-snapshot.json");
|
const snapshotPath = path.join(tmpDir, "crypto-idb-snapshot.json");
|
||||||
await seedDatabase({
|
await seedDatabase({
|
||||||
name: "openclaw-matrix-test::matrix-sdk-crypto",
|
name: cryptoDatabaseName,
|
||||||
storeName: "sessions",
|
storeName: "sessions",
|
||||||
records: [{ key: "room-1", value: { session: "abc123" } }],
|
records: [{ key: "room-1", value: { session: "abc123" } }],
|
||||||
});
|
});
|
||||||
await seedDatabase({
|
await seedDatabase({
|
||||||
name: "other-prefix::matrix-sdk-crypto",
|
name: otherCryptoDatabaseName,
|
||||||
storeName: "sessions",
|
storeName: "sessions",
|
||||||
records: [{ key: "room-2", value: { session: "should-not-restore" } }],
|
records: [{ key: "room-2", value: { session: "should-not-restore" } }],
|
||||||
});
|
});
|
||||||
|
|
||||||
await persistIdbToDisk({
|
await persistIdbToDisk({
|
||||||
snapshotPath,
|
snapshotPath,
|
||||||
databasePrefix: "openclaw-matrix-test",
|
databasePrefix: DATABASE_PREFIX,
|
||||||
});
|
});
|
||||||
expect(fs.existsSync(snapshotPath)).toBe(true);
|
expect(fs.existsSync(snapshotPath)).toBe(true);
|
||||||
|
|
||||||
const mode = fs.statSync(snapshotPath).mode & 0o777;
|
const mode = fs.statSync(snapshotPath).mode & 0o777;
|
||||||
expect(mode).toBe(0o600);
|
expect(mode).toBe(0o600);
|
||||||
|
|
||||||
await clearAllIndexedDbState();
|
await clearTestIndexedDbState();
|
||||||
|
|
||||||
const restored = await restoreIdbFromDisk(snapshotPath);
|
const restored = await restoreIdbFromDisk(snapshotPath);
|
||||||
expect(restored).toBe(true);
|
expect(restored).toBe(true);
|
||||||
|
|
||||||
const restoredRecords = await readDatabaseRecords({
|
const restoredRecords = await readDatabaseRecords({
|
||||||
name: "openclaw-matrix-test::matrix-sdk-crypto",
|
name: cryptoDatabaseName,
|
||||||
storeName: "sessions",
|
storeName: "sessions",
|
||||||
});
|
});
|
||||||
expect(restoredRecords).toEqual([{ key: "room-1", value: { session: "abc123" } }]);
|
expect(restoredRecords).toEqual([{ key: "room-1", value: { session: "abc123" } }]);
|
||||||
|
|
||||||
const dbs = await indexedDB.databases();
|
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 () => {
|
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 () => {
|
it("serializes concurrent persist operations via file lock", async () => {
|
||||||
const snapshotPath = path.join(tmpDir, "concurrent-persist.json");
|
const snapshotPath = path.join(tmpDir, "concurrent-persist.json");
|
||||||
await seedDatabase({
|
await seedDatabase({
|
||||||
name: "openclaw-matrix-test::matrix-sdk-crypto",
|
name: cryptoDatabaseName,
|
||||||
storeName: "sessions",
|
storeName: "sessions",
|
||||||
records: [{ key: "room-1", value: { session: "abc123" } }],
|
records: [{ key: "room-1", value: { session: "abc123" } }],
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" }),
|
persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX }),
|
||||||
persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" }),
|
persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(fs.existsSync(snapshotPath)).toBe(true);
|
expect(fs.existsSync(snapshotPath)).toBe(true);
|
||||||
@@ -123,12 +133,12 @@ describe("Matrix IndexedDB persistence", () => {
|
|||||||
it("releases lock after persist completes", async () => {
|
it("releases lock after persist completes", async () => {
|
||||||
const snapshotPath = path.join(tmpDir, "lock-release.json");
|
const snapshotPath = path.join(tmpDir, "lock-release.json");
|
||||||
await seedDatabase({
|
await seedDatabase({
|
||||||
name: "openclaw-matrix-test::matrix-sdk-crypto",
|
name: cryptoDatabaseName,
|
||||||
storeName: "sessions",
|
storeName: "sessions",
|
||||||
records: [{ key: "room-1", value: { session: "abc123" } }],
|
records: [{ key: "room-1", value: { session: "abc123" } }],
|
||||||
});
|
});
|
||||||
|
|
||||||
await persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" });
|
await persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX });
|
||||||
|
|
||||||
const lockPath = `${snapshotPath}.lock`;
|
const lockPath = `${snapshotPath}.lock`;
|
||||||
expect(fs.existsSync(lockPath)).toBe(false);
|
expect(fs.existsSync(lockPath)).toBe(false);
|
||||||
@@ -138,13 +148,13 @@ describe("Matrix IndexedDB persistence", () => {
|
|||||||
it("releases lock after restore completes", async () => {
|
it("releases lock after restore completes", async () => {
|
||||||
const snapshotPath = path.join(tmpDir, "lock-release-restore.json");
|
const snapshotPath = path.join(tmpDir, "lock-release-restore.json");
|
||||||
await seedDatabase({
|
await seedDatabase({
|
||||||
name: "openclaw-matrix-test::matrix-sdk-crypto",
|
name: cryptoDatabaseName,
|
||||||
storeName: "sessions",
|
storeName: "sessions",
|
||||||
records: [{ key: "room-1", value: { session: "abc123" } }],
|
records: [{ key: "room-1", value: { session: "abc123" } }],
|
||||||
});
|
});
|
||||||
|
|
||||||
await persistIdbToDisk({ snapshotPath, databasePrefix: "openclaw-matrix-test" });
|
await persistIdbToDisk({ snapshotPath, databasePrefix: DATABASE_PREFIX });
|
||||||
await clearAllIndexedDbState();
|
await clearTestIndexedDbState();
|
||||||
await drainFileLockStateForTest();
|
await drainFileLockStateForTest();
|
||||||
|
|
||||||
await restoreIdbFromDisk(snapshotPath);
|
await restoreIdbFromDisk(snapshotPath);
|
||||||
|
|||||||
Reference in New Issue
Block a user