import assert from "node:assert/strict"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import test from "node:test"; import { createFileSyncStore } from "../../apps/sync-server/src/store/fileSyncStore"; test("file-backed store survives restart and serves another device", () => { const tempDirectory = mkdtempSync(path.join(tmpdir(), "obsidian-sync-")); const storePath = path.join(tempDirectory, "sync-state.json"); try { const serverA = createFileSyncStore(storePath); const deviceA = serverA.registerDevice({ vaultId: "vault-1", deviceName: "Desktop" }); const deviceB = serverA.registerDevice({ vaultId: "vault-1", deviceName: "Phone" }); const pushResponse = serverA.push({ vaultId: "vault-1", deviceId: deviceA.deviceId, knownServerRevision: 0, files: [ { manifest: { path: "notes/hello.md", kind: "text", contentHash: "hash-1", revisionId: "rev-1", updatedAt: "2026-04-08T00:00:00.000Z", sizeBytes: 12, deviceId: deviceA.deviceId }, envelope: { algorithm: "AES-GCM-256", keyId: "key-1", iv: "iv-1", ciphertext: "cipher-1" } } ], tombstones: [] }); assert.deepEqual(pushResponse.acceptedFilePaths, ["notes/hello.md"]); const serverB = createFileSyncStore(storePath); assert.equal(serverB.authenticate("vault-1", deviceB.deviceId, deviceB.token), true); const pullResponse = serverB.pull("vault-1", 0); assert.equal(pullResponse.changes.length, 1); assert.equal(pullResponse.changes[0].file?.manifest.path, "notes/hello.md"); assert.equal(pullResponse.serverRevision, 1); assert.equal(pullResponse.hasMore, false); assert.equal(pullResponse.nextSinceServerRevision, 1); } finally { rmSync(tempDirectory, { recursive: true, force: true }); } }); test("file-backed store paginates pull results", () => { const tempDirectory = mkdtempSync(path.join(tmpdir(), "obsidian-sync-page-")); const storePath = path.join(tempDirectory, "sync-state.json"); try { const store = createFileSyncStore(storePath); const device = store.registerDevice({ vaultId: "vault-2", deviceName: "Desktop" }); store.push({ vaultId: "vault-2", deviceId: device.deviceId, knownServerRevision: 0, files: [ { manifest: { path: "a.md", kind: "text", contentHash: "hash-a", revisionId: "rev-a", updatedAt: "2026-04-08T00:00:00.000Z", sizeBytes: 1, deviceId: device.deviceId }, envelope: { algorithm: "AES-GCM-256", keyId: "key-a", iv: "iv-a", ciphertext: "cipher-a" } } ], tombstones: [] }); store.push({ vaultId: "vault-2", deviceId: device.deviceId, knownServerRevision: 1, files: [ { manifest: { path: "b.md", kind: "text", contentHash: "hash-b", revisionId: "rev-b", updatedAt: "2026-04-08T00:01:00.000Z", sizeBytes: 1, deviceId: device.deviceId }, envelope: { algorithm: "AES-GCM-256", keyId: "key-a", iv: "iv-b", ciphertext: "cipher-b" } } ], tombstones: [] }); const firstPage = store.pull("vault-2", 0, 1); assert.equal(firstPage.changes.length, 1); assert.equal(firstPage.hasMore, true); assert.equal(firstPage.nextSinceServerRevision, 1); const secondPage = store.pull("vault-2", firstPage.nextSinceServerRevision, 1); assert.equal(secondPage.changes.length, 1); assert.equal(secondPage.hasMore, false); assert.equal(secondPage.nextSinceServerRevision, 2); assert.equal(secondPage.changes[0].file?.manifest.path, "b.md"); } finally { rmSync(tempDirectory, { recursive: true, force: true }); } }); test("device revocation and key rotation state are durable", () => { const tempDirectory = mkdtempSync(path.join(tmpdir(), "obsidian-sync-device-")); const storePath = path.join(tempDirectory, "sync-state.json"); try { const store = createFileSyncStore(storePath); const desktop = store.registerDevice({ vaultId: "vault-3", deviceName: "Desktop" }); const phone = store.registerDevice({ vaultId: "vault-3", deviceName: "Phone" }); const rotation = store.rotateVaultKey("vault-3", "key-rotated"); assert.equal(rotation.activeKeyId, "key-rotated"); const revoked = store.revokeDevice("vault-3", phone.deviceId); assert.equal(revoked.targetDeviceId, phone.deviceId); assert.equal(store.authenticate("vault-3", phone.deviceId, phone.token), false); const restartedStore = createFileSyncStore(storePath); const devices = restartedStore.listDevices("vault-3"); assert.equal(devices.activeKeyId, "key-rotated"); assert.equal(devices.devices.find((device) => device.deviceId === phone.deviceId)?.revokedAt !== undefined, true); assert.equal(restartedStore.authenticate("vault-3", desktop.deviceId, desktop.token), true); } finally { rmSync(tempDirectory, { recursive: true, force: true }); } });