/** * AgentDB Backend Tests * * Tests for agentdb@2.0.0-alpha.3.4 integration with V3 memory system */ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { AgentDBBackend } from './agentdb-backend.js'; import { createDefaultEntry } from './types.js'; describe('AgentDBBackend', () => { let backend; beforeEach(async () => { backend = new AgentDBBackend({ dbPath: ':memory:', namespace: 'test', vectorDimension: 384, }); await backend.initialize(); }); afterEach(async () => { await backend.shutdown(); }); describe('Initialization', () => { it('should initialize successfully', async () => { expect(backend).toBeDefined(); }); it('should handle missing agentdb gracefully', async () => { const fallbackBackend = new AgentDBBackend(); await fallbackBackend.initialize(); // Should still work with in-memory fallback expect(fallbackBackend).toBeDefined(); await fallbackBackend.shutdown(); }); }); describe('Basic CRUD Operations', () => { it('should store and retrieve entries', async () => { const entry = createDefaultEntry({ key: 'test-key', content: 'Test content', type: 'episodic', }); await backend.store(entry); const retrieved = await backend.get(entry.id); expect(retrieved).toBeDefined(); expect(retrieved?.content).toBe('Test content'); }); it('should get entry by key', async () => { const entry = createDefaultEntry({ key: 'unique-key', content: 'Unique content', namespace: 'test', }); await backend.store(entry); const retrieved = await backend.getByKey('test', 'unique-key'); expect(retrieved).toBeDefined(); expect(retrieved?.content).toBe('Unique content'); }); it('should update entries', async () => { const entry = createDefaultEntry({ key: 'update-test', content: 'Original content', }); await backend.store(entry); const originalVersion = entry.version; const updated = await backend.update(entry.id, { content: 'Updated content', }); expect(updated?.content).toBe('Updated content'); expect(updated?.version).toBe(originalVersion + 1); }); it('should delete entries', async () => { const entry = createDefaultEntry({ key: 'delete-test', content: 'To be deleted', }); await backend.store(entry); const deleted = await backend.delete(entry.id); expect(deleted).toBe(true); const retrieved = await backend.get(entry.id); expect(retrieved).toBeNull(); }); }); describe('Query Operations', () => { beforeEach(async () => { // Insert test data await backend.store(createDefaultEntry({ key: 'entry-1', content: 'First entry', namespace: 'test', tags: ['tag1', 'tag2'], })); await backend.store(createDefaultEntry({ key: 'entry-2', content: 'Second entry', namespace: 'test', tags: ['tag2', 'tag3'], })); await backend.store(createDefaultEntry({ key: 'entry-3', content: 'Third entry', namespace: 'other', tags: ['tag1'], })); }); it('should query by namespace', async () => { const results = await backend.query({ type: 'hybrid', namespace: 'test', limit: 10, }); expect(results.length).toBe(2); }); it('should query by exact key', async () => { const results = await backend.query({ type: 'exact', namespace: 'test', key: 'entry-1', limit: 10, }); expect(results.length).toBe(1); expect(results[0].content).toBe('First entry'); }); it('should query by prefix', async () => { const results = await backend.query({ type: 'prefix', keyPrefix: 'entry-', limit: 10, }); expect(results.length).toBeGreaterThanOrEqual(2); }); it('should query by tags', async () => { const results = await backend.query({ type: 'tag', tags: ['tag2'], limit: 10, }); expect(results.length).toBe(2); }); }); describe('Vector Search', () => { it('should perform brute-force search when agentdb unavailable', async () => { const entry = createDefaultEntry({ key: 'vector-test', content: 'Vector content', }); // Create a fake embedding entry.embedding = new Float32Array([0.1, 0.2, 0.3, 0.4]); await backend.store(entry); const queryEmbedding = new Float32Array([0.1, 0.2, 0.3, 0.4]); const results = await backend.search(queryEmbedding, { k: 5 }); expect(results.length).toBeGreaterThan(0); expect(results[0].entry.id).toBe(entry.id); }); it('should handle semantic queries', async () => { const entry = createDefaultEntry({ key: 'semantic-test', content: 'Semantic content', }); entry.embedding = new Float32Array([0.5, 0.5, 0.5, 0.5]); await backend.store(entry); const results = await backend.query({ type: 'semantic', embedding: new Float32Array([0.5, 0.5, 0.5, 0.5]), limit: 5, }); expect(results.length).toBeGreaterThan(0); }); }); describe('Bulk Operations', () => { it('should bulk insert entries', async () => { const entries = [ createDefaultEntry({ key: 'bulk-1', content: 'Bulk 1' }), createDefaultEntry({ key: 'bulk-2', content: 'Bulk 2' }), createDefaultEntry({ key: 'bulk-3', content: 'Bulk 3' }), ]; await backend.bulkInsert(entries); const count = await backend.count(); expect(count).toBeGreaterThanOrEqual(3); }); it('should bulk delete entries', async () => { const entries = [ createDefaultEntry({ key: 'delete-1', content: 'Delete 1' }), createDefaultEntry({ key: 'delete-2', content: 'Delete 2' }), ]; await backend.bulkInsert(entries); const ids = entries.map((e) => e.id); const deleted = await backend.bulkDelete(ids); expect(deleted).toBe(2); }); }); describe('Statistics and Health', () => { it('should provide backend statistics', async () => { await backend.store(createDefaultEntry({ key: 'stats-test', content: 'Stats content' })); const stats = await backend.getStats(); expect(stats.totalEntries).toBeGreaterThan(0); expect(stats.avgQueryTime).toBeGreaterThanOrEqual(0); expect(stats.memoryUsage).toBeGreaterThan(0); }); it('should perform health checks', async () => { const health = await backend.healthCheck(); expect(health.status).toBeDefined(); expect(['healthy', 'degraded', 'unhealthy']).toContain(health.status); expect(health.components).toBeDefined(); expect(health.components.storage).toBeDefined(); expect(health.components.index).toBeDefined(); expect(health.components.cache).toBeDefined(); }); }); describe('Namespace Operations', () => { beforeEach(async () => { await backend.store(createDefaultEntry({ key: 'ns1-entry', content: 'NS1', namespace: 'namespace1', })); await backend.store(createDefaultEntry({ key: 'ns2-entry', content: 'NS2', namespace: 'namespace2', })); }); it('should list namespaces', async () => { const namespaces = await backend.listNamespaces(); expect(namespaces).toContain('namespace1'); expect(namespaces).toContain('namespace2'); }); it('should count entries by namespace', async () => { const count = await backend.count('namespace1'); expect(count).toBe(1); }); it('should clear namespace', async () => { const cleared = await backend.clearNamespace('namespace1'); expect(cleared).toBe(1); const count = await backend.count('namespace1'); expect(count).toBe(0); }); }); describe('Graceful Degradation', () => { it('should work without agentdb package', async () => { const fallbackBackend = new AgentDBBackend({ dbPath: ':memory:', }); await fallbackBackend.initialize(); const entry = createDefaultEntry({ key: 'fallback-test', content: 'Fallback content', }); await fallbackBackend.store(entry); const retrieved = await fallbackBackend.get(entry.id); expect(retrieved).toBeDefined(); expect(retrieved?.content).toBe('Fallback content'); await fallbackBackend.shutdown(); }); it('should indicate availability status', () => { expect(typeof backend.isAvailable()).toBe('boolean'); }); }); }); //# sourceMappingURL=agentdb-backend.test.js.map