/** * @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 metrics: Record }> 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) })