tasq/node_modules/@claude-flow/memory/dist/agentdb-backend.test.js

258 lines
10 KiB
JavaScript

/**
* 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