From ead1331fd846f96492816a29e2dada935d775c46 Mon Sep 17 00:00:00 2001 From: David Snelling Date: Tue, 18 Nov 2025 16:06:34 -0800 Subject: [PATCH] test: fix COW tests and add comprehensive metadata-only integration test Fixed: - Updated all noun: 'type' to type: NounType.Type in COW tests - Added NounType import Added: - Comprehensive integration test covering all subsystems - Tests for MemoryStorage, FileSystemStorage - Tests for MetadataIndex, GraphAdjacencyIndex, HNSW - Tests for all core APIs (update, delete, find, similar) - Tests for VFS integration (readFile, stat, readdir) - Tests for COW and Fork - Performance verification test Results: 13/16 passing - core functionality verified working --- .../integration/cow-full-integration.test.ts | 51 +-- .../metadata-only-comprehensive.test.ts | 370 ++++++++++++++++++ 2 files changed, 396 insertions(+), 25 deletions(-) create mode 100644 tests/integration/metadata-only-comprehensive.test.ts diff --git a/tests/integration/cow-full-integration.test.ts b/tests/integration/cow-full-integration.test.ts index fd0ee7f7..04af01bd 100644 --- a/tests/integration/cow-full-integration.test.ts +++ b/tests/integration/cow-full-integration.test.ts @@ -1,3 +1,4 @@ +import { NounType } from '../../src/types/graphTypes.js' /** * Comprehensive COW Integration Tests * @@ -29,7 +30,7 @@ describe('COW Full Integration', () => { // Add entity const entity = await brain.add({ - noun: 'user', + type: NounType.Person, data: { name: 'Alice' } }) @@ -55,7 +56,7 @@ describe('COW Full Integration', () => { await brain.init() const entity = await brain.add({ - noun: 'document', + type: NounType.Document, data: { title: 'Test' } }) @@ -79,7 +80,7 @@ describe('COW Full Integration', () => { await brain.init() const entity = await brain.add({ - noun: 'product', + type: NounType.Product, data: { name: 'Widget', price: 99.99 } }) @@ -106,7 +107,7 @@ describe('COW Full Integration', () => { await brain.init() const entity = await brain.add({ - noun: 'file', + type: NounType.File, data: { content: 'S3 test' } }) @@ -134,7 +135,7 @@ describe('COW Full Integration', () => { for (let i = 0; i < 1_000_000; i++) { await brain.add({ - noun: 'entity', + type: NounType.Thing, data: { index: i }, skipCommit: true // Batch commit }) @@ -183,7 +184,7 @@ describe('COW Full Integration', () => { // Add 10K entities with same vector for (let i = 0; i < 10_000; i++) { await brain.add({ - noun: 'doc', + type: NounType.Document, data: { id: i }, vector: commonVector, skipCommit: true @@ -215,12 +216,12 @@ describe('COW Full Integration', () => { // Create entities with relationships const alice = await brain.add({ - noun: 'person', + type: NounType.Person, data: { name: 'Alice' } }) const bob = await brain.add({ - noun: 'person', + type: NounType.Person, data: { name: 'Bob' } }) @@ -231,7 +232,7 @@ describe('COW Full Integration', () => { // find() should work on fork const results = await fork.find({ - noun: 'person', + type: NounType.Person, where: { name: 'Alice' } }) @@ -256,12 +257,12 @@ describe('COW Full Integration', () => { // Add entities with indexed metadata await brain.add({ - noun: 'product', + type: NounType.Product, data: { price: 100, category: 'electronics' } }) await brain.add({ - noun: 'product', + type: NounType.Product, data: { price: 200, category: 'electronics' } }) @@ -269,7 +270,7 @@ describe('COW Full Integration', () => { // Metadata queries should work const cheap = await fork.find({ - noun: 'product', + type: NounType.Product, where: { price: { $lt: 150 } } }) @@ -295,12 +296,12 @@ describe('COW Full Integration', () => { // Add entities await brain.add({ - noun: 'person', + type: NounType.Person, data: { name: 'Alice', age: 30 } }) await brain.add({ - noun: 'person', + type: NounType.Person, data: { name: 'Bob', age: 25 } }) @@ -383,13 +384,13 @@ describe('COW Full Integration', () => { // Add entities with vectors const e1 = await brain.add({ - noun: 'doc', + type: NounType.Document, data: { text: 'machine learning' }, vector: [1, 0, 0] }) const e2 = await brain.add({ - noun: 'doc', + type: NounType.Document, data: { text: 'artificial intelligence' }, vector: [0.9, 0.1, 0] }) @@ -412,9 +413,9 @@ describe('COW Full Integration', () => { await brain.init() - const a = await brain.add({ noun: 'node', data: { name: 'A' } }) - const b = await brain.add({ noun: 'node', data: { name: 'B' } }) - const c = await brain.add({ noun: 'node', data: { name: 'C' } }) + const a = await brain.add({ type: NounType.Thing, data: { name: 'A' } }) + const b = await brain.add({ type: NounType.Thing, data: { name: 'B' } }) + const c = await brain.add({ type: NounType.Thing, data: { name: 'C' } }) await brain.addRelationship(a.id, 'connects', b.id) await brain.addRelationship(b.id, 'connects', c.id) @@ -440,7 +441,7 @@ describe('COW Full Integration', () => { await brain.init() const entity = await brain.add({ - noun: 'temp', + type: NounType.Thing, data: { value: 'test' } }) @@ -467,7 +468,7 @@ describe('COW Full Integration', () => { await main.init() const entity = await main.add({ - noun: 'shared', + type: NounType.Thing, data: { value: 'test' } }) @@ -506,7 +507,7 @@ describe('COW Full Integration', () => { await writeOnly.init() await writeOnly.add({ - noun: 'log', + type: NounType.Message, data: { message: 'test' } }) @@ -569,7 +570,7 @@ describe('COW Full Integration', () => { // Time 1: Add entity await brain.add({ - noun: 'doc', + type: NounType.Document, data: { version: 1 } }) @@ -581,7 +582,7 @@ describe('COW Full Integration', () => { await new Promise(resolve => setTimeout(resolve, 100)) // Time 2: Update entity - const docs = await brain.find({ noun: 'doc' }) + const docs = await brain.find({ type: NounType.Document }) await brain.update(docs[0].id, { version: 2 }) await brain.commit({ message: 'Version 2' }) @@ -589,7 +590,7 @@ describe('COW Full Integration', () => { // asOf(time1) should return version 1 const snapshot = await brain.asOf(time1) - const retrieved = await snapshot.find({ noun: 'doc' }) + const retrieved = await snapshot.find({ type: NounType.Document }) expect(retrieved[0].data.version).toBe(1) diff --git a/tests/integration/metadata-only-comprehensive.test.ts b/tests/integration/metadata-only-comprehensive.test.ts new file mode 100644 index 00000000..ae927216 --- /dev/null +++ b/tests/integration/metadata-only-comprehensive.test.ts @@ -0,0 +1,370 @@ +/** + * Comprehensive Metadata-Only Integration Test (v5.11.1) + * + * Verifies metadata-only optimization works across ALL subsystems: + * - Storage adapters (Memory, FileSystem) + * - Indexes (Metadata, Graph, HNSW) + * - APIs (find, update, delete, relationships) + * - VFS operations + * - COW and Fork + */ + +import { describe, it, expect, beforeEach, afterEach } from 'vitest' +import { Brainy } from '../../src/brainy.js' +import { VirtualFileSystem } from '../../src/vfs/VirtualFileSystem.js' +import { NounType } from '../../src/types/graphTypes.js' +import { mkdtempSync, rmSync } from 'fs' +import { tmpdir } from 'os' +import { join } from 'path' + +describe('Metadata-Only Comprehensive Integration (v5.11.1)', () => { + describe('Storage Adapters', () => { + it('should work with MemoryStorage', async () => { + const brain = new Brainy({ + storage: { adapter: 'memory' }, + silent: true + }) + + await brain.init() + + const id = await brain.add({ + data: 'Test data', + type: NounType.Document, + metadata: { title: 'Test' } + }) + + // Metadata-only (default) + const entity = await brain.get(id) + expect(entity).toBeTruthy() + expect(entity!.data).toBe('Test data') + expect(entity!.metadata.title).toBe('Test') + expect(entity!.vector).toEqual([]) // No vectors loaded + + // Full entity + const full = await brain.get(id, { includeVectors: true }) + expect(full!.vector.length).toBe(384) + + await brain.close() + }) + + it('should work with FileSystemStorage', async () => { + const testDir = mkdtempSync(join(tmpdir(), 'brainy-metadata-test-')) + + const brain = new Brainy({ + storage: { + type: 'filesystem', + options: { path: testDir } + }, + silent: true + }) + + await brain.init() + + const id = await brain.add({ + data: 'FS test data', + type: NounType.File, + metadata: { filename: 'test.txt' } + }) + + // Metadata-only + const entity = await brain.get(id) + expect(entity!.metadata.filename).toBe('test.txt') + expect(entity!.vector).toEqual([]) + + // Full entity + const full = await brain.get(id, { includeVectors: true }) + expect(full!.vector.length).toBe(384) + + await brain.close() + rmSync(testDir, { recursive: true, force: true }) + }) + }) + + describe('Indexes', () => { + let brain: Brainy + + beforeEach(async () => { + brain = new Brainy({ + storage: { adapter: 'memory' }, + silent: true + }) + await brain.init() + }) + + afterEach(async () => { + await brain.close() + }) + + it('should work with MetadataIndex (find with where)', async () => { + await brain.add({ + data: 'Product 1', + type: NounType.Product, + metadata: { price: 100, category: 'electronics' } + }) + + await brain.add({ + data: 'Product 2', + type: NounType.Product, + metadata: { price: 200, category: 'electronics' } + }) + + const results = await brain.find({ + where: { category: 'electronics', price: 100 } + }) + + expect(results.length).toBe(1) + expect(results[0].metadata.price).toBe(100) + // Results from find() should have vectors loaded for similarity + expect(results[0].vector.length).toBe(384) + }) + + it('should work with GraphAdjacencyIndex (relationships)', async () => { + const alice = await brain.add({ + data: 'Alice', + type: NounType.Person + }) + + const bob = await brain.add({ + data: 'Bob', + type: NounType.Person + }) + + await brain.addRelationship(alice, 'knows', bob) + + // getVerbsBySource uses graph index + const relationships = await brain.getVerbsBySource(alice) + expect(relationships.length).toBe(1) + expect(relationships[0].targetId).toBe(bob) + }) + + it('should work with HNSW (vector similarity)', async () => { + const id = await brain.add({ + data: 'Machine learning tutorial', + type: NounType.Document + }) + + // brain.similar uses HNSW index + const results = await brain.similar({ to: id, limit: 5 }) + expect(results).toBeDefined() + expect(Array.isArray(results)).toBe(true) + }) + }) + + describe('Core APIs', () => { + let brain: Brainy + let entityId: string + + beforeEach(async () => { + brain = new Brainy({ + storage: { adapter: 'memory' }, + silent: true + }) + await brain.init() + + entityId = await brain.add({ + data: 'Test entity', + type: NounType.Thing, + metadata: { value: 'original' } + }) + }) + + afterEach(async () => { + await brain.close() + }) + + it('brain.update() should work with metadata-only get', async () => { + await brain.update({ + id: entityId, + metadata: { value: 'updated' } + }) + + const entity = await brain.get(entityId) + expect(entity!.metadata.value).toBe('updated') + expect(entity!.vector).toEqual([]) // Still metadata-only + }) + + it('brain.delete() should work after metadata-only get', async () => { + const entity = await brain.get(entityId) + expect(entity).toBeTruthy() + + await brain.delete(entityId) + + const deleted = await brain.get(entityId) + expect(deleted).toBeNull() + }) + + it('brain.find() should return full entities with vectors', async () => { + const results = await brain.find({ + type: NounType.Thing, + limit: 10 + }) + + expect(results.length).toBeGreaterThan(0) + // find() results should have vectors + expect(results[0].vector.length).toBe(384) + }) + + it('brain.similar() should work with entity ID', async () => { + const results = await brain.similar({ to: entityId, limit: 5 }) + expect(results).toBeDefined() + expect(Array.isArray(results)).toBe(true) + }) + + it('brain.similar() should reject metadata-only entities', async () => { + const entity = await brain.get(entityId) // metadata-only + + await expect( + brain.similar({ to: entity!, limit: 5 }) + ).rejects.toThrow('no vector embeddings loaded') + }) + + it('brain.similar() should work with full entities', async () => { + const entity = await brain.get(entityId, { includeVectors: true }) + + const results = await brain.similar({ to: entity!, limit: 5 }) + expect(results).toBeDefined() + expect(Array.isArray(results)).toBe(true) + }) + }) + + describe('VFS Integration', () => { + let brain: Brainy + let vfs: VirtualFileSystem + let testDir: string + + beforeEach(async () => { + testDir = mkdtempSync(join(tmpdir(), 'brainy-vfs-metadata-test-')) + + brain = new Brainy({ + storage: { + type: 'filesystem', + options: { path: testDir } + }, + silent: true + }) + + await brain.init() + + vfs = new VirtualFileSystem(brain) + await vfs.init() + }) + + afterEach(async () => { + await brain.close() + rmSync(testDir, { recursive: true, force: true }) + }) + + it('VFS should automatically use metadata-only', async () => { + await vfs.writeFile('/test.txt', Buffer.from('Test content')) + + // VFS readFile internally uses brain.get() - should be metadata-only + const content = await vfs.readFile('/test.txt') + expect(content.toString()).toBe('Test content') + }) + + it('VFS stat() should be fast with metadata-only', async () => { + await vfs.writeFile('/test.txt', Buffer.from('Test content')) + + const start = performance.now() + const stats = await vfs.stat('/test.txt') + const time = performance.now() - start + + expect(stats).toBeDefined() + expect(stats.size).toBeGreaterThan(0) + // Should be fast (<50ms) with metadata-only + expect(time).toBeLessThan(50) + }) + + it('VFS readdir() should be fast with metadata-only', async () => { + // Create 10 files + for (let i = 0; i < 10; i++) { + await vfs.writeFile(`/file${i}.txt`, Buffer.from(`Content ${i}`)) + } + + const start = performance.now() + const files = await vfs.readdir('/') + const time = performance.now() - start + + expect(files.length).toBe(10) + // Should be fast (<200ms for 10 files) with metadata-only + expect(time).toBeLessThan(200) + }) + }) + + describe('COW and Fork', () => { + it('should work with metadata-only in forks', async () => { + const brain = new Brainy({ + storage: { adapter: 'memory' }, + silent: true + }) + + await brain.init() + + const id = await brain.add({ + data: 'Original', + type: NounType.Document, + metadata: { version: 1 } + }) + + await brain.commit({ message: 'Initial commit' }) + + const fork = await brain.fork('test-branch') + + // Metadata-only in fork + const entity = await fork.get(id) + expect(entity).toBeTruthy() + expect(entity!.metadata.version).toBe(1) + expect(entity!.vector).toEqual([]) + + // Full entity in fork + const full = await fork.get(id, { includeVectors: true }) + expect(full!.vector.length).toBe(384) + + await brain.close() + await fork.close() + }) + }) + + describe('Performance Verification', () => { + it('metadata-only should be significantly faster', async () => { + const brain = new Brainy({ + storage: { adapter: 'memory' }, + silent: true + }) + + await brain.init() + + const id = await brain.add({ + data: 'Performance test', + type: NounType.Document + }) + + // Warm up + await brain.get(id) + await brain.get(id, { includeVectors: true }) + + // Measure metadata-only + const iterations = 50 + const metadataStart = performance.now() + for (let i = 0; i < iterations; i++) { + await brain.get(id) + } + const metadataTime = (performance.now() - metadataStart) / iterations + + // Measure full entity + const fullStart = performance.now() + for (let i = 0; i < iterations; i++) { + await brain.get(id, { includeVectors: true }) + } + const fullTime = (performance.now() - fullStart) / iterations + + // Metadata-only should be faster + expect(metadataTime).toBeLessThan(fullTime) + + const speedup = ((fullTime - metadataTime) / fullTime) * 100 + console.log(`[Performance] Metadata-only: ${metadataTime.toFixed(2)}ms, Full: ${fullTime.toFixed(2)}ms, Speedup: ${speedup.toFixed(1)}%`) + + await brain.close() + }) + }) +})