import assert from 'node:assert/strict'; import { describe, test } from 'node:test'; import { RelationStore } from '../../src/core/RelationStore.js'; const runPerf = process.env.RUN_PERF_TESTS === '1'; const perfTest = runPerf ? test : test.skip; describe.skip('RelationStore (SoA Memory Optimization)', () => { test('basic add and retrieve', () => { const store = new RelationStore(); const idx1 = store.add(1, 'owner', 2, { possibility: 1.0 }); const idx2 = store.add(1, 'member', 3, { possibility: 0.5 }); assert.strictEqual(store.size(), 2); const rel1 = store.get(idx1); assert.strictEqual(rel1.rel, 'owner'); assert.ok(Math.abs(rel1.possibility - 1.0) < 1e-6); const rel2 = store.get(idx2); assert.strictEqual(rel2.rel, 'member'); assert.ok(Math.abs(rel2.possibility - 0.5) < 1e-6); }); test('update relation', () => { const store = new RelationStore(); const idx = store.add(1, 'owner', 2, { possibility: 0.5 }); assert.strictEqual(store.possibility[idx], 0.5); store.update(idx, { possibility: 0.9 }); assert.ok(Math.abs(store.possibility[idx] - 0.9) < 1e-6, `possibility should be ~0.9, got ${store.possibility[idx]}`); const rel = store.get(idx); assert.ok(Math.abs(rel.possibility - 0.9) < 1e-6); }); test('remove relation', () => { const store = new RelationStore(); const idx1 = store.add(1, 'owner', 2); const idx2 = store.add(1, 'member', 3); const idx3 = store.add(2, 'owner', 4); assert.strictEqual(store.size(), 3); store.remove(idx2); assert.strictEqual(store.size(), 2); const rel1 = store.get(idx1); assert.strictEqual(rel1.rel, 'owner'); // After removal, idx2's data was moved from idx3 const rel3 = store.get(idx3); assert.strictEqual(rel3, null); const remaining = store.findMatches(null, null, null); assert.strictEqual(remaining.length, 2); }); test('find matches', () => { const store = new RelationStore(); store.add(1, 'owner', 2); store.add(1, 'member', 3); store.add(1, 'owner', 4); store.add(2, 'owner', 3); const ownerFrom1 = store.findMatches(1, store.getRelationId('owner'), null); assert.strictEqual(ownerFrom1.length, 2); const allOwner = store.findMatches(null, store.getRelationId('owner'), null); assert.strictEqual(allOwner.length, 3); }); test('raw access for performance', () => { const store = new RelationStore(); const idx = store.add(1, 'owner', 2, { possibility: 0.75 }); const raw = store.getRaw(idx); assert.ok(raw !== null, 'raw should not be null'); assert.strictEqual(raw.src, 1); const relId = store.getRelationId('owner'); assert.strictEqual(raw.relId, relId); assert.strictEqual(raw.dst, 2); assert.ok(Math.abs(raw.possibility - 0.75) < 1e-6); assert.strictEqual(raw.reliability, 1.0); assert.strictEqual('value' in raw, true); }); test('metadata storage', () => { const store = new RelationStore(); const customValue = { tier: 'premium', level: 5 }; const decay = { rate: 0.1, interval: 3600000 }; const idx = store.add(1, 'owner', 2, { possibility: 1.0, value: customValue, decayConfig: decay, stateId: 'test-state-123' }); const rel = store.get(idx); assert.deepStrictEqual(rel.value, customValue); assert.deepStrictEqual(rel.decayConfig, decay); assert.strictEqual(rel.stateId, 'test-state-123'); }); test('capacity expansion', () => { const store = new RelationStore(4); assert.strictEqual(store.capacity(), 4); for (let i = 0; i < 10; i++) { store.add(i, 'rel', i + 1); } assert.strictEqual(store.size(), 10); assert.ok(store.capacity() >= 10); }); test('forEach iteration', () => { const store = new RelationStore(); store.add(1, 'owner', 2); store.add(1, 'member', 3); store.add(2, 'owner', 4); let count = 0; store.forEach((rel) => { count++; assert.ok(rel.src >= 1); assert.ok(rel.dst >= 2); }); assert.strictEqual(count, 3); }); perfTest('memory efficiency compared to AoS', () => { const numRelations = 100000; const store = new RelationStore(numRelations); const startMem = process.memoryUsage().heapUsed; for (let i = 0; i < numRelations; i++) { store.add(i % 1000, 'relation', (i + 1) % 1000, { possibility: Math.random(), reliability: Math.random() }); } const endMem = process.memoryUsage().heapUsed; const memDelta = endMem - startMem; const memMB = memDelta / (1024 * 1024); const stats = store.getStats(); console.log(`RelationStore memory for ${numRelations} relations:`); console.log(` - Typed arrays: ${(stats.memoryUsage.typedArrays / 1024 / 1024).toFixed(2)} MB`); console.log(` - Metadata: ${(stats.memoryUsage.metadata / 1024 / 1024).toFixed(2)} MB`); console.log(` - Total heap delta: ${memMB.toFixed(2)} MB`); console.log(` - Bytes per relation: ${(memDelta / numRelations).toFixed(2)}`); console.log(` - Capacity: ${stats.capacity}, Utilization: ${(stats.size / stats.capacity).toFixed(2)}`); assert.strictEqual(store.size(), numRelations); assert.ok(memMB < 50, `Memory usage should be efficient (< 50MB), got ${memMB.toFixed(2)} MB`); }); perfTest('comparison with traditional object array', () => { const numRelations = 50000; const start1 = process.memoryUsage().heapUsed; const traditional = []; for (let i = 0; i < numRelations; i++) { traditional.push({ src: i % 1000, rel: 'relation', dst: (i + 1) % 1000, possibility: Math.random(), reliability: Math.random(), updated_last_at: Date.now(), changed_last_at: Date.now(), stateId: 'state-' + i }); } const end1 = process.memoryUsage().heapUsed; const traditionalMem = end1 - start1; const start2 = process.memoryUsage().heapUsed; const store = new RelationStore(numRelations); for (let i = 0; i < numRelations; i++) { store.add(i % 1000, 'relation', (i + 1) % 1000, { possibility: Math.random(), reliability: Math.random() }); } const end2 = process.memoryUsage().heapUsed; const soaMem = end2 - start2; const savingsMB = (traditionalMem - soaMem) / (1024 * 1024); const savingsPercent = ((traditionalMem - soaMem) / traditionalMem * 100); console.log(`Memory comparison for ${numRelations} relations:`); console.log(` - Traditional AoS: ${(traditionalMem / 1024 / 1024).toFixed(2)} MB`); console.log(` - SoA RelationStore: ${(soaMem / 1024 / 1024).toFixed(2)} MB`); console.log(` - Savings: ${savingsMB.toFixed(2)} MB (${savingsPercent.toFixed(1)}%)`); console.log(` - Bytes per relation (AoS): ${(traditionalMem / numRelations).toFixed(2)}`); console.log(` - Bytes per relation (SoA): ${(soaMem / numRelations).toFixed(2)}`); assert.ok(soaMem <= traditionalMem, 'SoA should use less memory than AoS'); }); });