import assert from 'node:assert/strict'; import test from 'node:test'; import { inspectMemoryV2Backend } from './memory-v2-backend-contract.mjs'; import { createQdrantHttpClient, createQdrantMemoryBackend } from './memory-v2-qdrant.mjs'; test('qdrant backend is disabled by default and passes contract gate', async () => { const backend = createQdrantMemoryBackend(); const report = inspectMemoryV2Backend(backend); assert.equal(backend.name, 'qdrant'); assert.equal(backend.category, 'semantic'); assert.equal(backend.role, 'scale-out-vector-store'); assert.equal(backend.flag, 'MEMORY_QDRANT_ENABLED'); assert.equal(backend.isAvailable(), false); assert.equal(backend.getUnavailableReason(), 'not_configured'); assert.equal(report.ok, true); assert.deepEqual(await backend.resolve({}), { memories: [], semanticMemories: [], behaviorSummary: null, activeGoals: [], }); }); test('qdrant backend reports client_not_configured when configured without client', () => { const backend = createQdrantMemoryBackend({ enabled: true, url: 'https://qdrant.local:6333', collection: 'memind_memory', }); assert.equal(backend.isAvailable(), false); assert.equal(backend.url, 'https://qdrant.local:6333'); assert.equal(backend.collection, 'memind_memory'); assert.equal(backend.getUnavailableReason(), 'client_not_configured'); }); test('qdrant backend validates url and collection name', () => { assert.throws( () => createQdrantMemoryBackend({ enabled: true, url: 'ftp://qdrant.local' }), /Unsupported Qdrant URL protocol/, ); assert.throws( () => createQdrantMemoryBackend({ collection: 'bad collection name' }), /Invalid Qdrant collection name/, ); }); test('qdrant backend requires client and embedding function to become available', async () => { const backend = createQdrantMemoryBackend({ enabled: true, url: 'http://127.0.0.1:6333', collection: 'memind_memory', client: { async search() { return []; }, }, async embedQuery() { return [0.1, 0.2, 0.3]; }, }); assert.equal(backend.isAvailable(), true); assert.equal(backend.getUnavailableReason(), null); assert.deepEqual(await backend.resolve({ userId: 'u1', query: 'hello' }), { memories: [], semanticMemories: [], behaviorSummary: null, activeGoals: [], }); }); test('qdrant backend resolves memories through read-only client search', async () => { const calls = []; const backend = createQdrantMemoryBackend({ enabled: true, url: 'http://127.0.0.1:6333', collection: 'memind_memory', client: { async search(input) { calls.push(input); return [ { id: 'p1', score: 0.9, payload: { content: 'qdrant semantic memory', type: 'fact', created_at: '2026-07-03T00:00:00.000Z', }, }, ]; }, }, async embedQuery(query) { assert.equal(query, 'memory-chain'); return [0.1, 0.2, 0.3]; }, }); const result = await backend.resolve({ userId: 'u1', query: 'memory-chain', limit: 5 }); assert.equal(result.memories[0].text, 'qdrant semantic memory'); assert.deepEqual(result.semanticMemories, ['qdrant semantic memory']); assert.deepEqual(calls, [{ collection: 'memind_memory', vector: [0.1, 0.2, 0.3], userId: 'u1', limit: 5, }]); }); test('createQdrantHttpClient sends read-only search request with api key and filter', async () => { const calls = []; const client = createQdrantHttpClient({ url: 'http://127.0.0.1:6333/', apiKey: 'secret', timeoutMs: 1000, async fetchImpl(url, options) { calls.push({ url, options }); return { ok: true, async json() { return { result: [{ id: 'p1', score: 0.8, payload: { content: 'hello' }, }], }; }, }; }, }); const result = await client.search({ collection: 'memind_memory', vector: [1, 0, 0], userId: 'u1', limit: 3, }); assert.equal(result.length, 1); assert.equal(calls[0].url, 'http://127.0.0.1:6333/collections/memind_memory/points/search'); assert.equal(calls[0].options.method, 'POST'); assert.equal(calls[0].options.headers['api-key'], 'secret'); assert.deepEqual(JSON.parse(calls[0].options.body), { vector: [1, 0, 0], limit: 3, with_payload: true, filter: { must: [{ key: 'user_id', match: { value: 'u1' }, }], }, }); }); test('createQdrantHttpClient turns non-ok responses into errors', async () => { const client = createQdrantHttpClient({ url: 'http://127.0.0.1:6333', async fetchImpl() { return { ok: false, status: 500 }; }, }); await assert.rejects( () => client.search({ collection: 'memind_memory', vector: [1, 0, 0] }), /Qdrant request failed: 500/, ); });