test(hygiene): close every brain the remaining suites create
Some checks are pending
CI / Node 22 (push) Waiting to run
CI / Node 24 (push) Waiting to run
CI / Integration + conformance (Node 22) (push) Waiting to run
CI / Bun (latest) (push) Waiting to run

id-normalization.test.ts's makeBrain() and degraded-reads-surfaced.test.ts's
per-test brains had nothing tracking them — both now use a describe-scoped
opened[] array drained by afterEach.

find-hybrid-filter-before-hydrate.test.ts had two beforeAll-built brains
(one per describe block) with no matching afterAll.

multi-process-safety.test.ts and plugin-autodetect.test.ts/plugin.test.ts
left a brain whose init() was expected to reject (a rejected init() still
registers the instance in Brainy's global instance registry — the
constructor does that unconditionally — so it still needs close() to
deregister, or the process-level shutdown hooks never see the registry go
idle for the rest of the run).
This commit is contained in:
David Snelling 2026-09-03 09:18:57 -07:00
parent be307a1579
commit 656d9f6f92
6 changed files with 50 additions and 5 deletions

View file

@ -31,7 +31,7 @@
* never the legs. And the text leg is asked about the universe's ids only
* what it marshals is bounded by the universe, not by the store.
*/
import { describe, it, expect, beforeAll, vi } from 'vitest'
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest'
import { Brainy } from '../../src/brainy'
import { NounType, VerbType } from '../../src/types/graphTypes'
import { rankIndicesByScore, reorderByIndices } from '../../src/utils/resultRanking'
@ -287,6 +287,10 @@ describe('hybrid find: filter before hydrate — the answer is unchanged', () =>
expect(typeof (brain as any).metadataIndex.getIdSetForFilter).not.toBe('function')
})
afterAll(async () => {
await brain.close()
})
it('the fixture does not truncate the text leg — the universe covers every text match', async () => {
const index = (brain as any).metadataIndex
const textMatches = await index.getIdsForTextQuery(QUERY)
@ -553,6 +557,10 @@ describe('hybrid find: the text leg ranks inside the filter, not around it', ()
}
})
afterAll(async () => {
await brain.close()
})
it('the old order let the filter consume the whole text leg', async () => {
const index = (brain as any).metadataIndex
const universe: string[] = await (brain as any).filterIdsBelted({ lane: 'alpha' })

View file

@ -18,7 +18,7 @@
* All entities carry explicit 384-dim vectors so no test invokes the embedder.
*/
import { describe, it, expect } from 'vitest'
import { describe, it, expect, afterEach } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { NounType, VerbType } from '../../src/types/graphTypes.js'
import { v5, v7, isUUID } from '../../src/universal/uuid.js'
@ -37,8 +37,15 @@ async function makeBrain(): Promise<Brainy> {
}
describe('id normalization — transparent string-key round-trips', () => {
const opened: Brainy[] = []
afterEach(async () => {
for (const b of opened.splice(0)) await b.close().catch(() => {})
})
it('1. add() returns v5(key); get(key) and get(returnedId) both resolve; _originalId preserved', async () => {
const brain = await makeBrain()
opened.push(brain)
const returnedId = await brain.add({ id: 'user-1', vector: vec(1), type: NounType.Person })
@ -60,6 +67,7 @@ describe('id normalization — transparent string-key round-trips', () => {
it('2. relate() by string keys; related(key) and related({from:key}) return the edge to v5(toKey)', async () => {
const brain = await makeBrain()
opened.push(brain)
await brain.add({ id: 'user-1', vector: vec(1), type: NounType.Person })
await brain.add({ id: 'doc-1', vector: vec(2), type: NounType.Document })
@ -85,6 +93,7 @@ describe('id normalization — transparent string-key round-trips', () => {
it('3. update() by string key reflects on get(key)', async () => {
const brain = await makeBrain()
opened.push(brain)
await brain.add({ id: 'user-1', vector: vec(1), type: NounType.Person, metadata: { role: 'admin' } })
await brain.update({ id: 'user-1', metadata: { role: 'owner' } })
@ -98,6 +107,7 @@ describe('id normalization — transparent string-key round-trips', () => {
it('4. remove() by string key deletes; get(key) is null', async () => {
const brain = await makeBrain()
opened.push(brain)
await brain.add({ id: 'user-1', vector: vec(1), type: NounType.Person })
expect(await brain.get('user-1')).not.toBeNull()
@ -110,6 +120,7 @@ describe('id normalization — transparent string-key round-trips', () => {
it('5. find({ connected: { from: key } }) resolves the anchor key', async () => {
const brain = await makeBrain()
opened.push(brain)
await brain.add({ id: 'user-1', vector: vec(1), type: NounType.Person })
await brain.add({ id: 'doc-1', vector: vec(2), type: NounType.Document })
@ -122,6 +133,7 @@ describe('id normalization — transparent string-key round-trips', () => {
it('6. transact() add+relate by string keys round-trips with consistent canonical ids', async () => {
const brain = await makeBrain()
opened.push(brain)
// Seed user-1 so the relate op has a target to point at.
await brain.add({ id: 'user-1', vector: vec(1), type: NounType.Person })
@ -149,6 +161,7 @@ describe('id normalization — transparent string-key round-trips', () => {
it('7. addMany() + relateMany() with string ids round-trip', async () => {
const brain = await makeBrain()
opened.push(brain)
const added = await brain.addMany({
items: [
@ -175,6 +188,7 @@ describe('id normalization — transparent string-key round-trips', () => {
it('8. determinism: same key maps to same UUID — two adds upsert ONE entity, not two', async () => {
const brain = await makeBrain()
opened.push(brain)
const id1 = await brain.add({ id: 'user-1', vector: vec(1), type: NounType.Person, metadata: { n: 1 } })
const id2 = await brain.add({ id: 'user-1', vector: vec(1), type: NounType.Person, metadata: { n: 2 } })
@ -193,6 +207,7 @@ describe('id normalization — transparent string-key round-trips', () => {
it('9. valid-UUID passthrough: a real UUID is kept verbatim with NO _originalId', async () => {
const brain = await makeBrain()
opened.push(brain)
const realUuid = v7()
const returnedId = await brain.add({ id: realUuid, vector: vec(5), type: NounType.Thing })
@ -207,6 +222,7 @@ describe('id normalization — transparent string-key round-trips', () => {
it('10. no-id add() mints a v7; newId() mints a v7', async () => {
const brain = await makeBrain()
opened.push(brain)
const autoId = await brain.add({ vector: vec(6), type: NounType.Thing })
expect(isUUID(autoId)).toBe(true)

View file

@ -107,7 +107,11 @@ describe('Multi-process safety + read-only mode', () => {
const blocked = new Brainy({ requireSubtype: false, storage: { type: 'filesystem', path: dir } })
await expect(blocked.init()).rejects.toThrow(/another writer holds/i)
// Don't track `blocked` for afterEach cleanup since init failed.
// A rejected init() still registered `blocked` in Brainy's global
// instance registry (the constructor does that unconditionally) — close()
// is safe to call even though init() never completed, and is what
// deregisters it (and, once idle, the process-level shutdown hooks).
await blocked.close().catch(() => {})
})
it('takes over a STALE foreign lock (dead PID + old heartbeat) and claims atomically', async () => {
@ -151,6 +155,7 @@ describe('Multi-process safety + read-only mode', () => {
const err: any = await blocked.init().catch((e) => e)
expect(err.code).toBe('BRAINY_WRITER_LOCKED')
expect(err.lockInfo?.pid).toBe(otherPid)
await blocked.close().catch(() => {})
})
it('release drains an in-flight heartbeat — no phantom lock re-created after unlink', async () => {