import assert from 'node:assert/strict'; import test from 'node:test'; import { MEMORY_V2_PLUGIN_BACKEND_SPECS, createMemoryV2PluginBackends, createUnavailableMemoryBackend, } from './memory-v2-plugin-backends.mjs'; import { createMemoryV2 } from './memory-v2.mjs'; function silentLogger() { return { warn() {} }; } test('Memory V2 plugin specs cover optional future backend families', () => { assert.deepEqual( MEMORY_V2_PLUGIN_BACKEND_SPECS.map((spec) => [spec.name, spec.category]), [ ['pgvector', 'semantic'], ['qdrant', 'semantic'], ['weaviate', 'semantic'], ['mem0', 'extraction'], ['letta', 'lifecycle'], ['neo4j', 'behavior'], ['redis-streams', 'behavior'], ['langgraph', 'policy'], ], ); }); test('createUnavailableMemoryBackend exposes declared capabilities without availability', async () => { const backend = createUnavailableMemoryBackend({ name: 'mem0', category: 'extraction', role: 'automatic-memory-generation', capabilities: ['write', 'compact'], flag: 'MEMORY_MEM0_ENABLED', }); assert.equal(backend.name, 'mem0'); assert.equal(backend.category, 'extraction'); assert.equal(backend.role, 'automatic-memory-generation'); assert.equal(backend.flag, 'MEMORY_MEM0_ENABLED'); assert.equal(backend.isAvailable(), false); assert.equal(backend.getUnavailableReason(), 'not_configured'); assert.equal(typeof backend.resolve, 'undefined'); assert.deepEqual(await backend.write({}), { saved: 0, analyzed: 0, memories: 0 }); assert.deepEqual(await backend.compact({}), { analyzed: 0, memories: 0 }); }); test('createMemoryV2PluginBackends can exclude a real injected backend', () => { const backends = createMemoryV2PluginBackends({ exclude: ['pgvector'] }); assert.equal(backends.some((backend) => backend.name === 'pgvector'), false); assert.equal(backends.some((backend) => backend.name === 'qdrant'), true); }); test('Memory V2 default status lists future plugin slots without selecting them', () => { const memory = createMemoryV2({ logger: silentLogger(), legacyMemoryService: { async listMemories() { return []; }, }, env: { MEMORY_ENABLED: '1', MEMORY_BACKEND: 'legacy', }, }); const status = memory.getStatus(); const byName = new Map(status.backends.map((backend) => [backend.name, backend])); assert.equal(status.selectedBackend, 'legacy-conversation-memory'); assert.equal(byName.get('legacy-conversation-memory').available, true); assert.deepEqual( [...byName.keys()], [ 'legacy-conversation-memory', 'pgvector', 'qdrant', 'weaviate', 'mem0', 'letta', 'neo4j', 'redis-streams', 'langgraph', ], ); assert.deepEqual(byName.get('qdrant'), { name: 'qdrant', available: false, supports: { resolve: true, write: false, compact: false, }, category: 'semantic', role: 'scale-out-vector-store', flag: 'MEMORY_QDRANT_ENABLED', reason: 'not_configured', }); }); test('Memory V2 falls back to legacy when a future plugin is configured but unavailable', async () => { const memory = createMemoryV2({ logger: silentLogger(), legacyMemoryService: { async listMemories() { return [{ label: 'fact', text: 'legacy remains the safe path' }]; }, }, env: { MEMORY_ENABLED: '1', MEMORY_BACKEND: 'qdrant', }, }); const result = await memory.resolve({ userId: 'u1', query: 'memory-chain' }); assert.equal(memory.getStatus().selectedBackend, 'legacy-conversation-memory'); assert.equal(result.source, 'legacy-conversation-memory'); assert.deepEqual(result.memories, [ { label: 'fact', text: 'legacy remains the safe path' }, ]); });