open-brainy/tests/integration/read-surface-readiness.test.ts
David Snelling 40e7119b85
Some checks failed
CI / Node 22 (push) Has been cancelled
CI / Node 24 (push) Has been cancelled
CI / Integration + conformance (Node 22) (push) Has been cancelled
CI / Bun (latest) (push) Has been cancelled
fix(reads): the readiness gate guards every index read surface — serving empty from a not-ready provider is unrepresentable
A production store acked writes while readback served empty for fifteen
minutes. The brainy half: the lazy readiness gate had exactly one caller —
find() — while related() and every VFS path served straight from providers
that init had deferred (disableAutoRebuild on a large store). A false
health verdict from the accelerator pulled the trigger; the unguarded read
surfaces were the gun.

Every index read funnels through three helpers; the gate now lives at those
choke points, so any first read on a cold instance waits for the build and
then serves truth — the fast path after the latch is one boolean. The lazy
build's start is narrated through the production logger, never the
silent-suppressible console: fifteen silent minutes taught that line.

Pinned with the production shape: related() as the first-ever read on a
fresh lazy instance serves the relation; a filtered find serves the row.
2026-08-20 11:49:03 -07:00

68 lines
2.9 KiB
TypeScript

/**
* @module tests/integration/read-surface-readiness
* @description THE READ-SURFACE READINESS GATE (a production blackout's
* brainy half): with `disableAutoRebuild: true`, init defers index builds —
* and before this gate, only find() waited for the lazy rebuild while
* related() and every VFS path served EMPTY from the not-ready providers
* (writes acked into canonical, readback empty — fifteen live minutes).
* The pins: on a fresh instance over a populated store, the FIRST read on
* every surface serves truth (it waits for the build), never 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/index.js'
import { NounType, VerbType } from '../../src/types/graphTypes.js'
const dirs: string[] = []
const brains: Brainy[] = []
afterEach(async () => {
for (const b of brains.splice(0)) await b.close().catch(() => {})
for (const d of dirs.splice(0)) rmSync(d, { recursive: true, force: true })
})
async function openLazy(dir: string): Promise<Brainy> {
const brain = new Brainy({
storage: { type: 'filesystem', path: dir },
requireSubtype: false,
silent: true,
disableAutoRebuild: true
})
await brain.init()
brains.push(brain)
return brain
}
describe('read-surface readiness gate', () => {
it('related() as the FIRST read on a fresh lazy instance serves truth, never empty', async () => {
const dir = mkdtempSync(join(tmpdir(), 'brainy-readgate-'))
dirs.push(dir)
const writer = await openLazy(dir)
const a = await writer.add({ data: 'hub row', type: NounType.Document, metadata: { n: 1 } })
const b = await writer.add({ data: 'leaf row', type: NounType.Document, metadata: { n: 2 } })
await writer.relate({ from: a, to: b, type: VerbType.RelatedTo })
await writer.flush()
await brains.pop()!.close()
// Fresh instance: indexes deferred at open. The production shape called
// related() FIRST (no find() to trigger the old, only gate).
const reader = await openLazy(dir)
const rels = await reader.related({ from: a })
expect(rels.length, 'the FIRST related() read waits for the build and serves').toBeGreaterThan(0)
expect(rels.some((r) => r.to === b || (r as { target?: string }).target === b)).toBe(true)
}, 120000)
it('a metadata-filtered read as the FIRST read serves truth, never empty', async () => {
const dir = mkdtempSync(join(tmpdir(), 'brainy-readgate2-'))
dirs.push(dir)
const writer = await openLazy(dir)
await writer.add({ data: 'tagged row', type: NounType.Document, metadata: { team: 'atlas' } })
await writer.flush()
await brains.pop()!.close()
const reader = await openLazy(dir)
const rows = await reader.find({ where: { team: 'atlas' } })
expect(rows.length, 'filtered find on a cold lazy instance serves').toBe(1)
}, 120000)
})