/** * SnapshotBinary round-trip tests. * * Verifies: * - serialize/deserialize preserves nodes and relations * - Deserialized snapshots are marked _snapshotReadOnly * - UTF-8 keys survive round-trip * - Relation configs survive round-trip * * Run: node --test --test-force-exit lib/tests/property-based/snapshot-binary.test.js */ import { Arbiter } from '../../src/core/Arbiter.js'; import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; function createArbiterFactory() { return () => new Arbiter({ fastConstructionMode: true, enableInference: false }); } async function loadSnapshotModule() { return import('../../src/core/SnapshotBinary.js'); } describe('SnapshotBinary — Round-Trip', () => { it('serialize and deserialize preserves nodes and relations', async () => { const { serializeArbiterSnapshot, deserializeArbiterSnapshot } = await loadSnapshotModule(); const original = new Arbiter({ fastConstructionMode: true, enableInference: false }); original.addNode('user:alice', 'user'); original.addNode('project:secret', 'project'); original.addRelation('user:alice', 'can_read', 'project:secret', { possibility: 0.9, value: 100 }); original.setRelationConfig('can_read', { type: 'direct' }); original.enableCondensedSnapshot(); const buffer = serializeArbiterSnapshot(original); assert.ok(buffer instanceof ArrayBuffer); assert.ok(buffer.byteLength > 0); const restored = deserializeArbiterSnapshot(buffer, createArbiterFactory()); assert.ok(restored.nodeIdByKey.has('user:alice')); assert.ok(restored.nodeIdByKey.has('project:secret')); const nodes = [...restored.nodes.values()].filter(Boolean); assert.ok(nodes.length >= 2); }); it('deserialized snapshot is marked read-only', async () => { const { serializeArbiterSnapshot, deserializeArbiterSnapshot } = await loadSnapshotModule(); const original = new Arbiter({ fastConstructionMode: true, enableInference: false }); original.addNode('user:alice', 'user'); original.addNode('project:secret', 'project'); original.addRelation('user:alice', 'can_read', 'project:secret', { possibility: 0.9 }); original.enableCondensedSnapshot(); const buffer = serializeArbiterSnapshot(original); const restored = deserializeArbiterSnapshot(buffer, createArbiterFactory()); assert.strictEqual(restored._snapshotReadOnly, true); assert.strictEqual(restored.snapshotEnabled, true); assert.ok(restored.snapshotGraph); }); it('UTF-8 keys survive round-trip', async () => { const { serializeArbiterSnapshot, deserializeArbiterSnapshot } = await loadSnapshotModule(); const original = new Arbiter({ fastConstructionMode: true, enableInference: false }); original.addNode('user:alice', 'user'); original.addNode('project:résumé', 'project'); original.addRelation('user:alice', 'can_read', 'project:résumé', { possibility: 0.9 }); original.setRelationConfig('can_read', { type: 'direct' }); original.enableCondensedSnapshot(); const buffer = serializeArbiterSnapshot(original); const restored = deserializeArbiterSnapshot(buffer, createArbiterFactory()); assert.ok(restored.nodeIdByKey.has('project:résumé')); assert.strictEqual(restored.relationConfigs.has('can_read'), true); }); it('relation configs survive round-trip', async () => { const { serializeArbiterSnapshot, deserializeArbiterSnapshot } = await loadSnapshotModule(); const original = new Arbiter({ fastConstructionMode: true, enableInference: false }); original.addNode('user:alice', 'user'); original.addNode('project:secret', 'project'); original.addRelation('user:alice', 'can_access', 'project:secret', { possibility: 0.8, value: 42, reliability: 0.95 }); original.setRelationConfig('can_access', { type: 'chain', steps: [{ relation: 'parent', direction: 'out' }] }); original.setRelationConfig('can_read', { type: 'direct', minPossibility: 0.5 }); original.enableCondensedSnapshot(); const buffer = serializeArbiterSnapshot(original); const restored = deserializeArbiterSnapshot(buffer, createArbiterFactory()); const accessConfig = restored.relationConfigs.get('can_access'); assert.strictEqual(accessConfig && accessConfig.type, 'chain'); assert.deepStrictEqual(accessConfig && accessConfig.steps, [{ relation: 'parent', direction: 'out' }]); const readConfig = restored.relationConfigs.get('can_read'); assert.strictEqual(readConfig && readConfig.type, 'direct'); assert.strictEqual(readConfig && readConfig.minPossibility, 0.5); }); });