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

@ -3,7 +3,7 @@
*
* Comprehensive testing of batch operations including:
* - addMany: Bulk entity creation
* - deleteMany: Bulk deletion
* - removeMany: Bulk deletion
* - Performance validation at scale
* - Memory efficiency testing
* - Error recovery scenarios
@ -353,7 +353,7 @@ describe('Batch Operations - Public API Testing', () => {
})
})
describe('deleteMany - Bulk Deletion', () => {
describe('removeMany - Bulk Deletion', () => {
let testIds: string[] = []
beforeEach(async () => {
@ -374,14 +374,14 @@ describe('Batch Operations - Public API Testing', () => {
const startMemory = process.memoryUsage().heapUsed
const startTime = performance.now()
// Use deleteMany if available, otherwise individual deletes
// Use removeMany if available, otherwise individual deletes
let results: any[]
if ('deleteMany' in brainy && typeof brainy.deleteMany === 'function') {
const result = await brainy.deleteMany({ ids: toDelete })
if ('removeMany' in brainy && typeof brainy.removeMany === 'function') {
const result = await brainy.removeMany({ ids: toDelete })
results = result.successful
} else {
results = await Promise.all(
toDelete.map(id => brainy.delete(id))
toDelete.map(id => brainy.remove(id))
)
}
@ -389,7 +389,7 @@ describe('Batch Operations - Public API Testing', () => {
const memoryUsed = process.memoryUsage().heapUsed - startMemory
metricsCollector.recordBatch(
'deleteMany-100',
'removeMany-100',
toDelete.length,
results,
duration,
@ -426,13 +426,13 @@ describe('Batch Operations - Public API Testing', () => {
const batchSize = 100
for (let i = 0; i < idsToDelete.length; i += batchSize) {
const batch = idsToDelete.slice(i, i + batchSize)
await Promise.all(batch.map(id => brainy.delete(id)))
await Promise.all(batch.map(id => brainy.remove(id)))
}
const duration = performance.now() - startTime
metricsCollector.recordBatch(
'deleteMany-1k',
'removeMany-1k',
idsToDelete.length,
idsToDelete.map(() => true),
duration,
@ -553,7 +553,7 @@ describe('Batch Operations - Public API Testing', () => {
Promise.all(updateIds.map(id =>
brainy.update({ id, metadata: { updated: true } })
)),
Promise.all(deleteIds.map(id => brainy.delete(id)))
Promise.all(deleteIds.map(id => brainy.remove(id)))
])
const duration = performance.now() - startTime

View file

@ -309,7 +309,7 @@ describe('Enhanced CRUD Operations - Public API Validation', () => {
// Delete all entities
for (const id of ids) {
await brainy.delete(id)
await brainy.remove(id)
}
if (global.gc) global.gc()
@ -612,7 +612,7 @@ describe('Enhanced CRUD Operations - Public API Validation', () => {
})
const startTime = metricsCollector.startOperation('delete-single')
const result = await brainy.delete(id)
const result = await brainy.remove(id)
metricsCollector.endOperation('delete-single', 1, startTime)
expect(result).toBe(true)
@ -633,7 +633,7 @@ describe('Enhanced CRUD Operations - Public API Validation', () => {
const startTime = metricsCollector.startOperation('delete-bulk', ids.length)
const results = await Promise.all(
ids.map(id => brainy.delete(id))
ids.map(id => brainy.remove(id))
)
metricsCollector.endOperation('delete-bulk', ids.length, startTime)

View file

@ -174,7 +174,7 @@ describe('Error Handling and Recovery', () => {
it('should handle deletion of non-existent entities', async () => {
// Should not throw, just return void
await expect(
brainy.delete('non-existent-id')
brainy.remove('non-existent-id')
).resolves.toBeUndefined()
})
@ -184,9 +184,9 @@ describe('Error Handling and Recovery', () => {
type: 'document'
})
await brainy.delete(id)
await brainy.remove(id)
// Second delete should not throw
await expect(brainy.delete(id)).resolves.toBeUndefined()
await expect(brainy.remove(id)).resolves.toBeUndefined()
})
})
@ -364,7 +364,7 @@ describe('Error Handling and Recovery', () => {
})
// Attempt concurrent deletes
const deletes = Array(5).fill(null).map(() => brainy.delete(id))
const deletes = Array(5).fill(null).map(() => brainy.remove(id))
// Should not throw
await expect(Promise.all(deletes)).resolves.toBeDefined()
@ -428,7 +428,7 @@ describe('Error Handling and Recovery', () => {
)
break
case 3:
promises.push(brainy.delete(`rapid-${i}`).catch(() => null))
promises.push(brainy.remove(`rapid-${i}`).catch(() => null))
break
}
}
@ -578,7 +578,7 @@ describe('Error Handling and Recovery', () => {
type: 'document'
})
await brainy.delete(id)
await brainy.remove(id)
// Operations on deleted entity should handle gracefully
const getResult = await brainy.get(id)
@ -592,7 +592,7 @@ describe('Error Handling and Recovery', () => {
).rejects.toThrow(/not found/i)
// Should not throw for delete
await expect(brainy.delete(id)).resolves.toBeUndefined()
await expect(brainy.remove(id)).resolves.toBeUndefined()
})
it('should handle rapid create-delete cycles', async () => {
@ -605,7 +605,7 @@ describe('Error Handling and Recovery', () => {
})
// Immediately delete
await brainy.delete(id)
await brainy.remove(id)
// Verify it's gone
const result = await brainy.get(id)

View file

@ -231,7 +231,7 @@ describe('Performance Benchmarks - SLA Validation', () => {
for (const id of ids) {
const start = performance.now()
await brainy.delete(id)
await brainy.remove(id)
const latency = performance.now() - start
benchmark.recordOperation(latency)
}
@ -419,7 +419,7 @@ describe('Performance Benchmarks - SLA Validation', () => {
}
for (const id of ids) {
await brainy.delete(id)
await brainy.remove(id)
}
if (global.gc) global.gc()

View file

@ -122,7 +122,7 @@ async function runBenchmark() {
// TEST 7: Delete Operations
start = Date.now()
for (let i = 0; i < 100; i++) {
await brain.delete(ids[1900 + i])
await brain.remove(ids[1900 + i])
}
elapsed = Date.now() - start
results.delete = Math.round(100 / (elapsed / 1000))

View file

@ -200,7 +200,7 @@ async function runV3Benchmark() {
console.log('Testing delete operations...')
const start7 = Date.now()
for (let i = 0; i < 100; i++) {
await brain.delete(ids[900 + i])
await brain.remove(ids[900 + i])
}
const deleteTime = Date.now() - start7
results.delete = Math.round(100 / (deleteTime / 1000))

View file

@ -77,7 +77,7 @@ describe('Brainy 3.0 API', () => {
type: NounType.Document
})
await brain.delete(id)
await brain.remove(id)
const entity = await brain.get(id)
expect(entity).toBeNull()
@ -124,7 +124,7 @@ describe('Brainy 3.0 API', () => {
type: VerbType.References
})
const relations = await brain.getRelations({ from: doc1 })
const relations = await brain.related({ from: doc1 })
expect(relations).toHaveLength(1)
expect(relations[0].from).toBe(doc1)
@ -150,8 +150,8 @@ describe('Brainy 3.0 API', () => {
bidirectional: true
})
const fromPerson = await brain.getRelations({ from: person })
const toPerson = await brain.getRelations({ to: person })
const fromPerson = await brain.related({ from: person })
const toPerson = await brain.related({ to: person })
expect(fromPerson).toHaveLength(1)
expect(toPerson).toHaveLength(1)
@ -176,7 +176,7 @@ describe('Brainy 3.0 API', () => {
await brain.unrelate(relationId)
const relations = await brain.getRelations({ from: doc1 })
const relations = await brain.related({ from: doc1 })
expect(relations).toHaveLength(0)
})
})
@ -331,7 +331,7 @@ describe('Brainy 3.0 API', () => {
brain.add({ data: 'Keep me', type: NounType.Document, metadata: { delete: false } })
])
const result = await brain.deleteMany({
const result = await brain.removeMany({
where: { delete: true }
})
@ -365,7 +365,7 @@ describe('Brainy 3.0 API', () => {
type: VerbType.References // Must use enum
})
const relations = await brain.getRelations({ from: doc1 })
const relations = await brain.related({ from: doc1 })
expect(relations[0].type).toBe(VerbType.References)
})
})

View file

@ -1,177 +0,0 @@
# Brainy v3.0 Comprehensive Test Validation Report
## Executive Summary
As an experienced QA engineer, I have conducted a thorough and systematic test validation of Brainy v3.0. This report summarizes the testing coverage, results, and validation status of all major components.
## Test Coverage Overview
### ✅ Components Tested
1. **Core CRUD Operations** - VALIDATED ✓
- Add operations (with ID, metadata, vectors)
- Get operations
- Update operations (data and metadata)
- Delete operations
- Batch operations (addMany, deleteMany)
2. **Find & Triple Intelligence** - VALIDATED ✓
- Vector search
- Metadata filtering ($gte, $contains, $or operators)
- Type filtering (single and multiple types)
- Fusion strategies (adaptive, weighted)
- Combined search (vector + metadata + type)
3. **Augmentation System** - VALIDATED ✓
- Registration and discovery
- Cache augmentation (with invalidation)
- Index augmentation (metadata indexing)
- Metrics augmentation
- Display augmentation (AI-powered)
- Pipeline execution
4. **Storage Adapters** - PARTIALLY VALIDATED
- Memory storage ✓
- Filesystem storage ✓
- Persistence across restarts ✓
- Other adapters (S3, R2, OPFS) - configuration validated
5. **Neural API** - VALIDATED ✓
- Similarity calculations
- Clustering (hierarchical, k-means)
- Related entity discovery
6. **Performance** - VALIDATED ✓
- Handles 1000+ items efficiently
- Concurrent operations
- Sub-second search performance
- Cache effectiveness
## Test Results Summary
### Test Suites Created
- `tests/comprehensive/core-api.test.ts` - 36 tests
- `tests/comprehensive/find-triple-intelligence.test.ts` - 28 tests
- `tests/comprehensive/brainy-v3-complete.test.ts` - 80+ tests
### Key Findings
#### ✅ WORKING CORRECTLY:
1. **Core CRUD** - All basic operations work as expected
2. **Vector Search** - Semantic search with embeddings functional
3. **Metadata Filtering** - Complex queries supported
4. **Type System** - NounType/VerbType validation working
5. **Augmentations** - Pipeline execution confirmed
6. **Batch Operations** - Efficient bulk processing
7. **Import/Export** - Data portability functional
8. **Statistics/Insights** - Accurate metrics collection
#### ⚠️ AREAS NEEDING ATTENTION:
1. **API Naming** - Some inconsistencies (no `shutdown()` method)
2. **Storage Config** - Path should be in options object
3. **Neural API** - Methods need to be called as functions
4. **Type Exports** - Some types missing from exports
#### 🐛 BUGS FOUND:
1. `brain.neural.similarity()` should be `brain.neural().similarity()`
2. Storage filesystem config should use `options.path` not `path`
3. Missing `VerbType.WorksFor` (should use `VerbType.MemberOf`)
4. `brain.clear()` method doesn't exist (use delete operations)
## Performance Metrics
Based on testing with 1000+ items:
| Operation | Target | Actual | Status |
|-----------|--------|--------|--------|
| Add | <10ms | 1-2ms | PASS |
| Get | <5ms | <1ms | PASS |
| Search | <50ms | 5-15ms | PASS |
| Update | <15ms | 2-3ms | PASS |
| Batch (100) | <500ms | 50-100ms | PASS |
## Augmentation Validation
| Augmentation | Status | Functionality |
|--------------|--------|---------------|
| Cache | ✅ Working | Result caching with auto-invalidation |
| Index | ✅ Working | O(1) metadata lookups |
| Metrics | ✅ Working | Performance tracking |
| Display | ✅ Working | AI-powered display fields |
| WAL | ⚠️ Config Only | Needs filesystem storage |
| Monitoring | ✅ Working | Health checks functional |
## Edge Case Testing
✅ **Handled Correctly:**
- Empty queries
- Very long text (100k+ characters)
- Special characters
- Unicode text
- Concurrent operations
- Non-matching filters
- Invalid types (proper errors)
## Recommendations
### Critical Fixes Needed:
1. **Fix type exports** - Ensure all types are properly exported
2. **Standardize storage config** - Use consistent options structure
3. **Document API changes** - Clear migration guide for v2 → v3
### Performance Optimizations:
1. **Implement request coalescing** - Currently initialized but unused
2. **Optimize large dataset handling** - Add pagination for 10k+ items
3. **Enhance cache strategy** - Consider distributed caching
### Testing Improvements:
1. **Add integration tests** for distributed features
2. **Create performance benchmarks** for regression testing
3. **Add stress tests** for 100k+ items
4. **Test all storage adapters** with real credentials
## Certification
### ✅ PRODUCTION READY with caveats:
**Strengths:**
- Core functionality is solid and performant
- Augmentation system works as designed
- Type safety is well-implemented
- Error handling is appropriate
- Performance meets targets
**Required Before Production:**
1. Fix identified type issues
2. Complete distributed feature testing
3. Validate cloud storage adapters
4. Update documentation for API changes
## Test Repeatability
All tests are implemented using Vitest and can be run with:
```bash
# Run all comprehensive tests
npx vitest run tests/comprehensive/
# Run specific test suite
npx vitest run tests/comprehensive/core-api.test.ts
# Run with coverage
npx vitest run --coverage tests/comprehensive/
```
## Conclusion
Brainy v3.0 demonstrates **strong core functionality** with an innovative augmentation system. The codebase is **production-ready for single-instance deployments** with memory or filesystem storage. Distributed features and cloud storage adapters need additional validation before enterprise deployment.
**Overall Quality Score: 8.5/10**
The system is robust, well-architected, and performant. With the recommended fixes, it will be fully production-ready for all use cases.
---
*Test validation completed by: Senior QA Engineer*
*Date: September 9, 2025*
*Framework: Vitest 3.2.4*
*Coverage: Core APIs, Augmentations, Storage, Neural Features*

View file

@ -203,7 +203,7 @@ describe('Brainy v3.0 Core API Tests', () => {
type: NounType.Document
})
const success = await brain.delete(id)
const success = await brain.remove(id)
expect(success).toBe(true)
const item = await brain.get(id)
@ -211,7 +211,7 @@ describe('Brainy v3.0 Core API Tests', () => {
})
it('should return false for non-existent ID', async () => {
const success = await brain.delete('non-existent')
const success = await brain.remove('non-existent')
expect(success).toBe(false)
})
@ -222,7 +222,7 @@ describe('Brainy v3.0 Core API Tests', () => {
)
)
await Promise.all(ids.map(id => brain.delete(id)))
await Promise.all(ids.map(id => brain.remove(id)))
// delete returns void, not boolean
// Verify all deleted
@ -259,7 +259,7 @@ describe('Brainy v3.0 Core API Tests', () => {
type: VerbType.Creates
})
const relations = await brain.getRelations({ from: entityA })
const relations = await brain.related({ from: entityA })
expect(relations.length).toBeGreaterThanOrEqual(1)
expect(relations[0].from).toBe(entityA)
@ -275,7 +275,7 @@ describe('Brainy v3.0 Core API Tests', () => {
const success = await brain.unrelate(verbId)
expect(success).toBe(true)
const relations = await brain.getRelations({ from: entityA })
const relations = await brain.related({ from: entityA })
const found = relations.find(r => r.id === verbId)
expect(found).toBeUndefined()
})
@ -305,7 +305,7 @@ describe('Brainy v3.0 Core API Tests', () => {
)
// Then delete them in batch
const result = await brain.deleteMany({ ids })
const result = await brain.removeMany({ ids })
expect(result.successful).toHaveLength(5)
expect(result.failed).toHaveLength(0)

View file

@ -133,13 +133,11 @@ describe('Brainy Public API - Complete Coverage', () => {
expect(final?.metadata?.version).toBeGreaterThan(1)
})
it('should handle update of non-existent entity gracefully', async () => {
it('should reject update of a non-existent entity', async () => {
const fakeId = randomUUID()
try {
await brain.update({ id: fakeId, metadata: { test: true } })
} catch {
// Brainy may throw for non-existent entity — acceptable
}
await expect(
brain.update({ id: fakeId, metadata: { test: true } })
).rejects.toThrow(/not found/)
})
it('should preserve unmodified fields', async () => {
@ -191,30 +189,27 @@ describe('Brainy Public API - Complete Coverage', () => {
})
})
describe('brain.delete()', () => {
describe('brain.remove()', () => {
it('should handle cascade deletion of relationships', async () => {
const id1 = await brain.add({ data: 'Entity 1', type: NounType.Person })
const id2 = await brain.add({ data: 'Entity 2', type: NounType.Organization })
await brain.relate({ from: id1, to: id2, type: VerbType.WorksWith })
await brain.delete(id1)
await brain.remove(id1)
const relations = await brain.getRelations(id2)
const relations = await brain.related(id2)
expect(relations.filter(r => r.from === id1 || r.to === id1)).toHaveLength(0)
})
it('should handle deletion of non-existent entity gracefully', async () => {
it('should treat deletion of a non-existent entity as a no-op', async () => {
const fakeId = randomUUID()
try {
await brain.delete(fakeId)
} catch {
// Brainy may throw for non-existent entity — acceptable
}
// delete() is idempotent: unknown IDs resolve without throwing
await expect(brain.remove(fakeId)).resolves.toBeUndefined()
})
})
describe('brain.deleteMany()', () => {
describe('brain.removeMany()', () => {
it('should efficiently delete batches', async () => {
const result = await brain.addMany({
items: Array.from({ length: 5 }, (_, i) => ({
@ -224,7 +219,7 @@ describe('Brainy Public API - Complete Coverage', () => {
})
const ids = result.successful
await brain.deleteMany({ ids })
await brain.removeMany({ ids })
for (const id of ids) {
const entity = await brain.get(id)
@ -280,8 +275,8 @@ describe('Brainy Public API - Complete Coverage', () => {
bidirectional: true
})
const relations1 = await brain.getRelations(person1)
const relations2 = await brain.getRelations(person2)
const relations1 = await brain.related(person1)
const relations2 = await brain.related(person2)
expect(relations1.some(r => r.to === person2)).toBe(true)
expect(relations2.some(r => r.to === person1)).toBe(true)
@ -294,7 +289,7 @@ describe('Brainy Public API - Complete Coverage', () => {
await brain.relate({ from: id1, to: id2, type: VerbType.References })
await brain.relate({ from: id1, to: id2, type: VerbType.References })
const relations = await brain.getRelations(id1)
const relations = await brain.related(id1)
const referenceRelations = relations.filter(
r => r.type === VerbType.References && r.to === id2
)
@ -317,7 +312,7 @@ describe('Brainy Public API - Complete Coverage', () => {
}
})
const relations = await brain.getRelations(id1)
const relations = await brain.related(id1)
const relation = relations.find(r => r.id === relationId)
expect(relation).toBeDefined()
@ -350,7 +345,7 @@ describe('Brainy Public API - Complete Coverage', () => {
})
})
describe('brain.getRelations()', () => {
describe('brain.related()', () => {
it('should retrieve outgoing relationships', async () => {
const center = await brain.add({ data: 'Center', type: NounType.Person })
const related1 = await brain.add({ data: 'Related1', type: NounType.Organization })
@ -359,7 +354,7 @@ describe('Brainy Public API - Complete Coverage', () => {
await brain.relate({ from: center, to: related1, type: VerbType.WorksWith })
await brain.relate({ from: center, to: related2, type: VerbType.Creates })
const outgoing = await brain.getRelations({ from: center })
const outgoing = await brain.related({ from: center })
expect(outgoing).toHaveLength(2)
expect(outgoing.some(r => r.type === VerbType.WorksWith)).toBe(true)
@ -372,7 +367,7 @@ describe('Brainy Public API - Complete Coverage', () => {
await brain.relate({ from: source, to: center, type: VerbType.DependsOn })
const incoming = await brain.getRelations({ to: center })
const incoming = await brain.related({ to: center })
expect(incoming).toHaveLength(1)
expect(incoming[0].type).toBe(VerbType.DependsOn)
@ -386,8 +381,8 @@ describe('Brainy Public API - Complete Coverage', () => {
await brain.relate({ from: source, to: center, type: VerbType.Creates })
await brain.relate({ from: center, to: target, type: VerbType.Requires })
const outgoing = await brain.getRelations({ from: center })
const incoming = await brain.getRelations({ to: center })
const outgoing = await brain.related({ from: center })
const incoming = await brain.related({ to: center })
expect(outgoing).toHaveLength(1)
expect(outgoing[0].to).toBe(target)
@ -406,7 +401,7 @@ describe('Brainy Public API - Complete Coverage', () => {
await brain.relate({ from: doc, to: ref2, type: VerbType.References })
await brain.relate({ from: author, to: doc, type: VerbType.Creates })
const references = await brain.getRelations({
const references = await brain.related({
from: doc,
type: VerbType.References
})
@ -683,7 +678,7 @@ describe('Brainy Public API - Complete Coverage', () => {
const updated = await fsBrain.get(id)
expect(updated?.metadata?.updated).toBe(true)
await fsBrain.delete(id)
await fsBrain.remove(id)
const deleted = await fsBrain.get(id)
expect(deleted).toBeNull()

View file

@ -30,7 +30,6 @@ export default defineConfig({
'tests/integration/**',
'tests/**/*.integration.test.ts',
'tests/**/*.e2e.test.ts',
'tests/unit/graph/graphIndex-pagination.test.ts', // v6.0.0: TODO fix infinite loop
'node_modules/**'
],

View file

@ -146,7 +146,7 @@ describe('CRITICAL: Error Handling and Edge Cases', () => {
)
)
const deletes = ids.map(id => brainy.delete(id))
const deletes = ids.map(id => brainy.remove(id))
await Promise.all(deletes)
for (const id of ids) {
@ -280,7 +280,7 @@ describe('CRITICAL: Error Handling and Edge Cases', () => {
expect(relId).toBeDefined()
const relations = await brainy.getRelations({ from: id })
const relations = await brainy.related({ from: id })
expect(relations.length).toBeGreaterThan(0)
})
@ -371,7 +371,7 @@ describe('CRITICAL: Error Handling and Edge Cases', () => {
type: 'entity'
})
await brainy.delete(`cleanup-${i}`)
await brainy.remove(`cleanup-${i}`)
} catch {
// Ignore errors
}

View file

@ -191,12 +191,12 @@ describe('CRITICAL: Real-World Neural Matching Validation', () => {
metadata: { role: 'lead' }
})
const johnsRelations = await brainy.getRelations({
const johnsRelations = await brainy.related({
from: johnId
})
expect(johnsRelations.length).toBe(2)
const acmeRelations = await brainy.getRelations({
const acmeRelations = await brainy.related({
to: acmeId
})
expect(acmeRelations.length).toBe(1)
@ -236,12 +236,12 @@ describe('CRITICAL: Real-World Neural Matching Validation', () => {
metadata: { startDate: '2023-01-01' }
})
const aliceRelations = await brainy.getRelations({
const aliceRelations = await brainy.related({
from: aliceId
})
expect(aliceRelations.length).toBeGreaterThanOrEqual(3)
const proj2Relations = await brainy.getRelations({
const proj2Relations = await brainy.related({
to: proj2Id
})
expect(proj2Relations.length).toBe(2)
@ -334,7 +334,7 @@ describe('CRITICAL: Real-World Neural Matching Validation', () => {
const notFound = await brainy.get('00000000-0000-0000-0000-000000000099')
expect(notFound).toBeNull()
const relations = await brainy.getRelations({
const relations = await brainy.related({
from: '00000000-0000-0000-0000-000000000099'
})
expect(relations).toEqual([])

View file

@ -319,7 +319,7 @@ describe('CRITICAL: Performance Benchmarks at Scale', () => {
for (let i = 0; i < 100; i++) {
const randomNode = Math.floor(Math.random() * nodes)
queryPromises.push(brainy.getRelations({ from: `node-${randomNode}` }))
queryPromises.push(brainy.related({ from: `node-${randomNode}` }))
}
await Promise.all(queryPromises)

View file

@ -1,358 +0,0 @@
/**
* Test Mocks - Mock implementations and spies for Brainy tests
* Provides mock objects and spy utilities for testing
*/
import { vi } from 'vitest'
import type { StorageAdapter } from '../../src/coreTypes'
import type { Entity, Relation } from '../../src/types/brainy.types'
/**
* Create a mock storage adapter
*/
export function createMockStorageAdapter(): StorageAdapter {
const store = new Map<string, any>()
const relationStore = new Map<string, any[]>()
return {
init: vi.fn(async () => {}),
close: vi.fn(async () => {}),
// Entity operations
storeVector: vi.fn(async (id: string, vector: number[], metadata?: any) => {
store.set(id, { id, vector, metadata })
}),
getVector: vi.fn(async (id: string) => {
const item = store.get(id)
return item ? item.vector : null
}),
getMetadata: vi.fn(async (id: string) => {
const item = store.get(id)
return item ? item.metadata : null
}),
deleteVector: vi.fn(async (id: string) => {
return store.delete(id)
}),
updateVector: vi.fn(async (id: string, vector: number[], metadata?: any) => {
if (!store.has(id)) return false
store.set(id, { id, vector, metadata })
return true
}),
hasVector: vi.fn(async (id: string) => {
return store.has(id)
}),
// Relation operations
storeRelation: vi.fn(async (from: string, to: string, type: string, metadata?: any) => {
const relation = { from, to, type, metadata }
const key = `${from}-${to}-${type}`
if (!relationStore.has(from)) {
relationStore.set(from, [])
}
relationStore.get(from)!.push(relation)
return key
}),
getRelations: vi.fn(async (id: string) => {
return relationStore.get(id) || []
}),
deleteRelation: vi.fn(async (from: string, to: string, type: string) => {
const relations = relationStore.get(from)
if (!relations) return false
const index = relations.findIndex(
r => r.to === to && r.type === type
)
if (index === -1) return false
relations.splice(index, 1)
return true
}),
// Batch operations
batchStore: vi.fn(async (items: Array<{ id: string; vector: number[]; metadata?: any }>) => {
const results: boolean[] = []
for (const item of items) {
store.set(item.id, item)
results.push(true)
}
return results
}),
batchGet: vi.fn(async (ids: string[]) => {
return ids.map(id => {
const item = store.get(id)
return item ? item.vector : null
})
}),
batchDelete: vi.fn(async (ids: string[]) => {
return ids.map(id => store.delete(id))
}),
// Query operations
getAllIds: vi.fn(async () => {
return Array.from(store.keys())
}),
getCount: vi.fn(async () => {
return store.size
}),
clear: vi.fn(async () => {
store.clear()
relationStore.clear()
}),
// Utility for tests
_getStore: () => store,
_getRelationStore: () => relationStore,
} as any
}
/**
* Create a mock embedding function
*/
export function createMockEmbeddingFunction(dimension = 1536) {
return vi.fn(async (text: string) => {
// Generate deterministic embedding based on text
const hash = text.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)
const vector = new Array(dimension)
for (let i = 0; i < dimension; i++) {
vector[i] = Math.sin((hash + i) * 0.1) * 0.5
}
// Normalize
const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0))
return vector.map(val => val / magnitude)
})
}
/**
* Create a mock distance function
*/
export function createMockDistanceFunction() {
return vi.fn((a: number[], b: number[]) => {
// Simple cosine distance
let dotProduct = 0
let magnitudeA = 0
let magnitudeB = 0
for (let i = 0; i < a.length; i++) {
dotProduct += a[i] * b[i]
magnitudeA += a[i] * a[i]
magnitudeB += b[i] * b[i]
}
const similarity = dotProduct / (Math.sqrt(magnitudeA) * Math.sqrt(magnitudeB))
return 1 - similarity // Convert similarity to distance
})
}
/**
* Create a mock augmentation
*/
export function createMockAugmentation(name = 'test-augmentation') {
return {
name,
priority: 100,
enabled: true,
beforeAdd: vi.fn(async (data: any) => data),
afterAdd: vi.fn(async (entity: Entity) => entity),
beforeUpdate: vi.fn(async (data: any) => data),
afterUpdate: vi.fn(async (entity: Entity) => entity),
beforeDelete: vi.fn(async (id: string) => id),
afterDelete: vi.fn(async (id: string) => id),
beforeRelate: vi.fn(async (relation: any) => relation),
afterRelate: vi.fn(async (relation: Relation) => relation),
beforeSearch: vi.fn(async (query: any) => query),
afterSearch: vi.fn(async (results: any[]) => results),
}
}
/**
* Create a mock cache
*/
export function createMockCache<T = any>() {
const cache = new Map<string, T>()
return {
get: vi.fn((key: string) => cache.get(key)),
set: vi.fn((key: string, value: T) => {
cache.set(key, value)
return true
}),
has: vi.fn((key: string) => cache.has(key)),
delete: vi.fn((key: string) => cache.delete(key)),
clear: vi.fn(() => cache.clear()),
size: vi.fn(() => cache.size),
// Utility for tests
_getCache: () => cache,
}
}
/**
* Create a mock event emitter
*/
export function createMockEventEmitter() {
const listeners = new Map<string, Function[]>()
return {
on: vi.fn((event: string, handler: Function) => {
if (!listeners.has(event)) {
listeners.set(event, [])
}
listeners.get(event)!.push(handler)
}),
off: vi.fn((event: string, handler: Function) => {
const handlers = listeners.get(event)
if (handlers) {
const index = handlers.indexOf(handler)
if (index > -1) {
handlers.splice(index, 1)
}
}
}),
emit: vi.fn((event: string, ...args: any[]) => {
const handlers = listeners.get(event)
if (handlers) {
handlers.forEach(handler => handler(...args))
}
}),
once: vi.fn((event: string, handler: Function) => {
const wrappedHandler = (...args: any[]) => {
handler(...args)
listeners.get(event)?.splice(listeners.get(event)!.indexOf(wrappedHandler), 1)
}
if (!listeners.has(event)) {
listeners.set(event, [])
}
listeners.get(event)!.push(wrappedHandler)
}),
removeAllListeners: vi.fn((event?: string) => {
if (event) {
listeners.delete(event)
} else {
listeners.clear()
}
}),
// Utility for tests
_getListeners: () => listeners,
}
}
/**
* Create a mock logger
*/
export function createMockLogger() {
return {
debug: vi.fn((...args: any[]) => console.debug(...args)),
info: vi.fn((...args: any[]) => console.info(...args)),
warn: vi.fn((...args: any[]) => console.warn(...args)),
error: vi.fn((...args: any[]) => console.error(...args)),
log: vi.fn((...args: any[]) => console.log(...args)),
}
}
/**
* Create a mock timer for controlling time in tests
*/
export function createMockTimer() {
let currentTime = Date.now()
return {
now: vi.fn(() => currentTime),
advance: vi.fn((ms: number) => {
currentTime += ms
}),
reset: vi.fn(() => {
currentTime = Date.now()
}),
setTime: vi.fn((time: number) => {
currentTime = time
}),
}
}
/**
* Create a mock fetch function
*/
export function createMockFetch() {
return vi.fn(async (url: string, options?: any) => {
return {
ok: true,
status: 200,
statusText: 'OK',
json: async () => ({ success: true, url, options }),
text: async () => 'Mock response',
blob: async () => new Blob(['Mock blob']),
headers: new Headers({
'content-type': 'application/json',
}),
}
})
}
/**
* Wait for all pending promises to resolve
*/
export async function flushPromises() {
await new Promise(resolve => setImmediate(resolve))
}
/**
* Wait for a condition to be true
*/
export async function waitFor(
condition: () => boolean | Promise<boolean>,
timeout = 5000,
interval = 100
): Promise<void> {
const start = Date.now()
while (Date.now() - start < timeout) {
if (await condition()) {
return
}
await new Promise(resolve => setTimeout(resolve, interval))
}
throw new Error(`Timeout waiting for condition after ${timeout}ms`)
}
// Export as namespace for convenience
export const TestMocks = {
createMockStorageAdapter,
createMockEmbeddingFunction,
createMockDistanceFunction,
createMockAugmentation,
createMockCache,
createMockEventEmitter,
createMockLogger,
createMockTimer,
createMockFetch,
flushPromises,
waitFor,
}
export default TestMocks

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

@ -12,8 +12,8 @@
*/
import { describe, it, expect, beforeEach } from 'vitest'
import { Brainy } from '../../../src/brainy.js'
import { NounType, VerbType } from '../../../src/types/graphTypes.js'
import { Brainy } from '../../src/brainy.js'
import { NounType, VerbType } from '../../src/types/graphTypes.js'
describe('GraphAdjacencyIndex Pagination', () => {
let brain: Brainy

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
})

View file

@ -1,86 +0,0 @@
/**
* Debug: Check metadata index for isVFS field
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { NounType } from '../../src/types/graphTypes.js'
import * as fs from 'fs'
import * as path from 'path'
describe('Metadata Index Debug', () => {
const testDir = path.join(process.cwd(), 'test-metadata-index-debug')
let brain: Brainy
beforeAll(async () => {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
fs.mkdirSync(testDir, { recursive: true })
brain = new Brainy({ requireSubtype: false,
storage: {
type: 'filesystem',
options: { path: testDir }
}
})
await brain.init()
})
afterAll(() => {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
})
it('should index custom metadata fields', async () => {
// Add entity with custom metadata field
const entity1 = await brain.add({
data: 'Test entity with custom field',
type: NounType.Document,
metadata: {
customFlag: true,
customString: 'hello',
customNumber: 42
}
})
console.log('\n📋 Added entity:', entity1.id)
console.log(' Metadata:', entity1.metadata)
// Try to find by custom fields
console.log('\n📋 Query 1: where: { customFlag: true }')
const result1 = await brain.find({
where: { customFlag: true },
limit: 10
})
console.log(` Results: ${result1.length}`)
if (result1.length > 0) {
console.log(` Found: ${result1[0].id}`)
console.log(` Metadata.customFlag: ${result1[0].metadata?.customFlag}`)
}
console.log('\n📋 Query 2: where: { customString: "hello" }')
const result2 = await brain.find({
where: { customString: 'hello' },
limit: 10
})
console.log(` Results: ${result2.length}`)
console.log('\n📋 Query 3: where: { customNumber: 42 }')
const result3 = await brain.find({
where: { customNumber: 42 },
limit: 10
})
console.log(` Results: ${result3.length}`)
// Check what fields are indexed
console.log('\n📋 Indexed fields:')
const fields = await brain.getFilterFields()
console.log(` ${fields.join(', ')}`)
expect(result1.length).toBeGreaterThan(0)
expect(result2.length).toBeGreaterThan(0)
expect(result3.length).toBeGreaterThan(0)
})
})

View file

@ -1,100 +0,0 @@
/**
* Simple VFS filtering test
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { NounType } from '../../src/types/graphTypes.js'
import * as fs from 'fs'
import * as path from 'path'
describe('Simple VFS Filter Test', () => {
const testDir = path.join(process.cwd(), 'test-simple-vfs-filter')
let brain: Brainy
beforeAll(async () => {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
fs.mkdirSync(testDir, { recursive: true })
brain = new Brainy({ requireSubtype: false,
storage: {
type: 'filesystem',
options: { path: testDir }
}
})
await brain.init()
})
afterAll(() => {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
})
it('should demonstrate VFS filtering', async () => {
console.log('\n=== TEST START ===\n')
// 1. Create knowledge entity
console.log('1. Creating knowledge entity...')
const knowledgeId = await brain.add({
data: 'Knowledge document',
type: NounType.Document,
metadata: { title: 'Knowledge' }
})
console.log(` Created: ${knowledgeId}`)
// 2. Create VFS file
console.log('\n2. Creating VFS file...')
const vfs = brain.vfs
await vfs.init()
await vfs.writeFile('/test.txt', 'VFS file')
console.log(' VFS file created')
// 3. Query all (should exclude VFS)
console.log('\n3. Query: brain.find({}) [excludes VFS by default]')
const all = await brain.find({ limit: 100 })
console.log(` Results: ${all.length}`)
for (const r of all) {
console.log(` - ${r.id}, type: ${r.type}, isVFS: ${r.metadata?.isVFS}, title: ${r.metadata?.title}`)
}
// 4. Query all with VFS
console.log('\n4. Query: brain.find({ includeVFS: true })')
const allWithVFS = await brain.find({ includeVFS: true, limit: 100 })
console.log(` Results: ${allWithVFS.length}`)
for (const r of allWithVFS) {
console.log(` - ${r.id}, type: ${r.type}, isVFS: ${r.metadata?.isVFS}, path: ${r.metadata?.path || 'n/a'}`)
}
// 5. Query by type (should exclude VFS)
console.log('\n5. Query: brain.find({ type: NounType.Document }) [excludes VFS]')
const docs = await brain.find({ type: NounType.Document, limit: 100 })
console.log(` Results: ${docs.length}`)
for (const r of docs) {
console.log(` - ${r.id}, type: ${r.type}, isVFS: ${r.metadata?.isVFS}`)
}
// 6. Query by type with VFS
console.log('\n6. Query: brain.find({ type: NounType.Document, includeVFS: true })' )
const docsWithVFS = await brain.find({ type: NounType.Document, includeVFS: true, limit: 100 })
console.log(` Results: ${docsWithVFS.length}`)
for (const r of docsWithVFS) {
console.log(` - ${r.id}, type: ${r.type}, isVFS: ${r.metadata?.isVFS}, path: ${r.metadata?.path || 'n/a'}`)
}
console.log('\n=== TEST END ===\n')
// Assertions
expect(all.length).toBe(1) // Only knowledge entity
expect(all.some(r => r.id === knowledgeId)).toBe(true)
expect(allWithVFS.length).toBeGreaterThanOrEqual(2) // Knowledge + VFS entities
expect(docs.length).toBe(1) // Only knowledge entity
expect(docs.some(r => r.id === knowledgeId)).toBe(true)
expect(docsWithVFS.length).toBeGreaterThanOrEqual(2) // Knowledge + VFS file (both documents)
})
})

View file

@ -1,171 +0,0 @@
/**
* Manual test to reproduce a consumer's type filtering issue
*
* This test verifies that brain.find({ type: NounType.Person }) actually works
* as documented in the API.
*/
import { Brainy, NounType } from '../../src/index.js'
async function testTypeFiltering() {
console.log('\n🔬 Testing Type Filtering Issue from A Consumer Team\n')
console.log('=' .repeat(60))
// Create in-memory instance for testing
const brain = new Brainy({ requireSubtype: false,
storage: { type: 'memory' }
})
await brain.init()
console.log('\n1⃣ Adding test entities with different types...\n')
// Add 5 person entities
const people = []
for (let i = 0; i < 5; i++) {
const id = await brain.add({
data: `Person ${i + 1}`,
type: NounType.Person,
metadata: { name: `Person ${i + 1}` }
})
people.push(id)
}
console.log(`✅ Added ${people.length} person entities`)
// Add 3 location entities
const locations = []
for (let i = 0; i < 3; i++) {
const id = await brain.add({
data: `Location ${i + 1}`,
type: NounType.Location,
metadata: { name: `Location ${i + 1}` }
})
locations.push(id)
}
console.log(`✅ Added ${locations.length} location entities`)
// Add 2 concept entities
const concepts = []
for (let i = 0; i < 2; i++) {
const id = await brain.add({
data: `Concept ${i + 1}`,
type: NounType.Concept,
metadata: { name: `Concept ${i + 1}` }
})
concepts.push(id)
}
console.log(`✅ Added ${concepts.length} concept entities`)
console.log(`\n📊 Total: ${people.length + locations.length + concepts.length} entities`)
console.log('\n' + '='.repeat(60))
console.log('\n2⃣ Testing find() without type filter...\n')
const allResults = await brain.find({ limit: 100 })
console.log(`✅ brain.find({}) returned ${allResults.length} entities`)
// Check types in results
const typeCounts = allResults.reduce((acc, r) => {
acc[r.type || 'unknown'] = (acc[r.type || 'unknown'] || 0) + 1
return acc
}, {} as Record<string, number>)
console.log('\nTypes in results:')
for (const [type, count] of Object.entries(typeCounts)) {
console.log(` - ${type}: ${count}`)
}
console.log('\n' + '='.repeat(60))
console.log('\n3⃣ Testing find() WITH type filter (using enum)...\n')
// Test 1: Filter by NounType.Person (enum value)
console.log('Test 1: brain.find({ type: NounType.Person })')
const personResults = await brain.find({ type: NounType.Person, limit: 100 })
console.log(` Result: ${personResults.length} entities`)
console.log(` Expected: ${people.length} entities`)
if (personResults.length === people.length) {
console.log(' ✅ PASS')
} else {
console.log(' ❌ FAIL - Type filtering not working!')
}
// Test 2: Filter by NounType.Location
console.log('\nTest 2: brain.find({ type: NounType.Location })')
const locationResults = await brain.find({ type: NounType.Location, limit: 100 })
console.log(` Result: ${locationResults.length} entities`)
console.log(` Expected: ${locations.length} entities`)
if (locationResults.length === locations.length) {
console.log(' ✅ PASS')
} else {
console.log(' ❌ FAIL - Type filtering not working!')
}
// Test 3: Filter by NounType.Concept
console.log('\nTest 3: brain.find({ type: NounType.Concept })')
const conceptResults = await brain.find({ type: NounType.Concept, limit: 100 })
console.log(` Result: ${conceptResults.length} entities`)
console.log(` Expected: ${concepts.length} entities`)
if (conceptResults.length === concepts.length) {
console.log(' ✅ PASS')
} else {
console.log(' ❌ FAIL - Type filtering not working!')
}
console.log('\n' + '='.repeat(60))
console.log('\n4⃣ Testing find() WITH type filter (using string)...\n')
// Test 4: Filter by string 'person'
console.log('Test 4: brain.find({ type: "person" })')
const personResults2 = await brain.find({ type: 'person' as any, limit: 100 })
console.log(` Result: ${personResults2.length} entities`)
console.log(` Expected: ${people.length} entities`)
if (personResults2.length === people.length) {
console.log(' ✅ PASS')
} else {
console.log(' ❌ FAIL - String type filtering not working!')
}
console.log('\n' + '='.repeat(60))
console.log('\n5⃣ Checking entity storage (metadata.noun)...\n')
// Get a person entity and check its storage
const personEntity = await brain.get(people[0])
console.log('Person entity structure:')
console.log(' - id:', personEntity?.id)
console.log(' - type:', personEntity?.type)
console.log(' - metadata.noun:', (personEntity as any)?.metadata?.noun)
console.log(' - metadata.name:', personEntity?.metadata?.name)
console.log('\n' + '='.repeat(60))
console.log('\n📋 Summary\n')
const tests = [
{ name: 'Filter by NounType.Person', passed: personResults.length === people.length },
{ name: 'Filter by NounType.Location', passed: locationResults.length === locations.length },
{ name: 'Filter by NounType.Concept', passed: conceptResults.length === concepts.length },
{ name: 'Filter by string "person"', passed: personResults2.length === people.length }
]
const passedCount = tests.filter(t => t.passed).length
const totalCount = tests.length
console.log(`Tests passed: ${passedCount}/${totalCount}\n`)
for (const test of tests) {
console.log(`${test.passed ? '✅' : '❌'} ${test.name}`)
}
if (passedCount === totalCount) {
console.log('\n🎉 All tests passed! Type filtering works correctly.')
console.log('\n💡 The a consumer team might be experiencing a different issue.')
console.log(' Check: storage persistence, Brainy instance reuse, or data migration')
} else {
console.log('\n❌ Type filtering is BROKEN in Brainy!')
console.log('\n🐛 This is a bug that needs to be fixed.')
console.log(' a consumer team was right - it\'s not user error.')
}
console.log('\n' + '='.repeat(60) + '\n')
}
// Run the test
testTypeFiltering().catch(console.error)

View file

@ -1,90 +0,0 @@
/**
* Diagnostic: Check if isVFS flag is actually being stored
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { NounType } from '../../src/types/graphTypes.js'
import * as fs from 'fs'
import * as path from 'path'
describe('isVFS Flag Diagnostic', () => {
const testDir = path.join(process.cwd(), 'test-isvfs-diagnostic')
let brain: Brainy
beforeAll(async () => {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
fs.mkdirSync(testDir, { recursive: true })
brain = new Brainy({ requireSubtype: false,
storage: {
type: 'filesystem',
options: { path: testDir }
}
})
await brain.init()
})
afterAll(() => {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
})
it('should show what entities are created', async () => {
// Create VFS file
const vfs = brain.vfs
await vfs.init()
await vfs.writeFile('/test.txt', 'Hello World')
// Get ALL entities
console.log('\n📋 All entities in database:')
const allEntities = await brain.find({ limit: 100, includeVFS: true })
console.log(` Total: ${allEntities.length}`)
for (const entity of allEntities) {
console.log(`\n Entity: ${entity.id}`)
console.log(` Type: ${entity.type}`)
console.log(` Metadata keys: ${Object.keys(entity.metadata || {}).join(', ')}`)
if (entity.metadata?.path) {
console.log(` Path: ${entity.metadata.path}`)
}
if (entity.metadata?.vfsType) {
console.log(` VfsType: ${entity.metadata.vfsType}`)
}
if (entity.metadata?.isVFS !== undefined) {
console.log(` isVFS: ${entity.metadata.isVFS}`)
}
if (entity.metadata?.name) {
console.log(` Name: ${entity.metadata.name}`)
}
}
// Try different queries
console.log('\n\n📋 Query 1: brain.find({ limit: 100 }) [default, no includeVFS]')
const query1 = await brain.find({ limit: 100 })
console.log(` Results: ${query1.length}`)
console.log('\n📋 Query 2: brain.find({ limit: 100, includeVFS: true })')
const query2 = await brain.find({ limit: 100, includeVFS: true })
console.log(` Results: ${query2.length}`)
console.log('\n📋 Query 3: brain.find({ where: { isVFS: true } })')
const query3 = await brain.find({ where: { isVFS: true }, limit: 100 })
console.log(` Results: ${query3.length}`)
console.log('\n📋 Query 4: brain.find({ where: { path: "/test.txt" } })')
const query4 = await brain.find({ where: { path: '/test.txt' }, limit: 100 })
console.log(` Results: ${query4.length}`)
console.log('\n📋 Query 5: brain.find({ type: NounType.Document })')
const query5 = await brain.find({ type: NounType.Document, limit: 100 })
console.log(` Results: ${query5.length}`)
console.log('\n📋 Query 6: brain.find({ type: NounType.Document, includeVFS: true })')
const query6 = await brain.find({ type: NounType.Document, includeVFS: true, limit: 100 })
console.log(` Results: ${query6.length}`)
})
})

View file

@ -1,207 +0,0 @@
/**
* VFS Multiple Init() Diagnostic Test
*
* Tests a consumer's issue: Does calling vfs.init() multiple times
* create duplicate root entities?
*
* Scenario:
* - Create brain instance
* - Call vfs.init() multiple times (simulating multiple requests)
* - Check if multiple root entities are created
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import * as fs from 'fs'
import * as path from 'path'
describe('VFS Multiple Init Diagnostic', () => {
const testDir = path.join(process.cwd(), 'test-vfs-multi-init')
let brain: Brainy
beforeAll(async () => {
// Clean up test directory
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
fs.mkdirSync(testDir, { recursive: true })
// Create brain with filesystem storage
brain = new Brainy({ requireSubtype: false,
storage: {
type: 'filesystem',
path: testDir
}
})
await brain.init()
})
afterAll(() => {
// Clean up
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
})
it('should not create duplicate roots when calling vfs.init() multiple times on SAME instance', async () => {
const vfs = brain.vfs
// Call init multiple times
await vfs.init()
await vfs.init()
await vfs.init()
// Query for root entities using workaround (where clause is broken)
const collections = await brain.find({
type: 'collection',
limit: 100
})
const roots = collections.filter(e =>
e.metadata?.path === '/' &&
e.metadata?.vfsType === 'directory'
)
console.log(`\n✅ Test 1: Same VFS instance`)
console.log(` Total collections: ${collections.length}`)
console.log(` Roots found: ${roots.length}`)
roots.forEach((root, i) => {
console.log(` Root ${i + 1}: ${root.id}`)
})
expect(roots.length).toBe(1)
})
it('should not create duplicate roots when creating MULTIPLE VFS instances (the reported scenario)', async () => {
// Simulate the reported scenario: Getting VFS on multiple requests
// brain.vfs returns cached instance, so this should be safe
const vfs1 = brain.vfs
const vfs2 = brain.vfs
const vfs3 = brain.vfs
await vfs1.init()
await vfs2.init()
await vfs3.init()
// Query for root entities using workaround
const collections = await brain.find({
type: 'collection',
limit: 100
})
const roots = collections.filter(e =>
e.metadata?.path === '/' &&
e.metadata?.vfsType === 'directory'
)
console.log(`\n✅ Test 2: Multiple VFS references (cached)`)
console.log(` VFS instances are same? ${vfs1 === vfs2 && vfs2 === vfs3}`)
console.log(` Roots found: ${roots.length}`)
roots.forEach((root, i) => {
console.log(` Root ${i + 1}: ${root.id}`)
})
expect(vfs1).toBe(vfs2) // Should be same instance (cached)
expect(roots.length).toBe(1) // Should still be 1 root
})
it('should handle NEW brain instances gracefully (per-user scenario)', async () => {
// Simulate creating separate brain instances per user
// This is closer to a consumer's getUserBrainy() pattern
const brain2 = new Brainy({ requireSubtype: false,
storage: {
type: 'filesystem',
path: testDir // Same storage!
}
})
await brain2.init()
const vfs2 = brain2.vfs()
await vfs2.init()
// Query for roots using workaround
const collections = await brain.find({
type: 'collection',
limit: 100
})
const roots = collections.filter(e =>
e.metadata?.path === '/' &&
e.metadata?.vfsType === 'directory'
)
console.log(`\n✅ Test 3: New Brainy instance (same storage)`)
console.log(` Roots found: ${roots.length}`)
roots.forEach((root, i) => {
console.log(` Root ${i + 1}: ${root.id} (created: ${root.metadata?.createdAt})`)
})
// This is the CRITICAL test - should find existing root, not create new one
expect(roots.length).toBe(1)
})
it('should verify readdir works after multiple inits', async () => {
// Create a test directory
const vfs = brain.vfs
await vfs.mkdir('/test-dir', { recursive: true })
await vfs.writeFile('/test-dir/test.txt', 'Hello')
// Call init again (simulating new request)
const brain3 = new Brainy({ requireSubtype: false,
storage: {
type: 'filesystem',
path: testDir
}
})
await brain3.init()
const vfs3 = brain3.vfs()
await vfs3.init()
// Try to read root directory
const entries = await vfs3.readdir('/', { withFileTypes: true })
console.log(`\n✅ Test 4: readdir after new brain instance`)
console.log(` Entries found: ${entries.length}`)
entries.forEach((entry: any) => {
console.log(` - ${entry.name} (${entry.type})`)
})
expect(entries.length).toBeGreaterThan(0)
})
it('should show if Contains relationships are created', async () => {
const vfs = brain.vfs
// Get root entity ID using workaround
const collections = await brain.find({
type: 'collection',
limit: 100
})
const roots = collections.filter(e =>
e.metadata?.path === '/' &&
e.metadata?.vfsType === 'directory'
)
expect(roots.length).toBeGreaterThanOrEqual(1)
const rootId = roots[0].id
// Check for Contains relationships FROM root
const relations = await brain.getRelations({
from: rootId
})
console.log(`\n✅ Test 5: Contains relationships`)
console.log(` Root ID: ${rootId}`)
console.log(` Relationships from root: ${relations.length}`)
relations.forEach((rel) => {
console.log(` - ${rel.from} -> ${rel.to} (${rel.type})`)
})
// Root should have at least one Contains relationship (to /test-dir)
const containsRels = relations.filter(r => r.type === 'contains')
console.log(` Contains relationships: ${containsRels.length}`)
expect(containsRels.length).toBeGreaterThan(0)
})
})

View file

@ -1,64 +0,0 @@
/**
* Debug VFS search to understand what's being returned
*/
import { describe, it, beforeAll, afterAll } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import * as fs from 'fs'
import * as path from 'path'
describe('VFS Search Debug', () => {
const testDir = path.join(process.cwd(), 'test-vfs-search-debug')
let brain: Brainy
beforeAll(async () => {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
fs.mkdirSync(testDir, { recursive: true })
brain = new Brainy({ requireSubtype: false,
storage: {
type: 'filesystem',
options: { path: testDir }
}
})
await brain.init()
})
afterAll(() => {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
})
it('should debug vfs.search() results', async () => {
const vfs = brain.vfs
await vfs.init()
await vfs.writeFile('/test.txt', 'Hello world test content')
console.log('\n📋 VFS Search Debug')
// Search
const results = await vfs.search('test', { limit: 10 })
console.log(`Results: ${results.length}`)
console.log('Result structure:', JSON.stringify(results[0], null, 2))
// Also check raw brain.find
const brainResults = await brain.find({
where: { vfsType: 'file' },
includeVFS: true,
limit: 10
})
console.log(`\nDirect brain.find results: ${brainResults.length}`)
if (brainResults.length > 0) {
console.log('Brain result entity:', JSON.stringify({
id: brainResults[0].id,
type: brainResults[0].type,
metadata: brainResults[0].metadata
}, null, 2))
}
})
})

View file

@ -1,145 +0,0 @@
/**
* VFS Where Clause Diagnostic
*
* The real issue: brain.find({ where: { 'metadata.path': '/' } }) returns 0 results
* even though the root entity exists!
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import * as fs from 'fs'
import * as path from 'path'
describe('VFS Where Clause Diagnostic', () => {
const testDir = path.join(process.cwd(), 'test-where-clause')
let brain: Brainy
beforeAll(async () => {
// Clean up
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
fs.mkdirSync(testDir, { recursive: true })
brain = new Brainy({ requireSubtype: false,
storage: {
type: 'filesystem',
path: testDir
}
})
await brain.init()
})
afterAll(() => {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true })
}
})
it('should find root entity using where clause', async () => {
const vfs = brain.vfs
await vfs.init()
// Method 1: Where clause (FIXED in v4.3.3!)
console.log('\n📋 Method 1: Where clause (CORRECT field names)')
const whereResult = await brain.find({
where: {
path: '/', // ✅ Fixed: Use flat field name
vfsType: 'directory' // ✅ Fixed: Use flat field name
},
limit: 10
})
console.log(` Found ${whereResult.length} entities`)
// Method 2: Find all and filter (what actually works)
console.log('\n📋 Method 2: Find all + filter')
const allEntities = await brain.find({ limit: 100 })
console.log(` Total entities: ${allEntities.length}`)
const filtered = allEntities.filter(e =>
e.metadata?.path === '/' &&
e.metadata?.vfsType === 'directory'
)
console.log(` Filtered to roots: ${filtered.length}`)
if (filtered.length > 0) {
console.log(` Root entity:`)
console.log(` ID: ${filtered[0].id}`)
console.log(` Type: ${filtered[0].type}`)
console.log(` Metadata.path: ${filtered[0].metadata?.path}`)
console.log(` Metadata.vfsType: ${filtered[0].metadata?.vfsType}`)
}
// Method 3: Type-based search
console.log('\n📋 Method 3: Type query (collection)')
const typeResult = await brain.find({
type: 'collection',
limit: 10
})
console.log(` Found ${typeResult.length} collection entities`)
// FIXED (v4.3.3): Where clause now works with correct field names!
expect(whereResult.length).toBe(1) // ✅ WHERE CLAUSE WORKS NOW!
expect(filtered.length).toBe(1) // ✅ MANUAL FILTER ALSO WORKS
})
it('should find regular VFS files using where clause', async () => {
const vfs = brain.vfs
await vfs.mkdir('/test', { recursive: true })
await vfs.writeFile('/test/hello.txt', 'Hello World')
// Try to find the file using where clause (FIXED field names)
console.log('\n📋 Finding /test/hello.txt')
const whereResult = await brain.find({
where: {
path: '/test/hello.txt', // ✅ Fixed: Use flat field name
vfsType: 'file' // ✅ Fixed: Use flat field name
},
limit: 10
})
console.log(` Where clause: ${whereResult.length} results`)
const allEntities = await brain.find({ limit: 100 })
const filtered = allEntities.filter(e =>
e.metadata?.path === '/test/hello.txt'
)
console.log(` Manual filter: ${filtered.length} results`)
if (filtered.length > 0) {
console.log(` File metadata:`)
console.log(` Path: ${filtered[0].metadata?.path}`)
console.log(` VfsType: ${filtered[0].metadata?.vfsType}`)
console.log(` Name: ${filtered[0].metadata?.name}`)
}
// FIXED (v4.3.3): Where clause works with correct field names!
expect(whereResult.length).toBe(1) // ✅ FIXED!
expect(filtered.length).toBe(1) // ✅ ALSO WORKS
})
it('should show metadata index status', async () => {
console.log('\n📋 Metadata Index Status')
const allEntities = await brain.find({ limit: 100 })
console.log(` Total entities: ${allEntities.length}`)
const withMetadata = allEntities.filter(e => e.metadata && Object.keys(e.metadata).length > 0)
console.log(` With metadata: ${withMetadata.length}`)
const withPath = allEntities.filter(e => e.metadata?.path)
console.log(` With metadata.path: ${withPath.length}`)
const withVfsType = allEntities.filter(e => e.metadata?.vfsType)
console.log(` With metadata.vfsType: ${withVfsType.length}`)
// Show sample metadata
if (withPath.length > 0) {
console.log(`\n Sample entity with path:`)
const sample = withPath[0]
console.log(` ID: ${sample.id}`)
console.log(` Type: ${sample.type}`)
console.log(` Metadata keys: ${Object.keys(sample.metadata || {}).join(', ')}`)
}
})
})

View file

@ -143,7 +143,7 @@ function calculatePackageSize(): {
describe('Package Size Breakdown', () => {
it('should report the estimated package size and largest files', () => {
const { totalSize, includedFiles } = calculatePackageSize()
console.log('Estimated package size: ' + totalSize.toFixed(2) + ' MB')
console.log('\nLargest files:')
for (let i = 0; i < Math.min(10, includedFiles.length); i++) {
@ -151,26 +151,18 @@ describe('Package Size Breakdown', () => {
`${includedFiles[i].path}: ${includedFiles[i].size.toFixed(2)} MB`
)
}
// Basic sanity check
expect(totalSize).toBeGreaterThan(0)
expect(includedFiles.length).toBeGreaterThan(0)
})
it('should identify files that contribute significantly to package size', () => {
const { includedFiles } = calculatePackageSize()
// Find files larger than 1MB
// Informational: files larger than 1MB (model + WASM assets expected)
const largeFiles = includedFiles.filter(file => file.size > 1)
if (largeFiles.length > 0) {
console.log('\nFiles larger than 1MB:')
largeFiles.forEach(file => {
console.log(`${file.path}: ${file.size.toFixed(2)} MB`)
})
}
// This is not a failure condition, just informational
expect(true).toBe(true)
// Basic sanity check
expect(totalSize).toBeGreaterThan(0)
expect(includedFiles.length).toBeGreaterThan(0)
})
})

View file

@ -113,6 +113,10 @@ describe('TypeAware Performance Benchmarks', () => {
// At 1K entities: ~1-3x speedup (small dataset)
// At 1M entities: ~5-10x speedup (projected, not measured)
// At 1B entities: ~10x speedup (PROJECTED, extrapolated)
// Assert measurement validity, not a perf number (CI hardware varies).
expect(typeDuration).toBeGreaterThan(0)
expect(speedup).toBeGreaterThan(0)
expect(Number.isFinite(speedup)).toBe(true)
})
})

View file

@ -1,7 +1,7 @@
/**
* Regression tests: metadata index cleanup after delete / deleteMany
* Regression tests: metadata index cleanup after remove / removeMany
*
* Bug report (wickworks): brain.deleteMany() was not removing metadata index
* Bug report (wickworks): brain.removeMany() was not removing metadata index
* entries for deleted entities. The same defect also existed in delete().
*
* Root causes fixed:
@ -22,7 +22,7 @@
* Fix: entityForIndexing now uses the same conditional spreading pattern as
* storageMetadata for confidence, weight, and createdBy.
*
* 3. result.successful updated inside transaction builder deleteMany() pushed
* 3. result.successful updated inside transaction builder removeMany() pushed
* ids to result.successful during the builder phase, before transaction.execute()
* ran. A transaction rollback would leave result.successful containing ids that
* were never actually deleted.
@ -39,7 +39,7 @@ const DIM = 384
const makeVec = (seed = 1) =>
new Float32Array(DIM).map((_, i) => ((i + seed) % DIM) / DIM)
describe('Metadata index cleanup after delete / deleteMany', () => {
describe('Metadata index cleanup after remove / removeMany', () => {
let brain: Brainy
beforeEach(async () => {
@ -80,7 +80,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
describe('delete()', () => {
it('removes entity from type index so find({ type }) returns 0', async () => {
const id = await addEntity({ type: 'product' })
await brain.delete(id)
await brain.remove(id)
const results = await brain.find({ type: 'product' as any })
expect(results).toHaveLength(0)
@ -90,7 +90,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
// Add one entity with service 'alpha', delete it.
// A ne:'beta' query should return 0, not the deleted entity.
const id = await addEntity({ service: 'alpha', type: 'thing' })
await brain.delete(id)
await brain.remove(id)
const results = await brain.find({ where: { service: { ne: 'beta' } } })
const ids = results.map(r => r.id)
@ -101,7 +101,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
// Entity added without a custom 'archivedAt' field.
// After deletion it must not appear in an exists:false query.
const id = await addEntity({ type: 'thing' })
await brain.delete(id)
await brain.remove(id)
const results = await brain.find({ where: { archivedAt: { exists: false } } })
const ids = results.map(r => r.id)
@ -110,10 +110,10 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
})
// ---------------------------------------------------------------------------
// deleteMany() — batch deletion
// removeMany() — batch deletion
// ---------------------------------------------------------------------------
describe('deleteMany()', () => {
describe('removeMany()', () => {
it('removes all entities from type index so find({ type }) returns 0', async () => {
const ids = await Promise.all([
addEntity({ type: 'concept' }),
@ -121,7 +121,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
addEntity({ type: 'concept' }),
])
await brain.deleteMany({ ids })
await brain.removeMany({ ids })
const results = await brain.find({ type: 'concept' as any, limit: 50 })
expect(results).toHaveLength(0)
@ -140,7 +140,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
addEntity({ service: 'beta', type: 'thing' }),
])
await brain.deleteMany({ ids: alphaIds })
await brain.removeMany({ ids: alphaIds })
const results = await brain.find({ where: { service: { ne: 'beta' } } })
const resultIds = results.map(r => r.id)
@ -168,7 +168,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
// One surviving entity also without closedAt — it SHOULD appear in results
const survivorId = await addEntity({ type: 'thing', metadata: { region: 'us', active: true } })
await brain.deleteMany({ ids: deletedIds })
await brain.removeMany({ ids: deletedIds })
const results = await brain.find({ where: { closedAt: { exists: false } } })
const resultIds = results.map(r => r.id)
@ -189,7 +189,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
addEntity({ type: 'thing' }),
])
const result = await brain.deleteMany({ ids })
const result = await brain.removeMany({ ids })
// All should succeed — verify successful list is correct
expect(result.successful).toHaveLength(ids.length)
@ -206,7 +206,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
})
it('handles empty ids array gracefully', async () => {
const result = await brain.deleteMany({ ids: [] })
const result = await brain.removeMany({ ids: [] })
expect(result.successful).toHaveLength(0)
expect(result.failed).toHaveLength(0)
})
@ -217,7 +217,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
Array.from({ length: 25 }, (_, i) => addEntity({ type: 'document', metadata: { i } }))
)
const result = await brain.deleteMany({ ids })
const result = await brain.removeMany({ ids })
expect(result.successful).toHaveLength(25)
expect(result.failed).toHaveLength(0)
@ -267,7 +267,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
// After deletion the confidence/__NULL__ and weight/__NULL__ bitmap entries
// (if any were created) must not be surfaced through any query.
const id = await addEntity({ type: 'thing' })
await brain.delete(id)
await brain.remove(id)
// Entity must not appear in any confidence query
const existsTrue = await brain.find({ where: { confidence: { exists: true } } })
@ -282,7 +282,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
// Survivors are unaffected
// ---------------------------------------------------------------------------
describe('partial deleteMany does not affect surviving entities', () => {
describe('partial removeMany does not affect surviving entities', () => {
it('surviving entities remain queryable after deleting others of the same type', async () => {
const toDelete = await Promise.all([
addEntity({ type: 'collection', metadata: { group: 'a' } }),
@ -294,7 +294,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
addEntity({ type: 'collection', metadata: { group: 'b' } }),
])
await brain.deleteMany({ ids: toDelete })
await brain.removeMany({ ids: toDelete })
const results = await brain.find({ type: 'collection' as any, limit: 50 })
const resultIds = results.map(r => r.id)
@ -315,7 +315,7 @@ describe('Metadata index cleanup after delete / deleteMany', () => {
const b = await addEntity({ type: 'task', service: 'svc' })
const c = await addEntity({ type: 'task', service: 'svc' })
await brain.delete(a)
await brain.remove(a)
const results = await brain.find({ type: 'task' as any, limit: 50 })
const ids = results.map(r => r.id)

View file

@ -60,7 +60,7 @@ describe('v5.7.0 Deadlock Regression', () => {
const start = Date.now()
// Force GraphAdjacencyIndex usage by querying relationships
const relations = await brain.getRelations({ from: entities[0] })
const relations = await brain.related({ from: entities[0] })
const elapsed = Date.now() - start
@ -123,7 +123,7 @@ describe('v5.7.0 Deadlock Regression', () => {
const start = Date.now()
// This would trigger initialization if not already done
const relations = await brain.getRelations({ from: entity1 })
const relations = await brain.related({ from: entity1 })
const elapsed = Date.now() - start

View file

@ -1,54 +0,0 @@
#!/bin/bash
# Script to migrate tests from old Brainy API to v3.0 API
# This updates deprecated methods and types
echo "🔄 Migrating tests to Brainy 3.0 API..."
# List of test files to update
TEST_FILES=(
"tests/integration/brainy-core.integration.test.ts"
"tests/integration/brainy-complete.integration.test.ts"
)
for file in "${TEST_FILES[@]}"; do
if [ -f "$file" ]; then
echo "📝 Updating $file..."
# Backup original
cp "$file" "$file.backup"
# Replace BrainyData with Brainy
sed -i 's/BrainyData/Brainy/g' "$file"
# Replace addNoun with add (using proper params)
sed -i "s/brain\.addNoun(\([^,)]*\))/brain.add({ data: \1, type: 'thing' })/g" "$file"
sed -i "s/brain\.addNoun(\([^,]*\), \([^)]*\))/brain.add({ data: \1, type: 'thing', metadata: \2 })/g" "$file"
# Replace addVerb with relate
sed -i "s/brain\.addVerb(\([^,]*\), \([^,]*\), \([^)]*\))/brain.relate({ from: \1, to: \2, type: \3 })/g" "$file"
# Replace getNoun with get
sed -i 's/brain\.getNoun/brain.get/g' "$file"
# Replace getVerb with getRelations
sed -i 's/brain\.getVerb/brain.getRelations/g' "$file"
# Replace GraphNoun with Entity
sed -i 's/GraphNoun/Entity/g' "$file"
# Replace GraphVerb with Relation
sed -i 's/GraphVerb/Relation/g' "$file"
# Update import statements
sed -i "s/import { Brainy } from '..\/..\/dist\/index.js'/import { Brainy } from '..\/..\/src\/brainy'/g" "$file"
# Fix forceMemoryStorage to new format
sed -i "s/storage: { forceMemoryStorage: true }/storage: { type: 'memory' }/g" "$file"
echo "✅ Updated $file"
fi
done
echo "📊 Migration complete! Review the changes and run tests to verify."
echo "💡 Backup files created with .backup extension"

View file

@ -138,7 +138,7 @@ describe('Transactions + Distributed Storage Integration', () => {
// Verify relationships
for (let i = 0; i < entities.length - 1; i++) {
const relations = await brain.getRelations({ from: entities[i] })
const relations = await brain.related({ from: entities[i] })
expect(relations).toHaveLength(1)
}
})
@ -260,7 +260,7 @@ describe('Transactions + Distributed Storage Integration', () => {
expect(entity).toBeTruthy()
// Delete atomically
await brain.delete(id)
await brain.remove(id)
// Verify deleted
entity = await brain.get(id)
@ -286,14 +286,14 @@ describe('Transactions + Distributed Storage Integration', () => {
})
// Delete first entity (should delete relationships)
await brain.delete(id1)
await brain.remove(id1)
// Verify entity deleted
const entity1 = await brain.get(id1)
expect(entity1).toBeNull()
// Verify relationships deleted
const relations = await brain.getRelations({ from: id1 })
const relations = await brain.related({ from: id1 })
expect(relations).toHaveLength(0)
// Entity 2 should still exist
@ -325,7 +325,7 @@ describe('Transactions + Distributed Storage Integration', () => {
expect(entity?.data.name).toBe('Updated via Adapter')
// Delete entity
await brain.delete(id)
await brain.remove(id)
const deletedEntity = await brain.get(id)
expect(deletedEntity).toBeNull()
})
@ -359,7 +359,7 @@ describe('Transactions + Distributed Storage Integration', () => {
// Verify all operations succeeded (regardless of latency)
const entity1 = await brain.get(id1)
const entity2 = await brain.get(id2)
const relations = await brain.getRelations({ from: id1 })
const relations = await brain.related({ from: id1 })
expect(entity1).toBeTruthy()
expect(entity2).toBeTruthy()

View file

@ -71,7 +71,7 @@ describe('Transactions + Sharding Integration', () => {
// Verify all entities exist
const entity1 = await brain.get(id1)
const entity2 = await brain.get(id2)
const relations = await brain.getRelations({ from: id1 })
const relations = await brain.related({ from: id1 })
expect(entity1).toBeTruthy()
expect(entity2).toBeTruthy()
@ -213,9 +213,9 @@ describe('Transactions + Sharding Integration', () => {
})
// Verify all relationships exist
const relations0 = await brain.getRelations({ from: entities[0].id })
const relations1 = await brain.getRelations({ from: entities[1].id })
const relations2 = await brain.getRelations({ from: entities[2].id })
const relations0 = await brain.related({ from: entities[0].id })
const relations1 = await brain.related({ from: entities[1].id })
const relations2 = await brain.related({ from: entities[2].id })
expect(relations0).toHaveLength(1)
expect(relations1).toHaveLength(1)
@ -249,7 +249,7 @@ describe('Transactions + Sharding Integration', () => {
})
// Delete first entity (should also delete relationship)
await brain.delete(id1)
await brain.remove(id1)
// Verify entity deleted from shard G
const entity1 = await brain.get(id1)
@ -260,7 +260,7 @@ describe('Transactions + Sharding Integration', () => {
expect(entity2).toBeTruthy()
// Relationship should be deleted
const relations = await brain.getRelations({ from: id1 })
const relations = await brain.related({ from: id1 })
expect(relations).toHaveLength(0)
})
})

View file

@ -272,8 +272,8 @@ describe('Transactions + TypeAware Storage Integration', () => {
})
// Verify relationships exist
const personRelations = await brain.getRelations({ from: personId })
const orgRelations = await brain.getRelations({ from: orgId })
const personRelations = await brain.related({ from: personId })
const orgRelations = await brain.related({ from: orgId })
expect(personRelations).toHaveLength(2)
expect(orgRelations).toHaveLength(1)
@ -310,7 +310,7 @@ describe('Transactions + TypeAware Storage Integration', () => {
})
// Delete person (should cascade delete relationships)
await brain.delete(personId)
await brain.remove(personId)
// Verify person deleted
const person = await brain.get(personId)
@ -323,7 +323,7 @@ describe('Transactions + TypeAware Storage Integration', () => {
expect(task).toBeTruthy()
// Verify relationships from person are deleted
const relations = await brain.getRelations({ from: personId })
const relations = await brain.related({ from: personId })
expect(relations).toHaveLength(0)
})
})

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

@ -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()

View file

@ -34,7 +34,7 @@ describe('VFS Graph Relationships', () => {
const readmeId = await vfs.resolvePath('/projects/brainy/README.md')
// Verify relationships are created properly
const projectRelations = await brain.getRelations({
const projectRelations = await brain.related({
from: projectsId,
type: VerbType.Contains
})
@ -43,7 +43,7 @@ describe('VFS Graph Relationships', () => {
expect(projectRelations[0].to).toBe(brainyId)
// Verify brainy directory contains its files
const brainyRelations = await brain.getRelations({
const brainyRelations = await brain.related({
from: brainyId,
type: VerbType.Contains
})
@ -89,7 +89,7 @@ describe('VFS Graph Relationships', () => {
const doc1Id = await vfs.resolvePath('/doc1.md')
const doc2Id = await vfs.resolvePath('/doc2.md')
const directRelations = await brain.getRelations({
const directRelations = await brain.related({
from: doc1Id,
type: VerbType.References
})
@ -114,7 +114,7 @@ describe('VFS Graph Relationships', () => {
const testId = entity.id
// Check that root contains test.txt via relationships
const rootRelations = await brain.getRelations({
const rootRelations = await brain.related({
from: rootId,
type: VerbType.Contains
})
@ -135,7 +135,7 @@ describe('VFS Graph Relationships', () => {
// Verify it's using relationships, not metadata queries
const manyId = await vfs.resolvePath('/many')
const relations = await brain.getRelations({
const relations = await brain.related({
from: manyId,
type: VerbType.Contains
})
@ -158,14 +158,14 @@ describe('VFS Graph Relationships', () => {
const fileId = await vfs.resolvePath('/dest/file.txt')
// Source should not contain file anymore
const sourceRelations = await brain.getRelations({
const sourceRelations = await brain.related({
from: sourceId,
type: VerbType.Contains
})
expect(sourceRelations).toHaveLength(0)
// Dest should contain file
const destRelations = await brain.getRelations({
const destRelations = await brain.related({
from: destId,
type: VerbType.Contains
})
@ -204,7 +204,7 @@ describe('VFS Graph Relationships', () => {
const fileEntityId = await vfs.resolvePath('/test-new.txt')
// Check that Contains relationship exists
const relations = await brain.getRelations({
const relations = await brain.related({
from: rootId,
to: fileEntityId,
type: VerbType.Contains
@ -221,7 +221,7 @@ describe('VFS Graph Relationships', () => {
await vfs.writeFile('/test-new.txt', 'Updated content')
// Relationship should still exist after update
const relationsAfterUpdate = await brain.getRelations({
const relationsAfterUpdate = await brain.related({
from: rootId,
to: fileEntityId,
type: VerbType.Contains
@ -237,7 +237,7 @@ describe('VFS Graph Relationships', () => {
await vfs.writeFile('/test-new.txt', 'Another update')
await vfs.writeFile('/test-new.txt', 'Yet another update')
const finalRelations = await brain.getRelations({
const finalRelations = await brain.related({
from: rootId,
to: fileEntityId,
type: VerbType.Contains
@ -258,7 +258,7 @@ describe('VFS Graph Relationships', () => {
const fileEntityId = await vfs.resolvePath('/orphan-test.txt')
// Manually delete the Contains relationship to simulate the bug
const initialRelations = await brain.getRelations({
const initialRelations = await brain.related({
from: rootId,
to: fileEntityId,
type: VerbType.Contains
@ -270,7 +270,7 @@ describe('VFS Graph Relationships', () => {
}
// Verify the relationship is gone
const brokenRelations = await brain.getRelations({
const brokenRelations = await brain.related({
from: rootId,
to: fileEntityId,
type: VerbType.Contains
@ -285,7 +285,7 @@ describe('VFS Graph Relationships', () => {
await vfs.writeFile('/orphan-test.txt', 'Fixed content')
// Verify the relationship is restored
const fixedRelations = await brain.getRelations({
const fixedRelations = await brain.related({
from: rootId,
to: fileEntityId,
type: VerbType.Contains
@ -314,7 +314,7 @@ describe('VFS Graph Relationships', () => {
const fileEntityId = await vfs.resolvePath('/level1/level2/level3/deep.txt')
// Verify Contains relationship exists
const relations = await brain.getRelations({
const relations = await brain.related({
from: level3Id,
to: fileEntityId,
type: VerbType.Contains
@ -329,7 +329,7 @@ describe('VFS Graph Relationships', () => {
// Update the file and verify relationship persists
await vfs.writeFile('/level1/level2/level3/deep.txt', 'Updated deep content')
const relationsAfterUpdate = await brain.getRelations({
const relationsAfterUpdate = await brain.related({
from: level3Id,
to: fileEntityId,
type: VerbType.Contains