/** * Comprehensive Metadata-Only Integration Test * * Verifies the 8.0 metadata-only read model works across subsystems: * - Storage adapters (memory, filesystem) * - Indexes (Metadata, Graph, HNSW) * - APIs (find, update, remove, related, similar) * - VFS operations (read/stat/readdir) * * Reminder (8.0): brain.get(id) loads metadata only — entity.vector is []. * Pass { includeVectors: true } when you need the embedding. find() returns * Result[] (flattened metadata + full entity); vectors are not on the Result. */ import { describe, it, expect, beforeEach, afterEach } from 'vitest' import { Brainy } from '../../src/brainy.js' import { VirtualFileSystem } from '../../src/vfs/VirtualFileSystem.js' import { NounType, VerbType } from '../../src/types/graphTypes.js' import { mkdtempSync, rmSync } from 'fs' import { tmpdir } from 'os' import { join } from 'path' describe('Metadata-Only Comprehensive Integration', () => { describe('Storage Adapters', () => { it('should work with MemoryStorage', async () => { const brain = new Brainy({ requireSubtype: false, storage: { type: '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({ requireSubtype: false, storage: { type: 'filesystem', 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({ requireSubtype: false, storage: { type: '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) // 8.0: find() Result is metadata-flattened; vectors are not part of the // Result shape. The metadata/where filter is what this test verifies. expect(results[0].metadata.category).toBe('electronics') }) 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.relate({ from: alice, to: bob, type: VerbType.Knows }) // related() reads the graph adjacency index (8.0 replaces getVerbsBySource) const relationships = await brain.related({ from: alice }) expect(relationships.length).toBe(1) expect(relationships[0].to).toBe(bob) expect(relationships[0].type).toBe(VerbType.Knows) }) 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({ requireSubtype: false, storage: { type: '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.remove() should work after metadata-only get', async () => { const entity = await brain.get(entityId) expect(entity).toBeTruthy() await brain.remove(entityId) const deleted = await brain.get(entityId) expect(deleted).toBeNull() }) it('brain.find() should return results with flattened metadata + entity', async () => { const results = await brain.find({ type: NounType.Thing, limit: 10 }) expect(results.length).toBeGreaterThan(0) // 8.0: find() returns Result[] with common entity fields flattened to the // top level (id/type/metadata) plus the full entity under `entity`. // Vectors are intentionally NOT part of the Result shape. const hit = results.find(r => r.id === entityId) expect(hit).toBeTruthy() expect(hit!.type).toBe(NounType.Thing) expect(hit!.metadata.value).toBe('original') expect(hit!.entity.id).toBe(entityId) }) 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({ requireSubtype: false, storage: { type: 'filesystem', 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) // PERF: env-dependent — relaxed generously from the original 50ms. expect(time).toBeLessThan(250) }) 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) // PERF: env-dependent — relaxed generously from the original 200ms. expect(time).toBeLessThan(1000) }) }) describe('Performance Verification', () => { it('metadata-only should be significantly faster', async () => { const brain = new Brainy({ requireSubtype: false, storage: { type: '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() }) }) })