import assert from 'node:assert/strict'; import test from 'node:test'; import { parseMemoryV2ExternalSmokeArgs, runMemoryV2ExternalSmokeCli, } from './smoke-memory-v2-external.mjs'; function writableBuffer() { let value = ''; return { write(chunk) { value += String(chunk); }, value() { return value; }, }; } test('parseMemoryV2ExternalSmokeArgs validates backend and defaults operation', () => { assert.deepEqual( parseMemoryV2ExternalSmokeArgs(['--backend', 'redis-streams']), { backend: 'redis-streams', operation: 'write', query: 'memory-chain smoke', limit: 1, vector: null, userId: 'memory-v2-smoke-user', sessionId: 'memory-v2-smoke-session', help: false, }, ); assert.deepEqual( parseMemoryV2ExternalSmokeArgs([ '--backend', 'weaviate', '--operation', 'resolve', '--vector', '0.1,0.2', '--limit', '2', ]).vector, [0.1, 0.2], ); assert.throws( () => parseMemoryV2ExternalSmokeArgs(['--backend', 'unknown']), /--backend must be one of/, ); }); test('runMemoryV2ExternalSmokeCli smokes Qdrant through runtime HTTP wiring', async () => { const stdout = writableBuffer(); const report = await runMemoryV2ExternalSmokeCli({ argv: ['--backend', 'qdrant', '--operation', 'resolve', '--limit', '2'], env: { MEMORY_QDRANT_ENABLED: '1', MEMORY_QDRANT_URL: 'http://qdrant.local', MEMORY_QDRANT_COLLECTION: 'memind_memory', MEMORY_QDRANT_EMBEDDING_MODULE: './embed.mjs', }, stdout, async importModule() { return { async embedQuery() { return [0.1, 0.2, 0.3]; } }; }, async fetchImpl(url, options) { assert.equal(url, 'http://qdrant.local/collections/memind_memory/points/search'); assert.equal(JSON.parse(options.body).limit, 2); return { ok: true, async json() { return { result: [ { id: 'q1', score: 0.9, payload: { content: 'semantic memory', type: 'semantic' }, }, ], }; }, }; }, }); const output = JSON.parse(stdout.value()); assert.equal(report.backendAvailable, true); assert.equal(output.selectedBackend, 'qdrant'); assert.equal(output.result.source, 'qdrant'); }); test('runMemoryV2ExternalSmokeCli smokes Weaviate through runtime HTTP wiring', async () => { const stdout = writableBuffer(); const report = await runMemoryV2ExternalSmokeCli({ argv: ['--backend', 'weaviate', '--operation', 'resolve', '--limit', '2'], env: { MEMORY_WEAVIATE_ENABLED: '1', MEMORY_WEAVIATE_URL: 'https://weaviate.local', MEMORY_WEAVIATE_EMBEDDING_MODULE: './embed.mjs', }, stdout, async importModule() { return { async embedQuery() { return [0.1, 0.2]; } }; }, async fetchImpl(url, options) { assert.equal(url, 'https://weaviate.local/v1/graphql'); assert.equal(JSON.parse(options.body).variables.limit, 2); return { ok: true, async json() { return { data: { Get: { MemindMemory: [{ content: 'semantic memory', _additional: { id: 'w1' } }], }, }, }; }, }; }, }); const output = JSON.parse(stdout.value()); assert.equal(report.backendAvailable, true); assert.equal(output.selectedBackend, 'weaviate'); assert.equal(output.result.source, 'weaviate'); }); test('runMemoryV2ExternalSmokeCli smokes Redis Streams with injected redis module', async () => { const stdout = writableBuffer(); const xAdds = []; let quitCalled = false; const report = await runMemoryV2ExternalSmokeCli({ argv: ['--backend', 'redis-streams'], env: { MEMORY_REDIS_STREAMS_ENABLED: '1', MEMORY_REDIS_STREAMS_URL: 'redis://localhost:6379', }, stdout, async importRedis() { return { createClient() { return { async connect() {}, async xAdd(stream, id, fields) { xAdds.push({ stream, id, fields }); }, async quit() { quitCalled = true; }, }; }, }; }, }); assert.equal(report.backendAvailable, true); assert.equal(report.result.source, 'redis-streams'); assert.equal(xAdds[0].stream, 'memind:memory-events'); assert.equal(quitCalled, true); }); test('runMemoryV2ExternalSmokeCli reports fallback when backend is not configured', async () => { const stdout = writableBuffer(); const report = await runMemoryV2ExternalSmokeCli({ argv: ['--backend', 'langgraph'], env: {}, stdout, }); assert.equal(report.backendAvailable, false); assert.equal(report.selectedBackend, 'legacy-conversation-memory'); assert.equal(report.backendReason, 'not_configured'); });