chore(8.0): final pre-RC1 sweep — API consistency, named errors, orphans, zero-cast codebase
This commit is contained in:
parent
970e08c466
commit
1f7e365a4e
237 changed files with 1951 additions and 49413 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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`)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
339
tests/integration/graphIndex-pagination.test.ts
Normal file
339
tests/integration/graphIndex-pagination.test.ts
Normal 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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -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')
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue