import { describe, expect, it, vi } from 'vitest'; import { createOperatorMemoryPlugin } from './operator-memory-plugin.js'; import type { Insight, InsightSearchResult, NewInsight } from './types.js'; function adapter() { return { name: 'test', embedder: null, storeInsight: vi.fn( async (value: NewInsight): Promise => ({ ...value, id: '1', createdAt: new Date(), }), ), searchInsights: vi.fn(async (): Promise => []), getInsight: vi.fn(), deleteInsight: vi.fn(), getPreference: vi.fn(), setPreference: vi.fn(), deletePreference: vi.fn(), listPreferences: vi.fn(), close: vi.fn(), }; } describe('OperatorMemoryPlugin', () => { it('isolates configured namespace storage across server-derived tenant, owner, and session scopes', async () => { const memory = adapter(); const plugin = createOperatorMemoryPlugin({ adapter: memory, instanceId: 'Nova', namespace: 'operator-memory', redact: (value) => value, }); await plugin.capture( { tenantId: 'tenant-a', ownerId: 'owner-a', sessionId: 'session-a' }, { content: 'one', source: 'test', category: 'note' }, ); await plugin.capture( { tenantId: 'tenant-b', ownerId: 'owner-a', sessionId: 'session-a' }, { content: 'two', source: 'test', category: 'note' }, ); await plugin.capture( { tenantId: 'tenant-a', ownerId: 'owner-a', sessionId: 'session-b' }, { content: 'three', source: 'test', category: 'note' }, ); expect( memory.storeInsight.mock.calls.map((call: unknown[]) => (call[0] as NewInsight).userId), ).toEqual([ '["operator-memory","tenant-a","owner-a","session-a"]', '["operator-memory","tenant-b","owner-a","session-a"]', '["operator-memory","tenant-a","owner-a","session-b"]', ]); }); it('rejects an incomplete runtime scope before it can produce a shared storage key', async () => { const memory = adapter(); const plugin = createOperatorMemoryPlugin({ adapter: memory, instanceId: 'Nova', namespace: 'operator-memory', redact: (value) => value, }); await expect( plugin.capture( { tenantId: 'tenant-a', ownerId: 'owner-a', sessionId: ' ' }, { content: 'note', source: 'test', category: 'note' }, ), ).rejects.toThrow('Operator memory session ID is required'); expect(memory.storeInsight).not.toHaveBeenCalled(); }); it('uses a differently named configured instance in retrieval provenance', async () => { const memory = adapter(); memory.searchInsights.mockResolvedValue([ { id: '1', content: 'x', score: 1, metadata: { source: 'project' } }, ]); const plugin = createOperatorMemoryPlugin({ adapter: memory, instanceId: 'Nova', namespace: 'operator-memory', redact: (value) => value, }); const results = await plugin.search({ tenantId: 't', ownerId: 'o', sessionId: 's' }, 'x'); expect(results[0]?.provenance).toEqual({ instanceId: 'Nova', namespace: 'operator-memory', source: 'project', }); }); it('orders startup context with project and flat-file truth before retrieved material', async () => { const memory = adapter(); memory.searchInsights.mockResolvedValue([ { id: 'retrieval', content: 'retrieval', score: 1 }, { id: 'flat-file', content: 'flat-file', score: 1, metadata: { source: 'flat-file' } }, { id: 'project', content: 'project', score: 1, metadata: { source: 'project' } }, ]); const plugin = createOperatorMemoryPlugin({ adapter: memory, instanceId: 'Nova', namespace: 'operator-memory', maxStartupContext: 2, redact: (value) => value, }); const context = await plugin.startupContext({ tenantId: 'tenant-a', ownerId: 'owner-a', sessionId: 'session-a', }); expect(context.map((result) => result.id)).toEqual(['project', 'flat-file']); expect(memory.searchInsights).toHaveBeenCalledWith(expect.any(String), '*', { limit: 64 }); }); it('redacts content before adapter persistence and records configured provenance metadata', async () => { const memory = adapter(); const plugin = createOperatorMemoryPlugin({ adapter: memory, instanceId: 'Nova', namespace: 'operator-memory', redact: (value) => value.replace('secret', '[REDACTED]'), }); await plugin.capture( { tenantId: 'tenant-a', ownerId: 'owner-a', sessionId: 'session-a' }, { content: 'secret note', source: 'project', category: 'note' }, ); expect(memory.storeInsight).toHaveBeenCalledWith( expect.objectContaining({ content: '[REDACTED] note', metadata: { instanceId: 'Nova', namespace: 'operator-memory', source: 'project', }, }), ); }); });