open-brainy/tests/integration/brain-relocation.test.ts

109 lines
4.2 KiB
TypeScript
Raw Normal View History

2026-08-10 10:55:11 -07:00
/**
* @module tests/integration/brain-relocation
* @description LC8 RELOCATABLE BRAIN DIRECTORY. A brain's directory moved
* wholesale to a new path (rename/copy backup-restore, disk migration,
* container re-mount) must open and serve IDENTICALLY: no absolute paths may
* hide in any persisted artifact. Pinned across every intelligence: point
* reads, metadata find, semantic find, graph traversal, aggregation plus
* continued writes with monotonic generations and time-travel reads over
* pre-move history.
*/
import { describe, it, expect, afterEach } from 'vitest'
import { mkdtempSync, rmSync, renameSync } 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 })
})
const AGG = {
name: 'by_kind',
source: { type: NounType.Document },
groupBy: ['kind'] as string[],
metrics: { count: { op: 'count' as const } }
}
describe('LC8 — a moved brain directory opens and serves identically', () => {
it('rename the directory: all three intelligences serve, writes continue, history travels', async () => {
const home = mkdtempSync(join(tmpdir(), 'brainy-reloc-'))
dirs.push(home)
const oldPath = join(home, 'brain-old')
const newPath = join(home, 'brain-new')
// Season a brain: rows, a relation, an aggregate, then flush + close.
let brain = new Brainy({ storage: { type: 'filesystem', path: oldPath }, requireSubtype: false })
await brain.init()
brains.push(brain)
brain.defineAggregate(AGG)
const alpha = await brain.add({
data: 'alpha document about mountain geology',
type: NounType.Document,
metadata: { kind: 'report', n: 1 }
})
const beta = await brain.add({
data: 'beta document about coastal erosion',
type: NounType.Document,
metadata: { kind: 'report', n: 2 }
})
await brain.relate({ from: alpha, to: beta, verb: VerbType.RelatedTo })
await brain.queryAggregate(AGG.name) // settle backfill
const preMoveGen = brain.generation()
await brain.flush()
await brain.close()
brains.pop()
// The move: wholesale directory rename.
renameSync(oldPath, newPath)
// Reopen at the NEW path — everything serves.
brain = new Brainy({ storage: { type: 'filesystem', path: newPath }, requireSubtype: false })
await brain.init()
brains.push(brain)
brain.defineAggregate(AGG)
// Point read + metadata find.
expect((await brain.get(alpha))!.data).toContain('mountain geology')
const found = await brain.find({ where: { kind: 'report' }, limit: 10 })
expect(found.map((r) => r.id).sort()).toEqual([alpha, beta].sort())
// Semantic find.
const sem = await brain.find({ query: 'alpha document about mountain geology', limit: 3 })
expect(sem.map((r) => r.id)).toContain(alpha)
// Graph traversal.
const related = await brain.related(alpha)
expect(related.map((r) => r.to)).toContain(beta)
// Aggregation.
const agg = (await brain.queryAggregate(AGG.name)) as Array<{
groupKey: Record<string, unknown>
metrics: Record<string, unknown>
}>
const reportRow = agg.find((g) => g.groupKey['kind'] === 'report')
expect(Number(reportRow?.metrics.count)).toBe(2)
// Writes continue with monotonic generations.
const gamma = await brain.add({
data: 'gamma addendum after the move',
type: NounType.Document,
metadata: { kind: 'report', n: 3 }
})
expect(brain.generation()).toBeGreaterThan(preMoveGen)
expect((await brain.get(gamma))!.data).toContain('addendum')
// Time travel across the move boundary: the pre-move pin sees exactly
// the pre-move world (no gamma), served from relocated history.
const dbPast = await brain.asOf(preMoveGen)
expect(await dbPast.get(gamma)).toBeNull()
expect((await dbPast.get(alpha))!.data).toContain('mountain geology')
await dbPast.release()
}, 120000)
})