chore(8.0): final pre-RC1 sweep — API consistency, named errors, orphans, zero-cast codebase

This commit is contained in:
David Snelling 2026-06-11 14:51:00 -07:00
parent 970e08c466
commit 1f7e365a4e
237 changed files with 1951 additions and 49413 deletions

View file

@ -174,7 +174,7 @@ describe('Aggregation Engine Integration', () => {
metadata: { category: 'food', amount: 15 }
})
await brain.delete(id1)
await brain.remove(id1)
const results = await brain.find({ aggregate: 'totals' })
expect(results).toHaveLength(1)

View file

@ -7,10 +7,10 @@
* 3. Production quality (no errors, proper returns)
*
* APIs tested:
* - brain.add(), brain.get(), brain.update(), brain.delete()
* - brain.add(), brain.get(), brain.update(), brain.remove()
* - brain.find(), brain.similar()
* - brain.relate(), brain.getRelations(), brain.unrelate()
* - brain.addMany(), brain.updateMany(), brain.deleteMany(), brain.relateMany()
* - brain.relate(), brain.related(), brain.unrelate()
* - brain.addMany(), brain.updateMany(), brain.removeMany(), brain.relateMany()
* - vfs.* (init, mkdir, writeFile, readdir, readFile, stat, exists)
* - neural.* (similar, neighbors, outliers)
* - import.* (would test if CSV/Excel/PDF available)
@ -113,12 +113,12 @@ describe('Comprehensive All-APIs Test', () => {
console.log(`✅ brain.similar() found ${similar.length} similar entities (no VFS)`)
})
it('brain.delete() - should delete entity', async () => {
await brain.delete(entityId)
it('brain.remove() - should delete entity', async () => {
await brain.remove(entityId)
const deleted = await brain.get(entityId)
expect(deleted).toBeNull()
console.log(`✅ brain.delete() deleted entity`)
console.log(`✅ brain.remove() deleted entity`)
})
})
@ -148,20 +148,20 @@ describe('Comprehensive All-APIs Test', () => {
console.log(`✅ brain.relate() created relation: ${relationId}`)
})
it('brain.getRelations() - should retrieve relationships', async () => {
const relations = await brain.getRelations({
it('brain.related() - should retrieve relationships', async () => {
const relations = await brain.related({
from: entity1Id
})
expect(relations.length).toBeGreaterThan(0)
expect(relations.some(r => r.id === relationId)).toBe(true)
console.log(`✅ brain.getRelations() found ${relations.length} relations`)
console.log(`✅ brain.related() found ${relations.length} relations`)
})
it('brain.unrelate() - should delete relationship', async () => {
await brain.unrelate(relationId)
const relations = await brain.getRelations({
const relations = await brain.related({
from: entity1Id
})
@ -203,7 +203,7 @@ describe('Comprehensive All-APIs Test', () => {
console.log(`✅ brain.relateMany() created ${relationIds.length} relations`)
})
it('brain.deleteMany() - should delete multiple entities', async () => {
it('brain.removeMany() - should delete multiple entities', async () => {
const ids = await brain.addMany({
items: [
{ data: 'Delete 1', type: NounType.Document },
@ -211,12 +211,12 @@ describe('Comprehensive All-APIs Test', () => {
]
})
const result = await brain.deleteMany({
const result = await brain.removeMany({
ids: ids.successful
})
expect(result.successful.length).toBe(2)
console.log(`✅ brain.deleteMany() deleted ${result.successful.length} entities`)
console.log(`✅ brain.removeMany() deleted ${result.successful.length} entities`)
})
})

View file

@ -97,7 +97,7 @@ describe('Batch Import with Immediate Relations (v5.7.3 Fix)', () => {
expect(relationIds.length).toBe(372)
// 4. Verify all relationships exist (use higher limit to get all 372)
const relationships = await brain.getRelations({ from: documentId, limit: 500 })
const relationships = await brain.related({ from: documentId, limit: 500 })
expect(relationships.length).toBe(372)
})
@ -126,7 +126,7 @@ describe('Batch Import with Immediate Relations (v5.7.3 Fix)', () => {
expect(relationId).toBeTruthy()
// Verify relationship exists
const relations = await brain.getRelations(addResult.successful[0])
const relations = await brain.related(addResult.successful[0])
expect(relations.length).toBe(1)
expect(relations[0].to).toBe(addResult.successful[1])
})
@ -263,7 +263,7 @@ describe('Batch Import with Immediate Relations (v5.7.3 Fix)', () => {
expect(relationIds.length).toBe(100)
// 4. Verify structure
const childrenRelations = await brain.getRelations(rootId)
const childrenRelations = await brain.related(rootId)
expect(childrenRelations.length).toBe(100)
})

View file

@ -238,7 +238,7 @@ describe('Brainy 3.0 Core (Integration Tests - Real AI)', () => {
const [alice, bob, project] = entityIds
// Get Alice's relationships
const aliceRelations = await brain.getRelations({ from: alice })
const aliceRelations = await brain.related({ from: alice })
expect(aliceRelations).toBeDefined()
expect(aliceRelations.length).toBeGreaterThan(0)

View file

@ -188,7 +188,7 @@ describe('Clear Persistence Bug Fix (v5.10.4)', () => {
// Verify data exists
expect((await brain1.find({ type: 'person' })).length).toBe(1)
expect((await brain1.find({ type: 'concept' })).length).toBe(1)
expect((await brain1.getRelations({})).length).toBe(1)
expect((await brain1.related({})).length).toBe(1)
// Clear
await brain1.clear()
@ -200,7 +200,7 @@ describe('Clear Persistence Bug Fix (v5.10.4)', () => {
// Verify everything is cleared
expect((await brain2.find({ type: 'person' })).length).toBe(0)
expect((await brain2.find({ type: 'concept' })).length).toBe(0)
expect((await brain2.getRelations({})).length).toBe(0)
expect((await brain2.related({})).length).toBe(0)
})
it('should handle clear() on empty storage', async () => {

View file

@ -262,7 +262,7 @@ describe('8.0 Db API — generational MVCC', () => {
// ZERO ops applied: generation unchanged, the add rolled back, no edge.
expect(brain.generation()).toBe(g0)
expect(await brain.get(uid('atomic-b'))).toBeNull()
expect((await brain.getRelations({ from: uid('atomic-b') })).length).toBe(0)
expect((await brain.related({ from: uid('atomic-b') })).length).toBe(0)
expect((await brain.find({ type: NounType.Person, limit: 10 })).length).toBe(0)
// No partial generation records: the staging directory was removed.
@ -388,7 +388,7 @@ describe('8.0 Db API — generational MVCC', () => {
for (const id of ids) {
preState.set(id, await brain.get(id))
}
const preRelations = await brain.getRelations({ from: ids[0] })
const preRelations = await brain.related({ from: ids[0] })
expect(preRelations.length).toBe(1)
const snapDir = path.join(makeTempDir(), 'snapshot')
@ -596,7 +596,7 @@ describe('8.0 Db API — generational MVCC', () => {
expect(brain.generation()).toBe(g0)
expect(((await brain.get(uid('with-a')))?.metadata as { v: number }).v).toBe(1)
expect(await brain.get(uid('with-new'))).toBeNull()
expect((await brain.getRelations({ from: uid('with-a') })).length).toBe(0)
expect((await brain.related({ from: uid('with-a') })).length).toBe(0)
await spec.release()
await db.release()
@ -833,7 +833,7 @@ describe('8.0 Db API — generational MVCC', () => {
expect(db.receipt!.ids[1]).toBe(uid('rcpt-bb'))
expect(db.receipt!.ids[3]).toBe(uid('rcpt-bb'))
const relationId = db.receipt!.ids[2]
expect((await brain.getRelations({ from: uid('rcpt-aa') }))[0].id).toBe(relationId)
expect((await brain.related({ from: uid('rcpt-aa') }))[0].id).toBe(relationId)
// Duplicate relate (same from/to/type) dedupes to the SAME id — both
// within a batch and against committed state, mirroring relate().
@ -841,7 +841,7 @@ describe('8.0 Db API — generational MVCC', () => {
{ op: 'relate', from: uid('rcpt-aa'), to: uid('rcpt-bb'), type: VerbType.Knows }
])
expect(dup.receipt!.ids[0]).toBe(relationId)
expect((await brain.getRelations({ from: uid('rcpt-aa') })).length).toBe(1)
expect((await brain.related({ from: uid('rcpt-aa') })).length).toBe(1)
await db.release()
await dup.release()
@ -927,7 +927,7 @@ describe('8.0 Db API — generational MVCC', () => {
const db = brain.now()
// Rewire the graph after the pin: a→b becomes a→c.
const oldEdge = (await brain.getRelations({ from: uid('trav-a') }))[0]
const oldEdge = (await brain.related({ from: uid('trav-a') }))[0]
await brain.transact([
{ op: 'unrelate', id: oldEdge.id },
{ op: 'relate', from: uid('trav-a'), to: uid('trav-c'), type: VerbType.References }

View file

@ -1,8 +1,9 @@
/**
* Integration Tests for getRelations() Fix (v4.1.3)
* Integration Tests for related() call patterns
*
* Tests for Bug: getRelations() returns empty array when called without parameters
* This validates that the fix allows retrieving all relationships with proper pagination
* Regression coverage (originally a v4.1.3 fix): related() must return all
* relationships with proper pagination when called without parameters, and
* support the string-id shorthand.
*
* NO MOCKS - Real integration tests with actual storage
*/
@ -12,7 +13,7 @@ import { Brainy } from '../../src/brainy.js'
import { NounType, VerbType } from '../../src/types/graphTypes.js'
import * as fs from 'fs/promises'
describe('getRelations() Fix (v4.1.3)', () => {
describe('related() call patterns', () => {
let brain: Brainy
const testPath = './test-brainy-get-relations-fix'
@ -56,7 +57,7 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Get all relationships - THIS IS THE BUG FIX!
const relations = await brain.getRelations()
const relations = await brain.related()
// Should return all 3 relationships
expect(relations).toHaveLength(3)
@ -70,7 +71,7 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Get all relationships
const relations = await brain.getRelations()
const relations = await brain.related()
// Should return empty array
expect(relations).toHaveLength(0)
@ -100,11 +101,11 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Get first page (limit: 10)
const page1 = await brain.getRelations({ limit: 10 })
const page1 = await brain.related({ limit: 10 })
expect(page1).toHaveLength(10)
// Get second page (offset: 10, limit: 10)
const page2 = await brain.getRelations({ offset: 10, limit: 10 })
const page2 = await brain.related({ offset: 10, limit: 10 })
expect(page2).toHaveLength(10)
// Ensure no duplicates between pages
@ -127,12 +128,12 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Get only FriendOf relationships
const friendRelations = await brain.getRelations({ type: VerbType.FriendOf })
const friendRelations = await brain.related({ type: VerbType.FriendOf })
expect(friendRelations).toHaveLength(2)
expect(friendRelations.every(r => r.type === VerbType.FriendOf)).toBe(true)
// Get only WorksWith relationships
const workRelations = await brain.getRelations({ type: VerbType.WorksWith })
const workRelations = await brain.related({ type: VerbType.WorksWith })
expect(workRelations).toHaveLength(1)
expect(workRelations[0].type).toBe(VerbType.WorksWith)
})
@ -152,14 +153,14 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Get all relationships first to verify total count
const allRelations = await brain.getRelations()
const allRelations = await brain.related()
expect(allRelations).toHaveLength(4)
// Get FriendOf and WorksWith relationships using type array filter
// NOTE: Current storage layer may not support array filters directly
// So we test each type separately and combine
const friendRelations = await brain.getRelations({ type: VerbType.FriendOf })
const workRelations = await brain.getRelations({ type: VerbType.WorksWith })
const friendRelations = await brain.related({ type: VerbType.FriendOf })
const workRelations = await brain.related({ type: VerbType.WorksWith })
expect(friendRelations).toHaveLength(2)
expect(workRelations).toHaveLength(1)
@ -169,7 +170,7 @@ describe('getRelations() Fix (v4.1.3)', () => {
})
describe('String ID Shorthand Syntax', () => {
it('should support string ID shorthand: getRelations(id)', async () => {
it('should support string ID shorthand: related(id)', async () => {
// Create entities
const person1 = await brain.add({ data: 'Alice', type: NounType.Person })
const person2 = await brain.add({ data: 'Bob', type: NounType.Person })
@ -181,14 +182,14 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Use string shorthand - THIS IS THE NEW SIGNATURE!
const relations = await brain.getRelations(person1)
const relations = await brain.related(person1)
// Should return both relationships from person1
expect(relations).toHaveLength(2)
expect(relations.every(r => r.from === person1)).toBe(true)
})
it('should be equivalent to getRelations({ from: id })', async () => {
it('should be equivalent to related({ from: id })', async () => {
// Create entities and relationships
const person1 = await brain.add({ data: 'Alice', type: NounType.Person })
const person2 = await brain.add({ data: 'Bob', type: NounType.Person })
@ -196,8 +197,8 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Both syntaxes should return the same results
const shorthand = await brain.getRelations(person1)
const explicit = await brain.getRelations({ from: person1 })
const shorthand = await brain.related(person1)
const explicit = await brain.related({ from: person1 })
expect(shorthand).toEqual(explicit)
})
@ -217,7 +218,7 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Get relationships from person1
const relations = await brain.getRelations({ from: person1 })
const relations = await brain.related({ from: person1 })
expect(relations).toHaveLength(2)
expect(relations.every(r => r.from === person1)).toBe(true)
@ -235,7 +236,7 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Get relationships to person3
const relations = await brain.getRelations({ to: person3 })
const relations = await brain.related({ to: person3 })
expect(relations).toHaveLength(2)
expect(relations.every(r => r.to === person3)).toBe(true)
@ -253,7 +254,7 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Get only FriendOf relationships from person1
const relations = await brain.getRelations({
const relations = await brain.related({
from: person1,
type: VerbType.FriendOf
})
@ -290,7 +291,7 @@ describe('getRelations() Fix (v4.1.3)', () => {
// Should handle query without issues
const startTime = Date.now()
const relations = await brain.getRelations({ limit: 100 })
const relations = await brain.related({ limit: 100 })
const duration = Date.now() - startTime
expect(relations).toHaveLength(100)
@ -331,11 +332,11 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Verify we have 150 total relationships
const allRelations = await brain.getRelations({ limit: 200 })
const allRelations = await brain.related({ limit: 200 })
expect(allRelations.length).toBe(150)
// Call without limit - should default to 100
const relations = await brain.getRelations()
const relations = await brain.related()
// Should return exactly 100 (default limit)
expect(relations).toHaveLength(100)
@ -364,11 +365,11 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// Verify we have 50 total
const all = await brain.getRelations({ limit: 100 })
const all = await brain.related({ limit: 100 })
expect(all.length).toBe(50)
// Custom limit of 25
const relations = await brain.getRelations({ limit: 25 })
const relations = await brain.related({ limit: 25 })
expect(relations).toHaveLength(25)
})
})
@ -377,7 +378,7 @@ describe('getRelations() Fix (v4.1.3)', () => {
it('should reproduce and fix the a consumer team bug scenario', async () => {
// Reproduce the exact scenario from the bug report:
// - 524 relationships exist in GraphAdjacencyIndex
// - brain.getRelations() was returning empty array
// - brain.related() was returning empty array
// Create entities similar to Consumer import
const entities = []
@ -407,7 +408,7 @@ describe('getRelations() Fix (v4.1.3)', () => {
await brain.flush()
// BUG FIX TEST: This should now return relationships, not empty array!
const relations = await brain.getRelations()
const relations = await brain.related()
// CRITICAL: Should NOT be empty
expect(relations.length).toBeGreaterThan(0)

View file

@ -0,0 +1,339 @@
/**
* GraphAdjacencyIndex Pagination Tests (v5.8.0, updated for the 8.0 u64 contract)
*
* Tests for pagination support in GraphIndex methods:
* - getNeighbors() with limit/offset
* - getVerbIdsBySource() with limit/offset
* - getVerbIdsByTarget() with limit/offset
*
* 8.0 BigInt boundary: entity ints in (resolved via the metadata index's
* idMapper), entity/verb ints out (`bigint[]`). Entity ints map back to UUIDs
* via `idMapper.getUuid(Number(int))`; verb ints via `verbIntsToIds()`.
*/
import { describe, it, expect, beforeEach } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { NounType, VerbType } from '../../src/types/graphTypes.js'
describe('GraphAdjacencyIndex Pagination', () => {
let brain: Brainy
let centralId: string
let neighborIds: string[]
/** The brain's graph index (BigInt provider contract). */
const graphIndex = () => (brain as any).graphIndex
/** The shared UUID ↔ int resolver. */
const idMapper = () => (brain as any).metadataIndex.getIdMapper()
/** Resolve a UUID to its entity int (must already be mapped). */
const entityInt = (uuid: string): bigint => {
const intId = idMapper().getInt(uuid)
expect(intId).toBeDefined()
return BigInt(intId!)
}
/** Map returned entity ints back to UUIDs. */
const intsToUuids = (ints: bigint[]): string[] =>
ints
.map((i) => idMapper().getUuid(Number(i)))
.filter((u: string | undefined): u is string => u !== undefined)
beforeEach(async () => {
brain = new Brainy({ requireSubtype: false })
await brain.init()
// Create central entity
centralId = await brain.add({
data: { name: 'Central Hub' },
type: NounType.Thing
})
// Create 50 neighbor entities with relationships
neighborIds = []
for (let i = 0; i < 50; i++) {
const neighborId = await brain.add({
data: { name: `Neighbor ${i}`, index: i },
type: NounType.Thing
})
neighborIds.push(neighborId)
// Create outgoing relationship from central to neighbor
await brain.relate({
from: centralId,
to: neighborId,
type: VerbType.RelatesTo
})
}
})
describe('getNeighbors() Pagination', () => {
it('should return all neighbors without pagination', async () => {
const neighborInts = await graphIndex().getNeighbors(entityInt(centralId))
const neighbors = intsToUuids(neighborInts)
// Should return all 50 neighbors
expect(neighbors).toHaveLength(50)
expect(neighbors.every((id: string) => neighborIds.includes(id))).toBe(true)
})
it('should return all outgoing neighbors with direction option', async () => {
const neighbors = await graphIndex().getNeighbors(entityInt(centralId), { direction: 'out' })
expect(neighbors).toHaveLength(50)
})
it('should paginate with limit only', async () => {
const page1Ints = await graphIndex().getNeighbors(entityInt(centralId), { limit: 10 })
const page1 = intsToUuids(page1Ints)
expect(page1).toHaveLength(10)
expect(page1.every((id: string) => neighborIds.includes(id))).toBe(true)
})
it('should paginate with offset only', async () => {
const all = await graphIndex().getNeighbors(entityInt(centralId))
const offsetResults = await graphIndex().getNeighbors(entityInt(centralId), { offset: 10 })
// Offset 10 should return all after first 10
expect(offsetResults).toHaveLength(all.length - 10)
})
it('should paginate with both limit and offset', async () => {
const central = entityInt(centralId)
const page1: bigint[] = await graphIndex().getNeighbors(central, { limit: 10, offset: 0 })
const page2: bigint[] = await graphIndex().getNeighbors(central, { limit: 10, offset: 10 })
const page3: bigint[] = await graphIndex().getNeighbors(central, { limit: 10, offset: 20 })
// Each page should have 10 results
expect(page1).toHaveLength(10)
expect(page2).toHaveLength(10)
expect(page3).toHaveLength(10)
// Pages should not overlap
const page2Set = new Set(page2)
const page3Set = new Set(page3)
const overlap12 = page1.filter((id) => page2Set.has(id))
const overlap23 = page2.filter((id) => page3Set.has(id))
expect(overlap12).toHaveLength(0)
expect(overlap23).toHaveLength(0)
})
it('should handle offset beyond total count', async () => {
const results = await graphIndex().getNeighbors(entityInt(centralId), { offset: 100 })
// Offset 100 > 50 total → empty array
expect(results).toHaveLength(0)
})
it('should handle limit = 0', async () => {
const results = await graphIndex().getNeighbors(entityInt(centralId), { limit: 0 })
expect(results).toHaveLength(0)
})
it('should paginate with direction filter', async () => {
// Get first 10 outgoing neighbors
const page1 = await graphIndex().getNeighbors(entityInt(centralId), {
direction: 'out',
limit: 10,
offset: 0
})
expect(page1).toHaveLength(10)
})
it('should handle pagination with incoming direction', async () => {
// Create some incoming relationships
const sourceId = await brain.add({
data: { name: 'Source' },
type: NounType.Thing
})
await brain.relate({
from: sourceId,
to: centralId,
type: VerbType.RelatesTo
})
// Get incoming neighbors with pagination
const incoming = await graphIndex().getNeighbors(entityInt(centralId), {
direction: 'in',
limit: 10
})
expect(incoming.length).toBeGreaterThan(0)
})
})
describe('getVerbIdsBySource() Pagination', () => {
it('should return all verb ints without pagination and resolve them back to ids', async () => {
const verbInts: bigint[] = await graphIndex().getVerbIdsBySource(entityInt(centralId))
// Should return all 50 verb ints
expect(verbInts).toHaveLength(50)
// Every int must resolve back to a verb-id string
const verbIds: (string | null)[] = await graphIndex().verbIntsToIds(verbInts)
expect(verbIds).toHaveLength(50)
expect(verbIds.every((id) => typeof id === 'string')).toBe(true)
})
it('should paginate verb ints with limit', async () => {
const page1 = await graphIndex().getVerbIdsBySource(entityInt(centralId), { limit: 10 })
expect(page1).toHaveLength(10)
})
it('should paginate verb ints with offset', async () => {
const all = await graphIndex().getVerbIdsBySource(entityInt(centralId))
const offsetResults = await graphIndex().getVerbIdsBySource(entityInt(centralId), { offset: 10 })
expect(offsetResults).toHaveLength(all.length - 10)
})
it('should paginate verb ints with limit and offset', async () => {
const central = entityInt(centralId)
const page1: bigint[] = await graphIndex().getVerbIdsBySource(central, { limit: 10, offset: 0 })
const page2: bigint[] = await graphIndex().getVerbIdsBySource(central, { limit: 10, offset: 10 })
const page3: bigint[] = await graphIndex().getVerbIdsBySource(central, { limit: 10, offset: 20 })
// Each page should have 10 results
expect(page1).toHaveLength(10)
expect(page2).toHaveLength(10)
expect(page3).toHaveLength(10)
// Pages should not overlap
const combined = [...page1, ...page2, ...page3]
const unique = new Set(combined)
expect(unique.size).toBe(30) // All unique
})
it('should handle pagination edge cases for verb ints', async () => {
// Offset beyond total
const beyondTotal = await graphIndex().getVerbIdsBySource(entityInt(centralId), { offset: 100 })
expect(beyondTotal).toHaveLength(0)
// Limit = 0
const zeroLimit = await graphIndex().getVerbIdsBySource(entityInt(centralId), { limit: 0 })
expect(zeroLimit).toHaveLength(0)
})
})
describe('getVerbIdsByTarget() Pagination', () => {
it('should return all verb ints targeting an entity', async () => {
// Pick a neighbor that's a target of relationships
const targetId = neighborIds[0]
const verbInts = await graphIndex().getVerbIdsByTarget(entityInt(targetId))
// Should have at least 1 (the relationship from central)
expect(verbInts.length).toBeGreaterThanOrEqual(1)
})
it('should paginate verb ints by target with limit', async () => {
// Create entity with many incoming relationships
const popularTarget = await brain.add({
data: { name: 'Popular Target' },
type: NounType.Thing
})
// Create 30 relationships pointing to it
for (let i = 0; i < 30; i++) {
const sourceId = await brain.add({
data: { name: `Source ${i}` },
type: NounType.Thing
})
await brain.relate({
from: sourceId,
to: popularTarget,
type: VerbType.RelatesTo
})
}
// Paginate
const target = entityInt(popularTarget)
const page1: bigint[] = await graphIndex().getVerbIdsByTarget(target, { limit: 10 })
const page2: bigint[] = await graphIndex().getVerbIdsByTarget(target, { limit: 10, offset: 10 })
expect(page1).toHaveLength(10)
expect(page2).toHaveLength(10)
// No overlap
const overlap = page1.filter((id) => page2.includes(id))
expect(overlap).toHaveLength(0)
})
})
describe('Performance with Pagination', () => {
it('should maintain sub-5ms performance with pagination', async () => {
const central = entityInt(centralId)
// Multiple paginated queries should be fast
const startTime = performance.now()
await graphIndex().getNeighbors(central, { limit: 10, offset: 0 })
await graphIndex().getNeighbors(central, { limit: 10, offset: 10 })
await graphIndex().getNeighbors(central, { limit: 10, offset: 20 })
const elapsed = performance.now() - startTime
// 3 paginated queries should complete in < 15ms (< 5ms each)
expect(elapsed).toBeLessThan(15)
})
})
describe('Real-World Use Cases', () => {
it('should efficiently paginate through high-degree node', async () => {
// Simulate popular entity with 100+ relationships
const hub = await brain.add({
data: { name: 'Popular Hub' },
type: NounType.Thing
})
// Create 100 relationships
const targetIds: string[] = []
for (let i = 0; i < 100; i++) {
const targetId = await brain.add({
data: { name: `Target ${i}` },
type: NounType.Thing
})
targetIds.push(targetId)
await brain.relate({
from: hub,
to: targetId,
type: VerbType.RelatesTo
})
}
// Paginate in chunks of 25
const hubInt = entityInt(hub)
const chunks: bigint[][] = []
for (let offset = 0; offset < 100; offset += 25) {
const chunk = await graphIndex().getNeighbors(hubInt, {
direction: 'out',
limit: 25,
offset
})
chunks.push(chunk)
}
// Should have 4 chunks
expect(chunks).toHaveLength(4)
// Each chunk should have 25 items
chunks.forEach(chunk => {
expect(chunk).toHaveLength(25)
})
// All chunks combined should equal total
const allNeighbors = chunks.flat()
expect(allNeighbors).toHaveLength(100)
// No duplicates
const uniqueNeighbors = new Set(allNeighbors)
expect(uniqueNeighbors.size).toBe(100)
})
})
})

View file

@ -413,7 +413,7 @@ describe('HNSW Index Rebuild (Integration Tests)', () => {
// Verify all indexes work
const searchResults = await brain1.find({ query: 'engineer', limit: 5 })
const relations = await brain1.getRelations({ from: alice })
const relations = await brain1.related({ from: alice })
const metadataResults = await brain1.find({ where: { role: 'engineer' } })
expect(searchResults.length).toBeGreaterThan(0)
@ -450,7 +450,7 @@ describe('HNSW Index Rebuild (Integration Tests)', () => {
console.log('✅ HNSW index operational')
// 2. Graph Adjacency Index (Bug #1 fix)
const relations2 = await brain2.getRelations({ from: alice })
const relations2 = await brain2.related({ from: alice })
expect(relations2.length).toBe(relations.length)
console.log('✅ Graph index operational')

View file

@ -183,11 +183,11 @@ describe('Metadata-Only Comprehensive Integration (v5.11.1)', () => {
expect(entity!.vector).toEqual([]) // Still metadata-only
})
it('brain.delete() should work after metadata-only get', async () => {
it('brain.remove() should work after metadata-only get', async () => {
const entity = await brain.get(entityId)
expect(entity).toBeTruthy()
await brain.delete(entityId)
await brain.remove(entityId)
const deleted = await brain.get(entityId)
expect(deleted).toBeNull()

View file

@ -58,13 +58,11 @@ describe('Metadata Vector Exclusion Fix', () => {
// Check _system directory for chunk files
const systemDir = join(testDir, '_system')
if (!existsSync(systemDir)) {
// No chunk files created - this is acceptable
expect(true).toBe(true)
return
}
const chunkFiles = readdirSync(systemDir).filter(f => f.startsWith('__chunk__'))
// No _system dir means no chunk files at all — the invariant below
// (no vector-dimension chunks) holds trivially on an empty list.
const chunkFiles = existsSync(systemDir)
? readdirSync(systemDir).filter(f => f.startsWith('__chunk__'))
: []
// Should have at most a few chunk files (name, email, tags)
// NOT hundreds of files from vector dimensions
@ -105,12 +103,10 @@ describe('Metadata Vector Exclusion Fix', () => {
const systemDir = join(testDir, '_system')
if (!existsSync(systemDir)) {
expect(true).toBe(true)
return
}
const chunkFiles = readdirSync(systemDir).filter(f => f.startsWith('__chunk__'))
// No _system dir means no chunk files — the assertions below hold on [].
const chunkFiles = existsSync(systemDir)
? readdirSync(systemDir).filter(f => f.startsWith('__chunk__'))
: []
// CRITICAL: Check that NO chunk files have numeric field names
// This is the v3.50.2 fix - prevents vectors-as-objects from being indexed
@ -167,12 +163,10 @@ describe('Metadata Vector Exclusion Fix', () => {
// Check chunk count - should NOT create 100 chunk files
const systemDir = join(testDir, '_system')
if (!existsSync(systemDir)) {
expect(true).toBe(true)
return
}
const chunkFiles = readdirSync(systemDir).filter(f => f.startsWith('__chunk__'))
// No _system dir means no chunk files — the assertions below hold on [].
const chunkFiles = existsSync(systemDir)
? readdirSync(systemDir).filter(f => f.startsWith('__chunk__'))
: []
// Should have minimal chunk files (just 'name' field)
expect(chunkFiles.length).toBeLessThan(5)
@ -295,12 +289,10 @@ describe('Metadata Vector Exclusion Fix', () => {
// Check total chunk files
const systemDir = join(testDir, '_system')
if (!existsSync(systemDir)) {
expect(true).toBe(true)
return
}
const chunkFiles = readdirSync(systemDir).filter(f => f.startsWith('__chunk__'))
// No _system dir means no chunk files — the assertions below hold on [].
const chunkFiles = existsSync(systemDir)
? readdirSync(systemDir).filter(f => f.startsWith('__chunk__'))
: []
// Should have reasonable number of chunks (not 7,210 for 10 entities!)
// Expected: ~30 chunks (name, email, tags fields across 10 entities)

View file

@ -60,8 +60,8 @@ describe('Multi-process safety + read-only mode', () => {
await expect(reader.add({ data: 'x', type: NounType.Concept })).rejects.toThrow(/read-only/i)
await expect(reader.addMany({ items: [] })).rejects.toThrow(/read-only/i)
await expect(reader.update({ id: 'x', data: 'y' })).rejects.toThrow(/read-only/i)
await expect(reader.delete('x')).rejects.toThrow(/read-only/i)
await expect(reader.deleteMany({ ids: ['x'] } as any)).rejects.toThrow(/read-only/i)
await expect(reader.remove('x')).rejects.toThrow(/read-only/i)
await expect(reader.removeMany({ ids: ['x'] } as any)).rejects.toThrow(/read-only/i)
await expect(reader.relate({ from: 'x', to: 'y' } as any)).rejects.toThrow(/read-only/i)
await expect(reader.unrelate('x')).rejects.toThrow(/read-only/i)
})

View file

@ -114,7 +114,7 @@ describe('Read-After-Write Consistency (v5.7.2 Bug Fix)', () => {
expect(typeof relationId).toBe('string')
// Verify relationship exists and is correct
const relations = await brain.getRelations(sourceId)
const relations = await brain.related(sourceId)
expect(relations).toHaveLength(1)
expect(relations[0].to).toBe(targetId)
expect(relations[0].type).toBe(VerbType.Contains)
@ -170,7 +170,7 @@ describe('Read-After-Write Consistency (v5.7.2 Bug Fix)', () => {
console.log(`Created 100 relationships in ${Date.now() - relStartTime}ms`)
// Verify all relationships created successfully
const relations = await brain.getRelations(documentId)
const relations = await brain.related(documentId)
expect(relations.length).toBe(100)
// Verify each entity is still queryable
@ -202,7 +202,7 @@ describe('Read-After-Write Consistency (v5.7.2 Bug Fix)', () => {
expect(entity?.metadata?.updated).toBe(true)
})
it('should verify entity is deleted after brain.delete()', async () => {
it('should verify entity is deleted after brain.remove()', async () => {
// Create entity
const entityId = await brain.add({
data: 'Entity to be deleted',
@ -214,7 +214,7 @@ describe('Read-After-Write Consistency (v5.7.2 Bug Fix)', () => {
expect(beforeDelete).not.toBeNull()
// Delete
await brain.delete(entityId)
await brain.remove(entityId)
// Verify it's deleted (should return null)
const afterDelete = await brain.get(entityId)
@ -260,13 +260,13 @@ describe('Read-After-Write Consistency (v5.7.2 Bug Fix)', () => {
await brain.relate({ from: concept2Id, to: concept3Id, type: VerbType.RelatedTo })
// Verify all relationships exist
const docRelations = await brain.getRelations(documentId)
const docRelations = await brain.related(documentId)
expect(docRelations.length).toBe(3)
const concept1Relations = await brain.getRelations(concept1Id)
const concept1Relations = await brain.related(concept1Id)
expect(concept1Relations.length).toBeGreaterThan(0)
const concept2Relations = await brain.getRelations(concept2Id)
const concept2Relations = await brain.related(concept2Id)
expect(concept2Relations.length).toBeGreaterThan(0)
})
@ -325,7 +325,7 @@ describe('Read-After-Write Consistency (v5.7.2 Bug Fix)', () => {
}
// Step 5: Verify relationships exist
const relations = await brain.getRelations(documentId)
const relations = await brain.related(documentId)
expect(relations.length).toBe(10)
})
})

View file

@ -94,7 +94,7 @@ describe('Relationship Intelligence', () => {
console.log('='.repeat(80))
// Get all relationships
const allRelations = await brain.getRelations()
const allRelations = await brain.related()
console.log(`\n📊 Total relationships: ${allRelations.length}`)
// Find relationships involving Arrowhead
@ -104,7 +104,7 @@ describe('Relationship Intelligence', () => {
})
expect(arrowheadEntity.length).toBe(1)
const arrowheadRelations = await brain.getRelations({
const arrowheadRelations = await brain.related({
from: arrowheadEntity[0].id
})

View file

@ -88,7 +88,7 @@ describe('S3 Storage Integration', () => {
metadata: {}
});
const deleted = await brain.delete(id);
const deleted = await brain.remove(id);
expect(deleted).toBe(true);
const retrieved = await brain.get(id);

View file

@ -359,7 +359,7 @@ projects:
expect(monaLisaEntities.length).toBeGreaterThan(0)
// Get relationships for Mona Lisa
const relationships = await brain.getRelations(monaLisaEntities[0].id)
const relationships = await brain.related(monaLisaEntities[0].id)
expect(relationships.length).toBeGreaterThan(0)
// Should have CreatedBy relationship (not just RelatedTo)
@ -406,7 +406,7 @@ projects:
expect(stanfordEntities.length).toBeGreaterThan(0)
// Get relationships
const relationships = await brain.getRelations(stanfordEntities[0].id)
const relationships = await brain.related(stanfordEntities[0].id)
expect(relationships.length).toBeGreaterThan(0)
// Should have LocatedAt relationship
@ -453,7 +453,7 @@ projects:
expect(heartEntities.length).toBeGreaterThan(0)
// Get relationships
const relationships = await brain.getRelations(heartEntities[0].id)
const relationships = await brain.related(heartEntities[0].id)
expect(relationships.length).toBeGreaterThan(0)
// Should have PartOf relationship
@ -500,7 +500,7 @@ projects:
expect(aliceEntities.length).toBeGreaterThan(0)
// Get relationships
const relationships = await brain.getRelations(aliceEntities[0].id)
const relationships = await brain.related(aliceEntities[0].id)
expect(relationships.length).toBeGreaterThan(0)
// Should have WorksWith or MemberOf relationship (not just RelatedTo)
@ -548,7 +548,7 @@ projects:
expect(iphoneEntities.length).toBeGreaterThan(0)
// Get relationships
const relationships = await brain.getRelations(iphoneEntities[0].id)
const relationships = await brain.related(iphoneEntities[0].id)
expect(relationships.length).toBeGreaterThan(0)
// Should have CreatedBy relationship with high confidence
@ -593,7 +593,7 @@ project:
expect(projectEntities.length).toBeGreaterThan(0)
// Get relationships
const relationships = await brain.getRelations(projectEntities[0].id)
const relationships = await brain.related(projectEntities[0].id)
expect(relationships.length).toBeGreaterThan(0)
// Should have Contains relationship (hierarchical)

View file

@ -147,7 +147,7 @@ describe('Subtype + Facets (7.29.0)', () => {
const id = await brain.add({ data: 'g', type: NounType.Person, subtype: 'employee' })
expect(brain.counts.bySubtype(NounType.Person, 'employee')).toBe(3)
await brain.delete(id)
await brain.remove(id)
expect(brain.counts.bySubtype(NounType.Person, 'employee')).toBe(2)
})

View file

@ -4,7 +4,7 @@
* opt-in enforcement primitives:
*
* - Layer V1: `subtype` on `relate()` / `Relation` / `RelateParams` round-trips
* through `getRelations({ subtype })`, the new `updateRelation()` method, and
* through `related({ subtype })`, the new `updateRelation()` method, and
* the persisted verb-subtype rollup.
* - Layer V2: `brain.counts.byRelationshipSubtype` / `topRelationshipSubtypes`
* / `brain.relationshipSubtypesOf` mirror the noun-side counts API.
@ -33,7 +33,7 @@ describe('Verb subtype + enforcement (7.30.0)', () => {
})
describe('Layer V1: subtype on relationships', () => {
it('persists subtype on relate() and round-trips through getRelations()', async () => {
it('persists subtype on relate() and round-trips through related()', async () => {
const fromId = await brain.add({ data: 'Avery', type: NounType.Person })
const toId = await brain.add({ data: 'Jordan', type: NounType.Person })
@ -44,14 +44,14 @@ describe('Verb subtype + enforcement (7.30.0)', () => {
subtype: 'direct'
})
const all = await brain.getRelations({ from: fromId })
const all = await brain.related({ from: fromId })
const found = all.find(r => r.id === relId)
expect(found).toBeDefined()
expect(found!.subtype).toBe('direct')
expect(found!.type).toBe(VerbType.ReportsTo)
})
it('getRelations({ subtype }) filters by single value', async () => {
it('related({ subtype }) filters by single value', async () => {
const a = await brain.add({ data: 'A', type: NounType.Person })
const b = await brain.add({ data: 'B', type: NounType.Person })
const c = await brain.add({ data: 'C', type: NounType.Person })
@ -59,13 +59,13 @@ describe('Verb subtype + enforcement (7.30.0)', () => {
await brain.relate({ from: a, to: b, type: VerbType.ReportsTo, subtype: 'direct' })
await brain.relate({ from: a, to: c, type: VerbType.ReportsTo, subtype: 'dotted-line' })
const direct = await brain.getRelations({ from: a, subtype: 'direct' })
const direct = await brain.related({ from: a, subtype: 'direct' })
expect(direct).toHaveLength(1)
expect(direct[0].to).toBe(b)
expect(direct[0].subtype).toBe('direct')
})
it('getRelations({ subtype: [...] }) filters by set membership', async () => {
it('related({ subtype: [...] }) filters by set membership', async () => {
const a = await brain.add({ data: 'A', type: NounType.Person })
const b = await brain.add({ data: 'B', type: NounType.Person })
const c = await brain.add({ data: 'C', type: NounType.Person })
@ -75,7 +75,7 @@ describe('Verb subtype + enforcement (7.30.0)', () => {
await brain.relate({ from: a, to: c, type: VerbType.ReportsTo, subtype: 'dotted-line' })
await brain.relate({ from: a, to: d, type: VerbType.ReportsTo, subtype: 'matrix' })
const both = await brain.getRelations({
const both = await brain.related({
from: a,
type: VerbType.ReportsTo,
subtype: ['direct', 'dotted-line']
@ -96,7 +96,7 @@ describe('Verb subtype + enforcement (7.30.0)', () => {
await brain.updateRelation({ id: relId, subtype: 'dotted-line' })
const after = await brain.getRelations({ from: a, type: VerbType.ReportsTo })
const after = await brain.related({ from: a, type: VerbType.ReportsTo })
const found = after.find(r => r.id === relId)
expect(found!.subtype).toBe('dotted-line')
})
@ -113,7 +113,7 @@ describe('Verb subtype + enforcement (7.30.0)', () => {
await brain.updateRelation({ id: relId, weight: 0.5 })
const after = await brain.getRelations({ from: a, type: VerbType.ReportsTo })
const after = await brain.related({ from: a, type: VerbType.ReportsTo })
const found = after.find(r => r.id === relId)
expect(found!.subtype).toBe('direct')
expect(found!.weight).toBe(0.5)
@ -253,7 +253,7 @@ describe('Verb subtype + enforcement (7.30.0)', () => {
expect(result.errors).toEqual([])
expect(result.migrated).toBe(1)
const after = await brain.getRelations({ from: a })
const after = await brain.related({ from: a })
const found = after.find(r => r.id === relId)
expect(found!.subtype).toBe('spouse')
expect((found!.metadata as any).kind).toBeUndefined()
@ -289,7 +289,7 @@ describe('Verb subtype + enforcement (7.30.0)', () => {
expect(ea!.subtype).toBe('employee')
const eb = await brain.get(b)
expect(eb!.subtype).toBe('employee')
const rels = await brain.getRelations({ from: a })
const rels = await brain.related({ from: a })
expect(rels[0].subtype).toBe('colleague')
})
@ -311,7 +311,7 @@ describe('Verb subtype + enforcement (7.30.0)', () => {
})
expect(result.migrated).toBe(1)
const rels = await brain.getRelations({ from: a })
const rels = await brain.related({ from: a })
const found = rels.find(r => r.id === relId)
expect(found!.subtype).toBe('sibling')
expect((found!.metadata as any).kind).toBe('sibling')

View file

@ -268,7 +268,7 @@ describe('VFS API Wiring Verification', () => {
console.log(` Created relation: ${relationId}`)
// Verify relationship exists
const relations = await brain.getRelations({
const relations = await brain.related({
from: conceptId,
to: vfsFile[0].id
})

View file

@ -134,7 +134,7 @@ describe('VFS-Knowledge Separation (Option 3C)', () => {
console.log(` To: ${vfsFile[0].metadata?.path} (VFS file)`)
// Verify relationship exists
const relations = await brain.getRelations({
const relations = await brain.related({
from: knowledgeEntity[0].id,
to: vfsFile[0].id
})