refactor(store): split gatewayStore into specialized domain stores
Major restructuring: - Split monolithic gatewayStore into 5 focused stores: - connectionStore: WebSocket connection and gateway lifecycle - configStore: quickConfig, workspaceInfo, MCP services - agentStore: clones, usage stats, agent management - handStore: hands, approvals, triggers, hand runs - workflowStore: workflows, workflow runs, execution - Update all components to use new stores with selector pattern - Remove
This commit is contained in:
299
docs/archive/v1-viking-dead-code/tests/vector-memory.test.ts
Normal file
299
docs/archive/v1-viking-dead-code/tests/vector-memory.test.ts
Normal file
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* Vector Memory Tests - Phase 4.2 Semantic Search
|
||||
*
|
||||
* Tests for vector-based semantic memory search:
|
||||
* - VectorMemoryService initialization
|
||||
* - Semantic search with OpenViking
|
||||
* - Similar memory finding
|
||||
* - Clustering functionality
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import {
|
||||
VectorMemoryService,
|
||||
getVectorMemory,
|
||||
resetVectorMemory,
|
||||
semanticSearch,
|
||||
findSimilarMemories,
|
||||
isVectorSearchAvailable,
|
||||
DEFAULT_VECTOR_CONFIG,
|
||||
type VectorSearchOptions,
|
||||
type VectorSearchResult,
|
||||
} from '../../desktop/src/lib/vector-memory';
|
||||
import { getVikingClient, resetVikingClient } from '../../desktop/src/lib/viking-client';
|
||||
import { getMemoryManager, resetMemoryManager } from '../../desktop/src/lib/agent-memory';
|
||||
|
||||
// === Mock Dependencies ===
|
||||
|
||||
const mockVikingClient = {
|
||||
isAvailable: vi.fn(async () => true),
|
||||
find: vi.fn(async () => [
|
||||
{ uri: 'memories/agent1/memory1', content: '用户偏好简洁的回答', score: 0.9, metadata: { tags: ['preference'] } },
|
||||
{ uri: 'memories/agent1/memory2', content: '项目使用 TypeScript', score: 0.7, metadata: { tags: ['fact'] } },
|
||||
{ uri: 'memories/agent1/memory3', content: '需要完成性能测试', score: 0.5, metadata: { tags: ['task'] } },
|
||||
]),
|
||||
addResource: vi.fn(async () => ({ uri: 'test', status: 'ok' })),
|
||||
removeResource: vi.fn(async () => undefined),
|
||||
};
|
||||
|
||||
vi.mock('../../desktop/src/lib/viking-client', () => ({
|
||||
getVikingClient: vi.fn(() => mockVikingClient),
|
||||
resetVikingClient: vi.fn(),
|
||||
VikingHttpClient: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockMemoryManager = {
|
||||
getByAgent: vi.fn(() => [
|
||||
{ id: 'memory1', agentId: 'agent1', content: '用户偏好简洁的回答', type: 'preference', importance: 7, createdAt: new Date().toISOString(), source: 'auto', tags: ['style'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
|
||||
{ id: 'memory2', agentId: 'agent1', content: '项目使用 TypeScript', type: 'fact', importance: 6, createdAt: new Date().toISOString(), source: 'auto', tags: ['tech'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
|
||||
{ id: 'memory3', agentId: 'agent1', content: '需要完成性能测试', type: 'task', importance: 8, createdAt: new Date().toISOString(), source: 'auto', tags: ['todo'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
|
||||
]),
|
||||
getAll: vi.fn(async () => [
|
||||
{ id: 'memory1', agentId: 'agent1', content: '用户偏好简洁的回答', type: 'preference', importance: 7, createdAt: new Date().toISOString(), source: 'auto', tags: ['style'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
|
||||
{ id: 'memory2', agentId: 'agent1', content: '项目使用 TypeScript', type: 'fact', importance: 6, createdAt: new Date().toISOString(), source: 'auto', tags: ['tech'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
|
||||
{ id: 'memory3', agentId: 'agent1', content: '需要完成性能测试', type: 'task', importance: 8, createdAt: new Date().toISOString(), source: 'auto', tags: ['todo'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
|
||||
]),
|
||||
save: vi.fn(async () => 'memory-id'),
|
||||
};
|
||||
|
||||
vi.mock('../../desktop/src/lib/agent-memory', () => ({
|
||||
getMemoryManager: vi.fn(() => mockMemoryManager),
|
||||
resetMemoryManager: vi.fn(),
|
||||
}));
|
||||
|
||||
// === VectorMemoryService Tests ===
|
||||
|
||||
describe('VectorMemoryService', () => {
|
||||
let service: VectorMemoryService;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
resetVectorMemory();
|
||||
resetVikingClient();
|
||||
service = new VectorMemoryService();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetVectorMemory();
|
||||
});
|
||||
|
||||
describe('Initialization', () => {
|
||||
it('should initialize with default config', () => {
|
||||
const config = service.getConfig();
|
||||
expect(config.enabled).toBe(true);
|
||||
expect(config.defaultTopK).toBe(10);
|
||||
expect(config.defaultMinScore).toBe(0.3);
|
||||
expect(config.defaultLevel).toBe('L1');
|
||||
});
|
||||
|
||||
it('should accept custom config', () => {
|
||||
const customService = new VectorMemoryService({
|
||||
defaultTopK: 20,
|
||||
defaultMinScore: 0.5,
|
||||
});
|
||||
const config = customService.getConfig();
|
||||
expect(config.defaultTopK).toBe(20);
|
||||
expect(config.defaultMinScore).toBe(0.5);
|
||||
});
|
||||
|
||||
it('should update config', () => {
|
||||
service.updateConfig({ defaultTopK: 15 });
|
||||
const config = service.getConfig();
|
||||
expect(config.defaultTopK).toBe(15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Semantic Search', () => {
|
||||
it('should perform semantic search', async () => {
|
||||
const results = await service.semanticSearch('用户偏好');
|
||||
|
||||
expect(mockVikingClient.find).toHaveBeenCalled();
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
expect(results[0].score).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
it('should respect topK option', async () => {
|
||||
await service.semanticSearch('测试', { topK: 5 });
|
||||
|
||||
expect(mockVikingClient.find).toHaveBeenCalledWith(
|
||||
'测试',
|
||||
expect.objectContaining({ limit: 5 })
|
||||
);
|
||||
});
|
||||
|
||||
it('should respect minScore option', async () => {
|
||||
await service.semanticSearch('测试', { minScore: 0.8 });
|
||||
|
||||
expect(mockVikingClient.find).toHaveBeenCalledWith(
|
||||
'测试',
|
||||
expect.objectContaining({ minScore: 0.8 })
|
||||
);
|
||||
});
|
||||
|
||||
it('should respect level option', async () => {
|
||||
await service.semanticSearch('测试', { level: 'L2' });
|
||||
|
||||
expect(mockVikingClient.find).toHaveBeenCalledWith(
|
||||
'测试',
|
||||
expect.objectContaining({ level: 'L2' })
|
||||
);
|
||||
});
|
||||
|
||||
it('should return empty array when disabled', async () => {
|
||||
service.updateConfig({ enabled: false });
|
||||
const results = await service.semanticSearch('测试');
|
||||
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
it('should filter by types when specified', async () => {
|
||||
const results = await service.semanticSearch('用户偏好', { types: ['preference'] });
|
||||
|
||||
// Should only return preference type memories
|
||||
for (const result of results) {
|
||||
expect(result.memory.type).toBe('preference');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Find Similar', () => {
|
||||
it('should find similar memories', async () => {
|
||||
const results = await service.findSimilar('memory1', { agentId: 'agent1' });
|
||||
|
||||
expect(mockMemoryManager.getAll).toHaveBeenCalledWith('agent1');
|
||||
expect(mockVikingClient.find).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return empty array for non-existent memory', async () => {
|
||||
mockMemoryManager.getAll.mockResolvedValueOnce([]);
|
||||
|
||||
const results = await service.findSimilar('non-existent', { agentId: 'agent1' });
|
||||
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Find By Concept', () => {
|
||||
it('should find memories by concept', async () => {
|
||||
const results = await service.findByConcept('代码优化');
|
||||
|
||||
expect(mockVikingClient.find).toHaveBeenCalledWith(
|
||||
'代码优化',
|
||||
expect.any(Object)
|
||||
);
|
||||
expect(results.length).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Clustering', () => {
|
||||
it('should cluster memories', async () => {
|
||||
const clusters = await service.clusterMemories('agent1', 3);
|
||||
|
||||
expect(mockMemoryManager.getAll).toHaveBeenCalledWith('agent1');
|
||||
expect(Array.isArray(clusters)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return empty array for agent with no memories', async () => {
|
||||
mockMemoryManager.getAll.mockResolvedValueOnce([]);
|
||||
|
||||
const clusters = await service.clusterMemories('empty-agent');
|
||||
|
||||
expect(clusters).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Availability', () => {
|
||||
it('should check availability', async () => {
|
||||
const available = await service.isAvailable();
|
||||
|
||||
expect(available).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when disabled', async () => {
|
||||
service.updateConfig({ enabled: false });
|
||||
const available = await service.isAvailable();
|
||||
|
||||
expect(available).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cache', () => {
|
||||
it('should clear cache', () => {
|
||||
service.clearCache();
|
||||
// No error means success
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// === Helper Function Tests ===
|
||||
|
||||
describe('Helper Functions', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
resetVectorMemory();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetVectorMemory();
|
||||
});
|
||||
|
||||
describe('getVectorMemory', () => {
|
||||
it('should return singleton instance', () => {
|
||||
const instance1 = getVectorMemory();
|
||||
const instance2 = getVectorMemory();
|
||||
|
||||
expect(instance1).toBe(instance2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('semanticSearch helper', () => {
|
||||
it('should call service.semanticSearch', async () => {
|
||||
const results = await semanticSearch('测试查询');
|
||||
|
||||
expect(mockVikingClient.find).toHaveBeenCalled();
|
||||
expect(Array.isArray(results)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findSimilarMemories helper', () => {
|
||||
it('should call service.findSimilar', async () => {
|
||||
const results = await findSimilarMemories('memory1', 'agent1');
|
||||
|
||||
expect(mockMemoryManager.getAll).toHaveBeenCalled();
|
||||
expect(Array.isArray(results)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isVectorSearchAvailable helper', () => {
|
||||
it('should call service.isAvailable', async () => {
|
||||
const available = await isVectorSearchAvailable();
|
||||
|
||||
expect(typeof available).toBe('boolean');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// === Integration Tests ===
|
||||
|
||||
describe('VectorMemoryService Integration', () => {
|
||||
it('should handle Viking client errors gracefully', async () => {
|
||||
mockVikingClient.find.mockRejectedValueOnce(new Error('Connection failed'));
|
||||
|
||||
const service = new VectorMemoryService();
|
||||
const results = await service.semanticSearch('测试');
|
||||
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle missing Viking client gracefully', async () => {
|
||||
vi.mocked(getVikingClient).mockImplementation(() => {
|
||||
throw new Error('Viking not available');
|
||||
});
|
||||
|
||||
const service = new VectorMemoryService();
|
||||
const results = await service.semanticSearch('测试');
|
||||
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user