brainy/tests/comprehensive/brainy-v3-complete.test.ts
David Snelling 58daf09403 feat: remove legacy ImportManager, standardize getStats() API
- Removed ImportManager class and exports (use brain.import() instead)
- Fixed all documentation: getStatistics() → getStats()
- Updated 41 files across codebase for consistency
- Removed ImportManager section from API docs
- Added v3.30.0 migration guide to CHANGELOG

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 11:40:31 -07:00

958 lines
No EOL
28 KiB
TypeScript

/**
* Brainy v3.0 Comprehensive Test Suite
*
* Complete validation of all public APIs, augmentations, and features
* Designed for production-grade quality assurance
*/
import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { NounType, VerbType } from '../../src/types/graphTypes.js'
import { CacheAugmentation } from '../../src/augmentations/cacheAugmentation.js'
import { IndexAugmentation } from '../../src/augmentations/indexAugmentation.js'
import { MetricsAugmentation } from '../../src/augmentations/metricsAugmentation.js'
import { createStorage } from '../../src/storage/storageFactory.js'
import { promises as fs } from 'fs'
import { tmpdir } from 'os'
import { join } from 'path'
// Test utilities
const createTestBrain = async (config?: any) => {
const brain = new Brainy({
storage: { type: 'memory' },
cache: true,
index: true,
metrics: true,
...config
})
await brain.init()
return brain
}
const generateTestData = (count: number) => {
const data: Array<{
text: string
metadata: {
id: number
category: string
score: number
tags: string[]
created: string
}
}> = []
for (let i = 0; i < count; i++) {
data.push({
text: `Test item ${i} with content about ${['JavaScript', 'TypeScript', 'Python', 'AI', 'Database'][i % 5]}`,
metadata: {
id: i,
category: ['tech', 'science', 'business'][i % 3],
score: Math.random() * 100,
tags: [`tag${i % 5}`, `tag${i % 3}`],
created: new Date(Date.now() - i * 86400000).toISOString()
}
})
}
return data
}
describe('Brainy v3.0 Complete Test Suite', () => {
describe('1. Core CRUD Operations', () => {
let brain: Brainy
beforeEach(async () => {
brain = await createTestBrain()
})
afterEach(async () => {
await brain.close()
})
describe('1.1 Add Operations', () => {
it('should add a simple text item', async () => {
const id = await brain.add({
data: 'Hello world',
type: NounType.Document
})
expect(id).toBeDefined()
expect(typeof id).toBe('string')
expect(id.length).toBeGreaterThan(0)
})
it('should add item with custom ID', async () => {
const customId = 'custom-123'
const id = await brain.add({
id: customId,
data: 'Custom ID test',
type: NounType.Document
})
expect(id).toBe(customId)
})
it('should add item with metadata', async () => {
const metadata = {
title: 'Test Doc',
category: 'testing',
score: 95.5,
tags: ['test', 'validation']
}
const id = await brain.add({
data: 'Document with metadata',
metadata,
type: NounType.Document
})
const retrieved = await brain.get(id)
expect(retrieved?.metadata).toEqual(metadata)
})
it('should add item with pre-computed vector', async () => {
const vector = new Array(384).fill(0).map(() => Math.random())
const id = await brain.add({
data: 'Pre-vectorized content',
vector,
type: NounType.Document
})
const retrieved = await brain.get(id)
expect(retrieved).toBeDefined()
expect(retrieved?.vector).toHaveLength(384)
})
it('should handle adding multiple items concurrently', async () => {
const promises = Array(10).fill(0).map((_, i) =>
brain.add({
data: `Concurrent item ${i}`,
type: NounType.Document
})
)
const ids = await Promise.all(promises)
expect(ids).toHaveLength(10)
expect(new Set(ids).size).toBe(10) // All unique
})
it('should reject invalid noun types', async () => {
await expect(brain.add({
data: 'Invalid type test',
type: 'InvalidType' as any
})).rejects.toThrow()
})
})
describe('1.2 Get Operations', () => {
let testId: string
beforeEach(async () => {
testId = await brain.add({
data: 'Test document for retrieval',
metadata: { test: true },
type: NounType.Document
})
})
it('should retrieve existing item by ID', async () => {
const item = await brain.get(testId)
expect(item).toBeDefined()
expect(item?.id).toBe(testId)
expect(item?.metadata?.test).toBe(true)
expect(item?.type).toBe(NounType.Document)
})
it('should return null for non-existent ID', async () => {
const item = await brain.get('non-existent-id')
expect(item).toBeNull()
})
it('should retrieve with vector included', async () => {
const item = await brain.get(testId)
expect(item?.vector).toBeDefined()
expect(item?.vector).toHaveLength(384) // Default dimension
})
})
describe('1.3 Update Operations', () => {
let testId: string
beforeEach(async () => {
testId = await brain.add({
data: 'Original content',
metadata: { version: 1 },
type: NounType.Document
})
})
it('should update existing item data', async () => {
const success = await brain.update({
id: testId,
data: 'Updated content'
})
expect(success).toBe(true)
const updated = await brain.get(testId)
expect(updated).toBeDefined()
// Vector should be different after content update
})
it('should update only metadata', async () => {
const success = await brain.update({
id: testId,
metadata: { version: 2, updated: true }
})
expect(success).toBe(true)
const updated = await brain.get(testId)
expect(updated?.metadata).toEqual({ version: 2, updated: true })
})
it('should update both data and metadata', async () => {
const success = await brain.update({
id: testId,
data: 'New content',
metadata: { version: 3 }
})
expect(success).toBe(true)
const updated = await brain.get(testId)
expect(updated?.metadata?.version).toBe(3)
})
it('should return false for non-existent ID', async () => {
const success = await brain.update({
id: 'non-existent',
data: 'Will fail'
})
expect(success).toBe(false)
})
})
describe('1.4 Delete Operations', () => {
it('should delete existing item', async () => {
const id = await brain.add({
data: 'To be deleted',
type: NounType.Document
})
const success = await brain.delete(id)
expect(success).toBe(true)
const item = await brain.get(id)
expect(item).toBeNull()
})
it('should return false for non-existent ID', async () => {
const success = await brain.delete('non-existent')
expect(success).toBe(false)
})
it('should handle concurrent deletes safely', async () => {
const ids = await Promise.all(
Array(5).fill(0).map(() =>
brain.add({ data: 'Concurrent delete test', type: NounType.Document })
)
)
await Promise.all(ids.map(id => brain.delete(id)))
// delete() returns void, not boolean
// Verify all deleted
const items = await Promise.all(ids.map(id => brain.get(id)))
expect(items.every(item => item === null)).toBe(true)
})
})
describe('1.5 Clear Operations', () => {
it('should clear all data', async () => {
// Add test data
await Promise.all(
Array(10).fill(0).map((_, i) =>
brain.add({ data: `Item ${i}`, type: NounType.Document })
)
)
await brain.clear()
const stats = await brain.insights()
expect(stats.entities).toBe(0)
})
})
})
describe('2. Find and Triple Intelligence', () => {
let brain: Brainy
let testData: any[]
beforeAll(async () => {
brain = await createTestBrain()
testData = generateTestData(50)
// Add test data
for (const item of testData) {
await brain.add({
data: item.text,
metadata: item.metadata,
type: NounType.Document
})
}
})
afterAll(async () => {
await brain.close()
})
describe('2.1 Vector Search', () => {
it('should find similar items by query', async () => {
const results = await brain.find({
query: 'JavaScript programming',
limit: 5
})
expect(results).toHaveLength(5)
expect(results[0].score).toBeGreaterThanOrEqual(0)
expect(results[0].score).toBeLessThanOrEqual(1)
expect(results[0].entity).toBeDefined()
})
it('should respect limit parameter', async () => {
const results = await brain.find({
query: 'TypeScript',
limit: 3
})
expect(results).toHaveLength(3)
})
it('should find by vector directly', async () => {
const vector = new Array(384).fill(0).map(() => Math.random())
const results = await brain.find({
vector,
limit: 5
})
expect(results).toHaveLength(5)
})
})
describe('2.2 Metadata Filtering', () => {
it('should filter by single metadata field', async () => {
const results = await brain.find({
where: { category: 'tech' }
})
expect(results.length).toBeGreaterThan(0)
results.forEach(r => {
expect(r.entity.metadata?.category).toBe('tech')
})
})
it('should filter by multiple metadata fields', async () => {
const results = await brain.find({
where: {
category: 'science',
score: { $gte: 50 }
}
})
results.forEach(r => {
expect(r.entity.metadata?.category).toBe('science')
expect(r.entity.metadata?.score).toBeGreaterThanOrEqual(50)
})
})
it('should support range queries', async () => {
const results = await brain.find({
where: {
score: { $gte: 25, $lte: 75 }
}
})
results.forEach(r => {
const score = r.entity.metadata?.score
expect(score).toBeGreaterThanOrEqual(25)
expect(score).toBeLessThanOrEqual(75)
})
})
it('should support array contains', async () => {
const results = await brain.find({
where: {
tags: { $contains: 'tag1' }
}
})
results.forEach(r => {
expect(r.entity.metadata?.tags).toContain('tag1')
})
})
})
describe('2.3 Type Filtering', () => {
it('should filter by single type', async () => {
const results = await brain.find({
type: NounType.Document
})
results.forEach(r => {
expect(r.entity.type).toBe(NounType.Document)
})
})
it('should filter by multiple types', async () => {
const results = await brain.find({
type: [NounType.Document, NounType.File]
})
results.forEach(r => {
expect([NounType.Document, NounType.File]).toContain(r.entity.type)
})
})
})
describe('2.4 Triple Intelligence Fusion', () => {
it('should combine vector and metadata search', async () => {
const results = await brain.find({
query: 'JavaScript',
where: { category: 'tech' },
fusion: {
strategy: 'adaptive',
weights: { vector: 0.7, field: 0.3 }
}
})
expect(results.length).toBeGreaterThan(0)
results.forEach(r => {
expect(r.entity.metadata?.category).toBe('tech')
})
})
it('should use adaptive fusion strategy', async () => {
const results = await brain.find({
query: 'database',
where: { score: { $gte: 60 } },
fusion: { strategy: 'adaptive' }
})
expect(results).toBeDefined()
})
})
})
describe('3. Relationship Management', () => {
let brain: Brainy
let entityA: string
let entityB: string
let entityC: string
beforeAll(async () => {
brain = await createTestBrain()
entityA = await brain.add({ data: 'Entity A', type: NounType.Person })
entityB = await brain.add({ data: 'Entity B', type: NounType.Organization })
entityC = await brain.add({ data: 'Entity C', type: NounType.Document })
})
afterAll(async () => {
await brain.close()
})
it('should create relationships between entities', async () => {
const verbId = await brain.relate({
from: entityA,
to: entityB,
type: VerbType.WorksWith,
metadata: { since: '2023' }
})
expect(verbId).toBeDefined()
expect(typeof verbId).toBe('string')
})
it('should retrieve relationships', async () => {
await brain.relate({
from: entityA,
to: entityC,
type: VerbType.Creates
})
const relations = await brain.getRelations({ from: entityA })
expect(relations.length).toBeGreaterThanOrEqual(1)
expect(relations[0].from).toBe(entityA)
})
it('should find related entities', async () => {
const related = await brain.getRelations({
from: entityA,
type: VerbType.WorksWith
})
expect(related).toBeDefined()
expect(related.some(r => r.id === entityB)).toBe(true)
})
})
describe('4. Augmentation System', () => {
describe('4.1 Augmentation Registration', () => {
it('should list built-in augmentations', async () => {
const brain = await createTestBrain()
const augmentations = brain.augmentations.list()
expect(augmentations).toContain('cache')
expect(augmentations).toContain('index')
expect(augmentations).toContain('metrics')
await brain.close()
})
it('should check augmentation presence', async () => {
const brain = await createTestBrain()
expect(brain.augmentations.has('cache')).toBe(true)
expect(brain.augmentations.has('non-existent')).toBe(false)
await brain.close()
})
})
describe('4.2 Cache Augmentation', () => {
it('should cache search results', async () => {
const brain = await createTestBrain({ cache: true })
// Add test data
await brain.add({ data: 'Cached content', type: NounType.Document })
// First search (cache miss)
const start1 = Date.now()
const results1 = await brain.find({ query: 'Cached' })
const time1 = Date.now() - start1
// Second search (cache hit)
const start2 = Date.now()
const results2 = await brain.find({ query: 'Cached' })
const time2 = Date.now() - start2
expect(results1).toEqual(results2)
// Cache hit should be faster (this might be flaky in CI)
await brain.close()
})
it('should invalidate cache on data changes', async () => {
const brain = await createTestBrain({ cache: true })
const id = await brain.add({ data: 'Initial', type: NounType.Document })
// Search to populate cache
const results1 = await brain.find({ query: 'Initial' })
// Update data
await brain.update({ id, data: 'Modified' })
// Search again (cache should be invalidated)
const results2 = await brain.find({ query: 'Modified' })
expect(results2.length).toBeGreaterThanOrEqual(0)
await brain.close()
})
})
describe('4.3 Index Augmentation', () => {
it('should enable fast metadata filtering', async () => {
const brain = await createTestBrain({ index: true })
// Add items with metadata
const items = Array(100).fill(0).map((_, i) => ({
data: `Item ${i}`,
metadata: {
category: i % 3 === 0 ? 'A' : i % 3 === 1 ? 'B' : 'C',
index: i
},
type: NounType.Document
}))
for (const item of items) {
await brain.add(item)
}
// Fast metadata query
const start = Date.now()
const results = await brain.find({
where: { category: 'A' }
})
const queryTime = Date.now() - start
expect(results.length).toBeGreaterThan(0)
expect(queryTime).toBeLessThan(100) // Should be fast
await brain.close()
})
})
describe('4.4 Metrics Augmentation', () => {
it('should collect operation metrics', async () => {
const brain = await createTestBrain({ metrics: true })
// Perform operations
await brain.add({ data: 'Test', type: NounType.Document })
await brain.find({ query: 'Test' })
const stats = brain.getStats()
expect(stats).toBeDefined()
expect(stats.totalNouns).toBeGreaterThanOrEqual(1)
await brain.close()
})
})
})
describe('5. Storage Adapters', () => {
describe('5.1 Memory Storage', () => {
it('should work with memory storage', async () => {
const brain = new Brainy({
storage: { type: 'memory' }
})
await brain.init()
const id = await brain.add({ data: 'Memory test', type: NounType.Document })
const item = await brain.get(id)
expect(item).toBeDefined()
expect(item?.id).toBe(id)
await brain.close()
})
})
describe('5.2 Filesystem Storage', () => {
it('should work with filesystem storage', async () => {
const tempDir = join(tmpdir(), `brainy-test-${Date.now()}`)
const brain = new Brainy({
storage: {
type: 'filesystem',
options: {
path: tempDir
}
}
})
await brain.init()
const id = await brain.add({ data: 'FS test', type: NounType.Document })
const item = await brain.get(id)
expect(item).toBeDefined()
await brain.close()
// Cleanup
await fs.rm(tempDir, { recursive: true, force: true })
})
it('should persist data across restarts', async () => {
const tempDir = join(tmpdir(), `brainy-persist-${Date.now()}`)
// First session
const brain1 = new Brainy({
storage: { type: 'filesystem', options: { path: tempDir } }
})
await brain1.init()
const id = await brain1.add({
data: 'Persistent data',
metadata: { persistent: true },
type: NounType.Document
})
await brain1.close()
// Second session
const brain2 = new Brainy({
storage: { type: 'filesystem', options: { path: tempDir } }
})
await brain2.init()
const item = await brain2.get(id)
expect(item).toBeDefined()
expect(item?.metadata?.persistent).toBe(true)
await brain2.close()
// Cleanup
await fs.rm(tempDir, { recursive: true, force: true })
})
})
})
describe('6. Neural API and Clustering', () => {
let brain: Brainy
beforeAll(async () => {
brain = await createTestBrain()
// Add diverse test data for clustering
const topics = ['AI', 'Web', 'Database', 'Security', 'Cloud']
for (let i = 0; i < 25; i++) {
await brain.add({
data: `Document about ${topics[i % 5]} technology ${i}`,
metadata: { topic: topics[i % 5], index: i },
type: NounType.Document
})
}
})
afterAll(async () => {
await brain.close()
})
describe('6.1 Similarity Calculation', () => {
it('should calculate similarity between entities', async () => {
const id1 = await brain.add({ data: 'JavaScript programming', type: NounType.Document })
const id2 = await brain.add({ data: 'TypeScript programming', type: NounType.Document })
const id3 = await brain.add({ data: 'Database management', type: NounType.Document })
const sim12 = await brain.neural().similar(id1, id2)
const sim13 = await brain.neural().similar(id1, id3)
const score12 = typeof sim12 === 'number' ? sim12 : sim12.score
const score13 = typeof sim13 === 'number' ? sim13 : sim13.score
expect(score12).toBeGreaterThan(score13) // JS and TS more similar than JS and DB
expect(score12).toBeGreaterThan(0.5)
expect(score13).toBeLessThan(0.8)
})
})
describe('6.2 Clustering', () => {
it.skip('should perform hierarchical clustering', async () => {
// TODO: Fix clusters API call - parameters don't match implementation
// const clusters = await brain.neural().clusters({
// algorithm: 'hierarchical',
// threshold: 0.7
// })
// expect(clusters).toBeDefined()
// expect(Array.isArray(clusters)).toBe(true)
// expect(clusters.length).toBeGreaterThan(0)
// // Each cluster should have members
// clusters.forEach(cluster => {
// expect(cluster.members).toBeDefined()
// expect(cluster.members.length).toBeGreaterThan(0)
// })
})
it.skip('should perform k-means clustering', async () => {
// TODO: Fix clusters API call - parameters don't match implementation
// const clusters = await brain.neural().clusters({
// algorithm: 'kmeans',
// k: 3
// })
// expect(clusters).toHaveLength(3)
})
})
})
describe('7. Performance and Scale', () => {
it('should handle 1000 items efficiently', async () => {
const brain = await createTestBrain()
const start = Date.now()
// Add 1000 items
const promises = Array(1000).fill(0).map((_, i) =>
brain.add({
data: `Item ${i} with random content ${Math.random()}`,
metadata: { index: i },
type: NounType.Document
})
)
await Promise.all(promises)
const addTime = Date.now() - start
// Should complete in reasonable time
expect(addTime).toBeLessThan(30000) // 30 seconds for 1000 items
// Search should still be fast
const searchStart = Date.now()
const results = await brain.find({ query: 'random content', limit: 10 })
const searchTime = Date.now() - searchStart
expect(results).toHaveLength(10)
expect(searchTime).toBeLessThan(1000) // Search under 1 second
await brain.close()
}, 60000) // 60 second timeout for this test
it('should handle concurrent operations', async () => {
const brain = await createTestBrain()
// Concurrent adds
const addPromises = Array(50).fill(0).map((_, i) =>
brain.add({ data: `Concurrent ${i}`, type: NounType.Document })
)
// Concurrent searches
const searchPromises = Array(10).fill(0).map(() =>
brain.find({ query: 'test', limit: 5 })
)
const results = await Promise.all([...addPromises, ...searchPromises])
expect(results).toHaveLength(60)
await brain.close()
})
})
describe('8. Error Handling and Edge Cases', () => {
let brain: Brainy
beforeEach(async () => {
brain = await createTestBrain()
})
afterEach(async () => {
await brain.close()
})
it('should handle empty queries gracefully', async () => {
const results = await brain.find({ query: '' })
expect(results).toBeDefined()
expect(Array.isArray(results)).toBe(true)
})
it('should handle very long text', async () => {
const longText = 'x'.repeat(100000) // 100k characters
const id = await brain.add({ data: longText, type: NounType.Document })
expect(id).toBeDefined()
const item = await brain.get(id)
expect(item).toBeDefined()
})
it('should handle special characters', async () => {
const specialText = '!@#$%^&*()_+-=[]{}|;\':",./<>?`~'
const id = await brain.add({ data: specialText, type: NounType.Document })
const item = await brain.get(id)
expect(item).toBeDefined()
})
it('should handle unicode text', async () => {
const unicodeText = '你好世界 🌍 مرحبا بالعالم'
const id = await brain.add({ data: unicodeText, type: NounType.Document })
const item = await brain.get(id)
expect(item).toBeDefined()
})
it('should reject invalid metadata', async () => {
await expect(brain.add({
data: 'Test',
metadata: { circular: {} } as any,
type: NounType.Document
})).rejects.toThrow()
})
})
describe('9. Statistics and Insights', () => {
let brain: Brainy
beforeAll(async () => {
brain = await createTestBrain()
// Add varied test data
await brain.add({ data: 'Doc 1', type: NounType.Document })
await brain.add({ data: 'Person 1', type: NounType.Person })
await brain.add({ data: 'Org 1', type: NounType.Organization })
const id1 = await brain.add({ data: 'A', type: NounType.Document })
const id2 = await brain.add({ data: 'B', type: NounType.Document })
await brain.relate({ from: id1, to: id2, type: VerbType.References })
})
afterAll(async () => {
await brain.close()
})
it('should provide accurate statistics', async () => {
const stats = brain.getStats()
expect(stats).toBeDefined()
expect(stats.totalNouns).toBeGreaterThanOrEqual(5)
expect(stats.totalVerbs).toBeGreaterThanOrEqual(1)
})
it('should provide insights', async () => {
const insights = await brain.insights()
expect(insights).toBeDefined()
expect(insights.entities).toBeGreaterThanOrEqual(5)
expect(insights.relationships).toBeGreaterThanOrEqual(1)
expect(insights.types).toBeDefined()
expect(Object.keys(insights.types).length).toBeGreaterThan(0)
})
it('should suggest relevant queries', async () => {
const suggestions = await brain.suggest({ limit: 3 })
expect(suggestions).toBeDefined()
expect(suggestions.queries).toBeDefined()
expect(Array.isArray(suggestions.queries)).toBe(true)
})
})
})
// Run performance benchmark
describe('Performance Benchmarks', () => {
it('should meet performance targets', async () => {
const brain = await createTestBrain()
const benchmarks = {
add: { target: 10, actual: 0 }, // 10ms per add
get: { target: 5, actual: 0 }, // 5ms per get
search: { target: 50, actual: 0 }, // 50ms per search
update: { target: 15, actual: 0 } // 15ms per update
}
// Benchmark add
const addStart = Date.now()
const id = await brain.add({ data: 'Benchmark', type: NounType.Document })
benchmarks.add.actual = Date.now() - addStart
// Benchmark get
const getStart = Date.now()
await brain.get(id)
benchmarks.get.actual = Date.now() - getStart
// Benchmark search
const searchStart = Date.now()
await brain.find({ query: 'Benchmark', limit: 5 })
benchmarks.search.actual = Date.now() - searchStart
// Benchmark update
const updateStart = Date.now()
await brain.update({ id, metadata: { updated: true } })
benchmarks.update.actual = Date.now() - updateStart
// Check performance
Object.entries(benchmarks).forEach(([op, perf]) => {
console.log(`${op}: ${perf.actual}ms (target: ${perf.target}ms)`)
expect(perf.actual).toBeLessThan(perf.target * 2) // Allow 2x margin
})
await brain.close()
})
})