agenthub/tests/memoryService.test.ts
chahinebrini caf60ea939 fix(memory): FTS5-safe search-term quoting — "TSK-0037"-style queries no longer crash (TSK-0043)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 01:52:57 +02:00

86 lines
3.2 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, rmSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { addMemory, searchMemory, listMemory } from '../src/core/services/memoryService.js';
import { Index } from '../src/core/index.js';
describe('memoryService', () => {
let cwd: string;
beforeEach(() => { cwd = mkdtempSync(join(tmpdir(), 'ah-mem-')); });
afterEach(() => { rmSync(cwd, { recursive: true, force: true }); });
it('creates and searches memory', () => {
const m = addMemory(cwd, { title: 'DNS cache', category: 'technical', content: 'Use TTL' });
expect(m.id.startsWith('MEM-')).toBe(true);
expect(searchMemory(cwd, 'TTL')).toHaveLength(1);
expect(listMemory(cwd)).toHaveLength(1);
});
it('searches literal task IDs and other FTS5-special strings without throwing', () => {
addMemory(cwd, {
title: 'TSK-0037 result',
category: 'implementation',
content: 'Linked HOF-0030 and a:b plus foo-bar all belong to this note.',
});
expect(searchMemory(cwd, 'TSK-0037')).toHaveLength(1);
expect(searchMemory(cwd, 'HOF-0030')).toHaveLength(1);
expect(searchMemory(cwd, 'a:b')).toHaveLength(1);
expect(searchMemory(cwd, 'foo-bar')).toHaveLength(1);
expect(searchMemory(cwd, 'TTL')).toHaveLength(0);
});
it('keeps normal word search functional after FTS5 quoting', () => {
addMemory(cwd, { title: 'DNS cache', category: 'technical', content: 'Use TTL' });
addMemory(cwd, { title: 'Other note', category: 'technical', content: 'No cache keyword here' });
const results = searchMemory(cwd, 'TTL');
expect(results).toHaveLength(1);
expect(results[0].title).toBe('DNS cache');
});
it('stores relatedTasks in the memory object', () => {
const m = addMemory(cwd, {
title: 'TTL lesson',
category: 'lesson',
content: 'Use short TTL',
relatedTasks: ['TSK-0001', 'TSK-0002'],
});
expect(m.relatedTasks).toEqual(['TSK-0001', 'TSK-0002']);
});
it('stores optional tokens, duration, by fields', () => {
const m = addMemory(cwd, {
title: 'Perf stats',
category: 'implementation',
content: 'p99=50ms',
tokens: 1500,
duration: 30_000,
by: 'kimi',
});
expect(m.tokens).toBe(1500);
expect(m.duration).toBe(30_000);
expect(m.by).toBe('kimi');
});
it('listMemoryByTask returns entries linked to a given task', () => {
addMemory(cwd, { title: 'Related', category: 'technical', content: 'x', relatedTasks: ['TSK-0042'] });
addMemory(cwd, { title: 'Unrelated', category: 'technical', content: 'y', relatedTasks: ['TSK-0099'] });
const index = new Index(cwd);
const results = index.listMemoryByTask('TSK-0042');
index.close();
expect(results).toHaveLength(1);
expect(results[0].title).toBe('Related');
});
it('listMemoryByTask returns empty when no memory is linked', () => {
addMemory(cwd, { title: 'Other', category: 'technical', content: 'y', relatedTasks: ['TSK-0099'] });
const index = new Index(cwd);
const results = index.listMemoryByTask('TSK-0001');
index.close();
expect(results).toHaveLength(0);
});
});