/** * @module tests/integration/open-does-not-wait-for-a-rebuilding-provider * @description OPEN DOES NOT WAIT FOR A PROVIDER THAT IS REBUILDING ITSELF. * * Measured on a production store: a metadata provider that had to rebuild made * `init()` pay the ENTIRE rebuild on the foreground — 641 seconds — with every * other family idle behind it, because a provider reporting `serving: false` * because it is BUSY BUILDING and one reporting `serving: false` because it is * BROKEN were indistinguishable, and both were answered the same way: call * `rebuild()`, and wait. * * The law: a provider that reports `rebuildInProgress()` owns its own rebuild. * `init()` returns; every other family serves; THAT family's doors refuse by * name, carrying the provider's own progress; and the doors open by themselves * when the provider reports serving. Nothing is ever served empty. */ import { describe, it, expect, afterEach } from 'vitest' import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { Brainy } from '../../src/brainy.js' import { NounType } from '../../src/types/graphTypes.js' import type { ProviderRebuildProgress } from '../../src/utils/indexReadiness.js' /** How long the stub provider claims to be rebuilding. */ const REBUILD_MS = 6_000 describe('a provider rebuilding itself never blocks open', () => { const dirs: string[] = [] const brains: Brainy[] = [] afterEach(async () => { for (const b of brains.splice(0)) { try { await b.close() } catch { /* already closed */ } } for (const d of dirs.splice(0)) { try { rmSync(d, { recursive: true, force: true }) } catch { /* ignore */ } } }) it('init() returns in milliseconds, the family refuses by name, then answers', async () => { const dir = mkdtempSync(join(tmpdir(), 'brainy-rebuilding-provider-')) dirs.push(dir) // Seed a store so the open has something to (not) rebuild. const seed = new Brainy({ requireSubtype: false, storage: { type: 'filesystem', path: dir } }) await seed.init() await seed.add({ data: 'a row with a plain field', type: NounType.Concept, metadata: { kind: 'report' } }) await seed.flush() await seed.close() const brain = new Brainy({ requireSubtype: false, storage: { type: 'filesystem', path: dir } }) brains.push(brain) // Dress the metadata index as a provider that is rebuilding ITSELF: not // serving, and honest about why. `init()` wires the real index first, so // the hooks are installed on the instance as soon as it exists — the gate // reads them by feature detection, exactly as it would a native provider's. const rebuildStartedAt = Date.now() const stillRebuilding = () => Date.now() - rebuildStartedAt < REBUILD_MS let rebuildCalls = 0 const inner = brain as unknown as { metadataIndex: Record setupIndex?: unknown } // Install on the prototype-free instance right after construction by // patching the property the moment init() assigns it. const install = (target: Record) => { const realRebuild = target.rebuild as () => Promise target.rebuildInProgress = (): ProviderRebuildProgress | null => stillRebuilding() ? { phase: 'metadata shadow build', done: 4_096, total: 14_056, startedAt: rebuildStartedAt } : null target.healthReport = () => ({ provider: 'metadata', healthy: !stillRebuilding(), serving: !stillRebuilding(), generation: 1, invariants: [], unledgered: [] }) target.rebuild = async () => { rebuildCalls++ return realRebuild.call(target) } } // init() constructs the metadata index; patch as soon as it exists, before // the gate consults it. A microtask hop after the index is assigned is // enough because the gate runs later in the same init. const initPromise = (async () => { const originalEnsure = (brain as unknown as { setupIndex?: () => unknown }).setupIndex void originalEnsure return brain.init() })() // Patch on the first tick the index exists. const patcher = setInterval(() => { if (inner.metadataIndex && !inner.metadataIndex.rebuildInProgress) { install(inner.metadataIndex) } }, 1) const startedAt = Date.now() try { await initPromise } finally { clearInterval(patcher) } const openMs = Date.now() - startedAt // If the patch did not land before the gate ran, this test proves nothing — // say so loudly rather than passing vacuously. expect( typeof inner.metadataIndex.rebuildInProgress, 'the stub provider was never installed — the test is vacuous' ).toBe('function') // 1. The open did not wait out the rebuild. expect(openMs).toBeLessThan(REBUILD_MS) // 2. And brainy did not start a rebuild of its own on top of the provider's. expect(rebuildCalls).toBe(0) // 3. The family's door refuses BY NAME, carrying the provider's progress. let refusal: Error | null = null try { await brain.find({ where: { kind: 'report' } } as never) } catch (err) { refusal = err as Error } expect(refusal, 'a not-serving metadata family must refuse, never serve empty').not.toBeNull() expect(refusal!.message).toMatch(/metadata shadow build/i) expect(refusal!.message).toMatch(/4,096\/14,056/) expect(refusal!.message).toMatch(/no action is needed/i) // 4. Other families keep serving — the brain is open. const all = await brain.getNouns?.({ pagination: { limit: 1 } } as never) expect(all ?? true).toBeTruthy() // 5. When the provider reports itself serving, the door opens by itself. await new Promise((r) => setTimeout(r, REBUILD_MS)) ;(brain as unknown as { _metadataVerified: boolean })._metadataVerified = false await expect(brain.find({ where: { kind: 'report' } } as never)).resolves.toBeDefined() }, 180_000) it('a rebuilding provider reporting 0 entries is not a CRITICAL, and gets no second rebuild', async () => { const dir = mkdtempSync(join(tmpdir(), 'brainy-rebuilding-critical-')) dirs.push(dir) const seed = new Brainy({ requireSubtype: false, storage: { type: 'filesystem', path: dir } }) await seed.init() await seed.add({ data: 'a stored entity', type: NounType.Concept }) await seed.flush() await seed.close() const brain = new Brainy({ requireSubtype: false, storage: { type: 'filesystem', path: dir } }) brains.push(brain) let rebuildCalls = 0 const errors: string[] = [] const origError = console.error console.error = ((...a: unknown[]) => { errors.push(a.map(String).join(' ')) }) as typeof console.error const inner = brain as unknown as { metadataIndex: Record } const patcher = setInterval(() => { if (inner.metadataIndex && !inner.metadataIndex.rebuildInProgress) { const target = inner.metadataIndex target.rebuildInProgress = () => ({ phase: 'online metadata rebuild', startedAt: Date.now() }) target.healthReport = () => ({ provider: 'metadata', healthy: false, serving: false, generation: 1, invariants: [], unledgered: [] }) // The shape the native engine now has: the index reports NOTHING while // its rebuild runs online behind refusing doors. target.getStats = async () => ({ totalEntries: 0 }) target.rebuild = async () => { rebuildCalls++ } } }, 1) try { await brain.init() } finally { clearInterval(patcher) console.error = origError } expect( typeof inner.metadataIndex.rebuildInProgress, 'the stub provider was never installed — the test is vacuous' ).toBe('function') expect(errors.filter((l) => /CRITICAL: Metadata index has 0 entries/.test(l))).toEqual([]) expect(rebuildCalls).toBe(0) }, 180_000) })