From a3b181b3d8f035da5a47aa656fef25d0a1420ab9 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:31:44 +0000 Subject: [PATCH] test: add unit tests for pinMemory and fix dependency injection in getRelevantMemories - Add `test/memory-pinning.test.mjs` to test `pinMemory` happy path and edge cases. - Update `getRelevantMemories` in `src/services/memory/index.js` to use `repository.listMemories` when available, enabling isolated unit testing. - Verified all memory tests pass. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- src/services/memory/index.js | 6 ++- test/memory-pinning.test.mjs | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 test/memory-pinning.test.mjs diff --git a/src/services/memory/index.js b/src/services/memory/index.js index 494dc1d..41d8d6a 100644 --- a/src/services/memory/index.js +++ b/src/services/memory/index.js @@ -214,7 +214,11 @@ export async function getRelevantMemories(params) { return cached; } - const ranked = await filterAndRankMemories([...memoryStore.values()], queryParams); + const source = typeof repository.listMemories === 'function' + ? await repository.listMemories(queryParams) + : [...memoryStore.values()]; + + const ranked = await filterAndRankMemories(source, queryParams); await updateMemoryUsage(repository, ranked.map((memory) => memory.id)); cache.set(cacheKey, ranked); diff --git a/test/memory-pinning.test.mjs b/test/memory-pinning.test.mjs new file mode 100644 index 0000000..2c6ae90 --- /dev/null +++ b/test/memory-pinning.test.mjs @@ -0,0 +1,72 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { getRelevantMemories, pinMemory } from '../src/services/memory/index.js'; + +test('pinMemory pins the memory and clears the cache', async () => { + const records = [ + { id: 'm1', agent_id: 'agent-a', type: 'event', tags: ['ops'], pinned: false, last_seen: 10, merged_into: null }, + ]; + + let listMemoriesCallCount = 0; + const repository = { + async listMemories() { + listMemoriesCallCount++; + return records; + }, + async getMemoryById(id) { + return records.find((record) => record.id === id) ?? null; + }, + async setPinned(id, pinned) { + const index = records.findIndex((record) => record.id === id); + if (index < 0) return null; + records[index] = { ...records[index], pinned }; + return records[index]; + }, + async updateMemoryUsage() { return null; } + }; + + // 1. Populate cache + await getRelevantMemories({ agent_id: 'agent-a', repository }); + assert.equal(listMemoriesCallCount, 1, 'Repository should be queried initially'); + + // 2. Verify cache hit + await getRelevantMemories({ agent_id: 'agent-a', repository }); + assert.equal(listMemoriesCallCount, 1, 'Repository should NOT be queried on cache hit'); + + // 3. Pin memory + const pinned = await pinMemory('m1', { repository }); + assert.ok(pinned); + assert.equal(pinned.pinned, true); + assert.equal(records[0].pinned, true); + + // 4. Verify cache miss (cleared) + await getRelevantMemories({ agent_id: 'agent-a', repository }); + assert.equal(listMemoriesCallCount, 2, 'Repository should be queried again after cache clear'); +}); + +test('pinMemory returns null for non-existent ID but still clears cache', async () => { + const records = []; + let listMemoriesCallCount = 0; + const repository = { + async listMemories() { + listMemoriesCallCount++; + return records; + }, + async setPinned(id, pinned) { + return null; + }, + async updateMemoryUsage() { return null; } + }; + + // 1. Populate cache (use different agent_id to avoid cache collision with previous test) + await getRelevantMemories({ agent_id: 'agent-b', repository }); + assert.equal(listMemoriesCallCount, 1); + + // 2. Pin non-existent memory + const result = await pinMemory('non-existent', { repository }); + assert.equal(result, null); + + // 3. Verify cache cleared + await getRelevantMemories({ agent_id: 'agent-b', repository }); + assert.equal(listMemoriesCallCount, 2, 'Cache should be cleared even if ID not found'); +});