/** * @module tests/unit/graph/lsm-partial-load-failclosed * @description LSMTree partial-load fail-closed guard (Finding 3 of the honest * index-readiness spine plan). `loadSSTables()` used to swallow a per-SSTable * load failure with only a `prodLog.warn`, then `loadManifest()` still published * the FULL persisted `totalRelationships` count — so on a partial cold load, * `size()`/`isHealthy()` reported the full count and "healthy" while a subset of * the tree's edges were silently unqueryable. Fixed by failing the whole load * closed on ANY per-SSTable failure: `loadManifest()`'s existing catch then * resets `sstables`/`totalRelationships`/`sstablesByLevel` to honest-empty, so * `size()` reports 0 and the graph layer's existing `size()===0` self-heal * rebuilds from canonical records — the dishonest partial state can no longer be * observed. */ import { describe, it, expect } from 'vitest' import { LSMTree } from '../../../src/graph/lsm/LSMTree.js' import { MemoryStorage } from '../../../src/storage/adapters/memoryStorage.js' describe('LSMTree partial SSTable load fails closed (honest size())', () => { it('one SSTable fails to load → size() reports 0, not the manifest count', async () => { const storage = new MemoryStorage() await storage.init() const prefix = 'test-lsm-partial' const t1 = new LSMTree(storage, { storagePrefix: prefix, enableCompaction: false }) await t1.init() await t1.add('a', 'b'); await t1.flush() // SSTable #1 await t1.add('c', 'd'); await t1.flush() // SSTable #2 expect(t1.size()).toBeGreaterThanOrEqual(2) await t1.close() // Find an SSTable id from the persisted manifest. const manifest = await storage.getMetadata(`${prefix}-manifest`) const sstableIds = Object.keys((manifest!.data as any).sstables) expect(sstableIds.length).toBeGreaterThanOrEqual(2) // Reopen with ONE SSTable load forced to fail (deterministic). const t2 = new LSMTree(storage, { storagePrefix: prefix, enableCompaction: false }) const realGet = storage.getMetadata.bind(storage) ;(storage as any).getMetadata = async (key: string) => { if (key === `${prefix}-${sstableIds[1]}`) throw new Error('simulated SSTable load failure') return realGet(key) } await t2.init() // BEFORE THE FIX: t2.size() === (full manifest count) ← LIE (partial load, full count) // AFTER THE FIX: t2.size() === 0 ← fail-closed → self-heal rebuilds expect(t2.size()).toBe(0) expect(t2.isHealthy()).toBe(true) // honestly-empty-but-initialized, not "healthy with a lie" await t2.close() }) it('a clean load with no failures still reports the full honest count', async () => { const storage = new MemoryStorage() await storage.init() const prefix = 'test-lsm-clean' const t1 = new LSMTree(storage, { storagePrefix: prefix, enableCompaction: false }) await t1.init() await t1.add('a', 'b'); await t1.flush() await t1.add('c', 'd'); await t1.flush() const expectedSize = t1.size() expect(expectedSize).toBeGreaterThanOrEqual(2) await t1.close() // Reopen with no injected failures — a normal, fully-successful cold load. const t2 = new LSMTree(storage, { storagePrefix: prefix, enableCompaction: false }) await t2.init() expect(t2.size()).toBe(expectedSize) expect(t2.isHealthy()).toBe(true) await t2.close() }) })