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

@ -76,7 +76,7 @@ describe('Brainy 3.0 Core (Unit Tests)', () => {
expect(await brain.get(id)).toBeTruthy()
// Delete it
await brain.delete(id)
await brain.remove(id)
// Verify it's gone
expect(await brain.get(id)).toBeNull()
@ -96,7 +96,7 @@ describe('Brainy 3.0 Core (Unit Tests)', () => {
})).rejects.toThrow()
// delete should not throw for non-existent ID
await expect(brain.delete(fakeId)).resolves.not.toThrow()
await expect(brain.remove(fakeId)).resolves.not.toThrow()
})
})
@ -199,9 +199,11 @@ describe('Brainy 3.0 Core (Unit Tests)', () => {
data: { name: 'Test2' },
type: NounType.Concept
})
// Statistics would be available through augmentation system
// The exact API depends on augmentation configuration
const stats = await brain.stats()
expect(stats.mode).toBe('writer')
expect(stats.entityCount).toBeGreaterThanOrEqual(2)
expect(stats.entitiesByType[NounType.Concept]).toBeGreaterThanOrEqual(2)
})
})

View file

@ -155,7 +155,7 @@ describe('Brainy Batch Operations', () => {
})
})
describe('deleteMany - Batch Deletion', () => {
describe('removeMany - Batch Deletion', () => {
let testIds: string[]
beforeEach(async () => {
@ -171,7 +171,7 @@ describe('Brainy Batch Operations', () => {
})
it('should delete multiple entities at once', async () => {
await brain.deleteMany({ ids: testIds })
await brain.removeMany({ ids: testIds })
// All should be gone
for (const id of testIds) {
@ -185,7 +185,7 @@ describe('Brainy Batch Operations', () => {
const toDelete = [testIds[0], testIds[2], testIds[4]]
const toKeep = [testIds[1], testIds[3]]
await brain.deleteMany({ ids: toDelete })
await brain.removeMany({ ids: toDelete })
// Deleted ones should be gone
for (const id of toDelete) {
@ -212,7 +212,7 @@ describe('Brainy Batch Operations', () => {
await brain.relate({ from: person2, to: org, type: VerbType.MemberOf as any })
// Delete the organization
await brain.deleteMany({ ids: [org] })
await brain.removeMany({ ids: [org] })
// Organization should be gone
const deletedOrg = await brain.get(org)
@ -237,7 +237,7 @@ describe('Brainy Batch Operations', () => {
const manyIds = manyResult.successful
const startTime = Date.now()
await brain.deleteMany({ ids: manyIds })
await brain.removeMany({ ids: manyIds })
const duration = Date.now() - startTime
// v5.4.0: Increased to 14500ms for type-first storage + system load variance
@ -281,7 +281,7 @@ describe('Brainy Batch Operations', () => {
expect(relationIds).toHaveLength(3)
// Verify relationships exist
const person1Relations = await brain.getRelations({ from: entities[0] })
const person1Relations = await brain.related({ from: entities[0] })
expect(person1Relations.length).toBeGreaterThan(0)
})
@ -297,13 +297,13 @@ describe('Brainy Batch Operations', () => {
expect(relationIds).toHaveLength(3)
// Verify different types
const friendRelations = await brain.getRelations({
const friendRelations = await brain.related({
from: entities[0],
type: VerbType.FriendOf
})
expect(friendRelations.length).toBeGreaterThan(0)
const workRelations = await brain.getRelations({
const workRelations = await brain.related({
from: entities[0],
type: VerbType.WorksWith
})
@ -321,10 +321,10 @@ describe('Brainy Batch Operations', () => {
expect(relationIds).toHaveLength(2)
// Both should have the relationship
const person1Friends = await brain.getRelations({ from: entities[0] })
const person1Friends = await brain.related({ from: entities[0] })
expect(person1Friends.length).toBeGreaterThan(0)
const person2Friends = await brain.getRelations({ from: entities[1] })
const person2Friends = await brain.related({ from: entities[1] })
expect(person2Friends.length).toBeGreaterThan(0)
})
@ -358,7 +358,7 @@ describe('Brainy Batch Operations', () => {
expect(duration).toBeLessThan(1000) // Should be fast
// Verify company has all relationships
const companyRelations = await brain.getRelations({ to: company })
const companyRelations = await brain.related({ to: company })
expect(companyRelations.length).toBeGreaterThanOrEqual(50)
})
@ -460,7 +460,7 @@ describe('Brainy Batch Operations', () => {
await brain.relateMany({ items: relationships })
// 4. Delete some entities
await brain.deleteMany({ ids: initialIds.slice(15) })
await brain.removeMany({ ids: initialIds.slice(15) })
const totalTime = Date.now() - startTime
@ -473,7 +473,7 @@ describe('Brainy Batch Operations', () => {
const deleted = await brain.get(initialIds[19])
expect(deleted).toBeNull()
const relations = await brain.getRelations({ from: initialIds[0] })
const relations = await brain.related({ from: initialIds[0] })
expect(relations.length).toBeGreaterThan(0)
})
})
@ -487,7 +487,7 @@ describe('Brainy Batch Operations', () => {
expect(result.successful).toHaveLength(0)
await brain.updateMany({ items: [] })
await brain.deleteMany({ ids: [] })
await brain.removeMany({ ids: [] })
// Should not throw
})

View file

@ -169,9 +169,9 @@ describe('Brainy.fillSubtypes()', () => {
expect(report.filled).toBe(1)
expect(report.byType).toEqual({ relatedTo: 1 })
const relations = await brain.getRelations({ from: a })
const relations = await brain.related({ from: a })
expect(relations.find((r) => r.id === relId)!.subtype).toBe('unspecified')
const reverse = await brain.getRelations({ from: b })
const reverse = await brain.related({ from: b })
expect(reverse.find((r) => r.id === labeled)!.subtype).toBe('colleague') // untouched
})
@ -195,7 +195,7 @@ describe('Brainy.fillSubtypes()', () => {
})
expect(report.filled).toBe(1)
const relations = await brain.getRelations({ from: a })
const relations = await brain.related({ from: a })
expect(relations[0].subtype).toBe('dotted-line')
})

View file

@ -182,7 +182,7 @@ describe('Brainy.get()', () => {
data: 'To be deleted',
type: 'thing'
}))
await brain.delete(id)
await brain.remove(id)
// Act
const entity = await brain.get(id)

View file

@ -51,7 +51,7 @@ describe('Duplicate Check Optimization', () => {
expect(relationId2).toBe(relationId1)
// Verify only one relationship exists
const relations = await brain.getRelations({ from: personId })
const relations = await brain.related({ from: personId })
expect(relations).toHaveLength(1)
expect(relations[0].id).toBe(relationId1)
})
@ -85,7 +85,7 @@ describe('Duplicate Check Optimization', () => {
expect(relationId2).not.toBe(relationId1)
// Verify both relationships exist
const relations = await brain.getRelations({ from: personId })
const relations = await brain.related({ from: personId })
expect(relations).toHaveLength(2)
// Both relations should exist with different IDs
const relationIds = relations.map(r => r.id)
@ -129,7 +129,7 @@ describe('Duplicate Check Optimization', () => {
expect(elapsed).toBeLessThan(10)
// Verify duplicate was detected
const relations = await brain.getRelations({ from: sourceId })
const relations = await brain.related({ from: sourceId })
expect(relations).toHaveLength(50) // No duplicate created
})
@ -198,7 +198,7 @@ describe('Duplicate Check Optimization', () => {
})
// Verify both relationships exist
let relations = await brain.getRelations({ from: person })
let relations = await brain.related({ from: person })
expect(relations).toHaveLength(2)
const relationIds = relations.map(r => r.id)
expect(relationIds).toContain(rel1)
@ -218,7 +218,7 @@ describe('Duplicate Check Optimization', () => {
expect(duplicate).toBe(rel1)
// Verify still same number of relationships (no duplicate added)
const finalRelations = await brain.getRelations({ from: person })
const finalRelations = await brain.related({ from: person })
expect(finalRelations.length).toBe(relations.length)
})
})

View file

@ -61,7 +61,7 @@ describe('Brainy.relate()', () => {
expect(typeof relationId).toBe('string')
// Verify relationship exists
const relations = await brain.getRelations({ from: entity1Id })
const relations = await brain.related({ from: entity1Id })
expect(relations.length).toBeGreaterThan(0)
expect(relations.some(r => r.to === entity2Id)).toBe(true)
})
@ -76,7 +76,7 @@ describe('Brainy.relate()', () => {
})
// Assert
const relations = await brain.getRelations({ from: entity1Id })
const relations = await brain.related({ from: entity1Id })
const relation = relations.find(r => r.to === entity2Id)
expect(relation).toBeDefined()
expect(relation!.weight).toBe(0.8)
@ -99,7 +99,7 @@ describe('Brainy.relate()', () => {
})
// Assert
const relations = await brain.getRelations({ from: entity1Id })
const relations = await brain.related({ from: entity1Id })
const relation = relations.find(r => r.to === entity2Id)
expect(relation).toBeDefined()
expect(relation!.metadata || {}).toMatchObject(metadata)
@ -115,8 +115,8 @@ describe('Brainy.relate()', () => {
})
// Assert - Check both directions
const forwardRelations = await brain.getRelations({ from: entity1Id })
const reverseRelations = await brain.getRelations({ from: entity2Id })
const forwardRelations = await brain.related({ from: entity1Id })
const reverseRelations = await brain.related({ from: entity2Id })
expect(forwardRelations.some(r => r.to === entity2Id)).toBe(true)
expect(reverseRelations.some(r => r.to === entity1Id)).toBe(true)
@ -137,7 +137,7 @@ describe('Brainy.relate()', () => {
})
// Assert
const relations = await brain.getRelations({ from: entity1Id })
const relations = await brain.related({ from: entity1Id })
expect(relations.length).toBe(2)
expect(relations.some(r => r.to === entity2Id)).toBe(true)
expect(relations.some(r => r.to === entity3Id)).toBe(true)
@ -158,7 +158,7 @@ describe('Brainy.relate()', () => {
})
// Assert
const relations = await brain.getRelations({ from: entity1Id })
const relations = await brain.related({ from: entity1Id })
const toEntity2 = relations.filter(r => r.to === entity2Id)
expect(toEntity2.length).toBe(2)
expect(toEntity2.some(r => r.type === 'worksWith')).toBe(true)
@ -175,7 +175,7 @@ describe('Brainy.relate()', () => {
})
// Assert
const relations = await brain.getRelations({ from: entity1Id })
const relations = await brain.related({ from: entity1Id })
const selfRelation = relations.find(r => r.to === entity1Id)
expect(selfRelation).toBeDefined()
expect(selfRelation!.metadata?.type).toBe('self-reference')
@ -241,7 +241,7 @@ describe('Brainy.relate()', () => {
// Second call should return existing relationship ID instead of creating duplicate
expect(id1).toBe(id2)
const relations = await brain.getRelations({ from: entity1Id })
const relations = await brain.related({ from: entity1Id })
const matches = relations.filter(r =>
r.to === entity2Id && r.type === 'worksWith'
)
@ -267,7 +267,7 @@ describe('Brainy.relate()', () => {
})
// Assert
const relations = await brain.getRelations({ from: entity1Id })
const relations = await brain.related({ from: entity1Id })
const relation = relations.find(r => r.to === entity2Id)
expect(relation).toBeDefined()
expect(relation!.metadata?.bigArray).toHaveLength(100)
@ -291,7 +291,7 @@ describe('Brainy.relate()', () => {
})
// Assert
const relations = await brain.getRelations({ from: entity1Id })
const relations = await brain.related({ from: entity1Id })
const relation = relations.find(r => r.to === entity2Id)
expect(relation!.metadata || {}).toMatchObject(specialMetadata)
})
@ -356,8 +356,8 @@ describe('Brainy.relate()', () => {
})
// Assert - Multiple queries should return same data
const relations1 = await brain.getRelations({ from: entity1Id })
const relations2 = await brain.getRelations({ from: entity1Id })
const relations1 = await brain.related({ from: entity1Id })
const relations2 = await brain.related({ from: entity1Id })
expect(relations1.length).toBe(relations2.length)
const rel1 = relations1.find(r => r.to === entity2Id)
@ -386,7 +386,7 @@ describe('Brainy.relate()', () => {
})
// Assert - Relationship should still exist
const relations = await brain.getRelations({ from: entity1Id })
const relations = await brain.related({ from: entity1Id })
expect(relations.some(r => r.to === entity2Id)).toBe(true)
})
})
@ -407,8 +407,8 @@ describe('Brainy.relate()', () => {
})
// Act - Get relationships step by step
const step1 = await brain.getRelations({ from: entity1Id })
const entity2Relations = await brain.getRelations({ from: entity2Id })
const step1 = await brain.related({ from: entity1Id })
const entity2Relations = await brain.related({ from: entity2Id })
// Assert
expect(step1.some(r => r.to === entity2Id)).toBe(true)
@ -436,9 +436,9 @@ describe('Brainy.relate()', () => {
})
// Assert - All relationships should exist
const rel1 = await brain.getRelations({ from: entity1Id })
const rel2 = await brain.getRelations({ from: entity2Id })
const rel3 = await brain.getRelations({ from: entity3Id })
const rel1 = await brain.related({ from: entity1Id })
const rel2 = await brain.related({ from: entity2Id })
const rel3 = await brain.related({ from: entity3Id })
expect(rel1.some(r => r.to === entity2Id)).toBe(true)
expect(rel2.some(r => r.to === entity3Id)).toBe(true)

View file

@ -284,7 +284,7 @@ describe('reserved-field metadata remap (8.0 contract)', () => {
expect(result?.entity._rev).toBe(1)
})
it('getRelations() by target surfaces reserved fields top-level, custom-only metadata', async () => {
it('related() by target surfaces reserved fields top-level, custom-only metadata', async () => {
const a = await brain.add({ type: NounType.Person, subtype: 'employee', data: 'src' })
const b = await brain.add({ type: NounType.Person, subtype: 'employee', data: 'tgt' })
const relId = await brain.relate({
@ -298,7 +298,7 @@ describe('reserved-field metadata remap (8.0 contract)', () => {
metadata: { note: 'target path' }
})
const relations = await brain.getRelations({ to: b })
const relations = await brain.related({ to: b })
const rel = relations.find((r) => r.id === relId)
expect(rel).toBeDefined()
expect(rel?.metadata).toEqual({ note: 'target path' })
@ -329,7 +329,7 @@ describe('reserved-field metadata remap (8.0 contract)', () => {
service: 'orders'
})
const relations = await brain.getRelations({ from: a })
const relations = await brain.related({ from: a })
const rel = relations.find((r) => r.id === relId)
expect(rel?.confidence).toBe(0.77)
expect(rel?.service).toBe('orders')
@ -344,7 +344,7 @@ describe('reserved-field metadata remap (8.0 contract)', () => {
metadata: { confidence: 0.4, weight: 0.3, role: 'peer' } as object
})
const relations = await brain.getRelations({ from: a })
const relations = await brain.related({ from: a })
const rel = relations.find((r) => r.id === relId)
expect(rel?.confidence).toBe(0.4)
expect(rel?.weight).toBe(0.3)
@ -360,7 +360,7 @@ describe('reserved-field metadata remap (8.0 contract)', () => {
metadata: { note: 'no echo' }
})
const relations = await brain.getRelations({ from: a })
const relations = await brain.related({ from: a })
const rel = relations.find((r) => r.id === relId)
expect(rel?.type).toBe(VerbType.RelatedTo)
expect((rel?.metadata as Record<string, unknown>)?.verb).toBeUndefined()
@ -382,7 +382,7 @@ describe('reserved-field metadata remap (8.0 contract)', () => {
metadata: { confidence: 0.55, subtype: 'dotted-line', extra: 'applied' } as object
})
const relations = await brain.getRelations({ from: a })
const relations = await brain.related({ from: a })
const rel = relations.find((r) => r.id === relId)
expect(rel?.confidence).toBe(0.55)
expect(rel?.subtype).toBe('dotted-line')

View file

@ -10,7 +10,7 @@
* - `getNeighbors` speaks entity ints both ways via the shared idMapper
* - unmapped entity ints / UUIDs produce empty reads, never errors
* - a missing idMapper fails loudly (wiring bug, not a silent fallback)
* - coordinator-level behavior: relate getRelations/neighbors unrelate
* - coordinator-level behavior: relate related/neighbors unrelate
* works end-to-end over the BigInt boundary (public API unchanged)
*/
@ -156,7 +156,7 @@ describe('Coordinator — relate/related end-to-end over the BigInt boundary', (
await brain.close()
})
it('relate → getRelations → neighbors round-trips (public API unchanged)', async () => {
it('relate → related → neighbors round-trips (public API unchanged)', async () => {
const personId = await brain.add({
data: { name: 'Ada' },
type: NounType.Person
@ -172,8 +172,8 @@ describe('Coordinator — relate/related end-to-end over the BigInt boundary', (
type: VerbType.WorksWith
})
// getRelations resolves verb ints back to verb-id strings internally.
const relations = await brain.getRelations({ from: personId })
// related resolves verb ints back to verb-id strings internally.
const relations = await brain.related({ from: personId })
expect(relations).toHaveLength(1)
expect(relations[0].id).toBe(relId)
expect(relations[0].to).toBe(projectId)
@ -208,7 +208,7 @@ describe('Coordinator — relate/related end-to-end over the BigInt boundary', (
const relId = await brain.relate({ from: a, to: b, type: VerbType.RelatedTo })
await brain.unrelate(relId)
const relations = await brain.getRelations({ from: a })
const relations = await brain.related({ from: a })
expect(relations).toHaveLength(0)
})

View file

@ -1,339 +0,0 @@
/**
* 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

@ -611,8 +611,10 @@ describe('SmartExtractor', () => {
extractor.addToHistory('Test Entity', NounType.Person, vector)
// History is internal, just ensure no errors
expect(true).toBe(true)
// History lives on the embedding signal the extractor delegates to.
const history = (extractor as any).embeddingSignal.historicalEntities
expect(history).toHaveLength(1)
expect(history[0].text).toBe('Test Entity')
})
it('should clear history', () => {
@ -621,8 +623,8 @@ describe('SmartExtractor', () => {
extractor.clearHistory()
// History cleared, no errors
expect(true).toBe(true)
const history = (extractor as any).embeddingSignal.historicalEntities
expect(history).toHaveLength(0)
})
})
})

View file

@ -11,7 +11,7 @@
* 1. Option A: Never skip a verb type that's explicitly requested in the filter
* 2. Option B: Added fast path for sourceId + verbType combo (common VFS pattern)
*
* Bug Report: /home/dpsifr/Projects/workshop/docs/BRAINY_VFS_BUG_REPORT.md
* Originally reported by a downstream application using the VFS.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
@ -84,7 +84,7 @@ describe('VFS mkdir() Bug Fix (v6.2.9)', () => {
})
})
describe('getRelations should query Contains relationships correctly', () => {
describe('related should query Contains relationships correctly', () => {
it('should find Contains relationships via sourceId + verbType filter', async () => {
// Create some entities and relationships
const parent = await brain.add({
@ -110,7 +110,7 @@ describe('VFS mkdir() Bug Fix (v6.2.9)', () => {
await brain.relate({ from: parent, to: child2, type: VerbType.Contains })
// Query relationships (this is what getChildren() does)
const relations = await brain.getRelations({
const relations = await brain.related({
from: parent,
type: VerbType.Contains
})
@ -132,7 +132,7 @@ describe('VFS mkdir() Bug Fix (v6.2.9)', () => {
await brain.relate({ from: entityA, to: entityC, type: VerbType.RelatedTo })
// Query only Contains relationships
const containsRelations = await brain.getRelations({
const containsRelations = await brain.related({
from: entityA,
type: VerbType.Contains
})
@ -141,7 +141,7 @@ describe('VFS mkdir() Bug Fix (v6.2.9)', () => {
expect(containsRelations[0].to).toBe(entityB)
// Query only RelatedTo relationships
const relatedRelations = await brain.getRelations({
const relatedRelations = await brain.related({
from: entityA,
type: VerbType.RelatedTo
})
@ -174,7 +174,7 @@ describe('VFS mkdir() Bug Fix (v6.2.9)', () => {
// Query should use the new fast path
const startTime = performance.now()
const relations = await brain.getRelations({
const relations = await brain.related({
from: parent,
type: VerbType.Contains
})

View file

@ -120,7 +120,7 @@ describe('EntityIdMapper stability (foundation for 2.4.0)', () => {
const deletedInt = beforeInts[2]
const maxBefore = Math.max(...beforeInts)
await brain.delete(deletedId)
await brain.remove(deletedId)
expect(getInt(deletedId)).toBeUndefined()
const newId = await addEntity('f', 6)

View file

@ -255,14 +255,9 @@ describe('MetadataIndexManager - Phase 1b: Type-Aware Features', { timeout: 120_
})
it('should preload sparse indices for top fields of top types', async () => {
const managerAny = manager as any
// Warm cache
await manager.warmCacheForTopTypes(2)
// Check that sparse indices were loaded (by checking UnifiedCache)
// This is implementation-dependent, so we just verify no errors occurred
expect(true).toBe(true) // If we got here, warming succeeded
// Warming must complete cleanly against populated storage; the cache
// contents are an implementation detail, the contract is clean resolution.
await expect(manager.warmCacheForTopTypes(2)).resolves.toBeUndefined()
})
it('should handle empty database gracefully', async () => {

View file

@ -71,12 +71,14 @@ describe('Mutex Safety Tests', () => {
})
it('should not deadlock with nested different keys', async () => {
let innerRan = false
await mutex.runExclusive('key1', async () => {
await mutex.runExclusive('key2', async () => {
// Should not deadlock
expect(true).toBe(true)
innerRan = true
})
})
// Reaching here at all proves no deadlock; assert the inner body ran.
expect(innerRan).toBe(true)
})
it('should handle errors in exclusive function', async () => {
@ -89,9 +91,11 @@ describe('Mutex Safety Tests', () => {
).rejects.toThrow('Test error')
// Lock should be released, so we can acquire it again
let reacquired = false
await mutex.runExclusive('error-test', async () => {
expect(true).toBe(true)
reacquired = true
})
expect(reacquired).toBe(true)
})
it('should handle high concurrency without resource exhaustion', async () => {

View file

@ -40,8 +40,8 @@ describe('VFS restart persistence', () => {
const entries1 = await brain.vfs.readdir('/')
expect(entries1.length).toBeGreaterThan(0)
// In-session getRelations: root should have Contains relationship to the file
const relations1 = await brain.getRelations({ from: rootId, type: VerbType.Contains })
// In-session related: root should have Contains relationship to the file
const relations1 = await brain.related({ from: rootId, type: VerbType.Contains })
expect(relations1.length).toBeGreaterThan(0)
await brain.close()
@ -68,8 +68,8 @@ describe('VFS restart persistence', () => {
expect(entries2.length).toBeGreaterThan(0)
expect(entries2).toContain('chapter-1.txt')
// getRelations should find the Contains relationship
const relations2 = await brain.getRelations({ from: rootId, type: VerbType.Contains })
// related should find the Contains relationship
const relations2 = await brain.related({ from: rootId, type: VerbType.Contains })
expect(relations2.length).toBeGreaterThan(0)
await brain.close()
@ -104,8 +104,8 @@ describe('VFS restart persistence', () => {
const docEntries1 = await brain.vfs.readdir('/docs')
expect(docEntries1.length).toBe(2) // guide.txt + api.txt
// In-session getRelations: root should have Contains relationships
const rootRelations1 = await brain.getRelations({ from: rootId, type: VerbType.Contains })
// In-session related: root should have Contains relationships
const rootRelations1 = await brain.related({ from: rootId, type: VerbType.Contains })
expect(rootRelations1.length).toBeGreaterThan(0)
await brain.close()
@ -130,7 +130,7 @@ describe('VFS restart persistence', () => {
expect(docEntries2.sort()).toEqual(['api.txt', 'guide.txt'])
// Root relations
const rootRelations = await brain.getRelations({ from: rootId, type: VerbType.Contains })
const rootRelations = await brain.related({ from: rootId, type: VerbType.Contains })
expect(rootRelations.length).toBeGreaterThan(0)
await brain.close()