Skip to main content
Glama
orneryd

M.I.M.I.R - Multi-agent Intelligent Memory & Insight Repository

by orneryd
persistence.test.ts9.26 kB
/** * @fileoverview Unit tests for Neo4j persistence operations * * Tests the orchestration execution tracking persistence layer, * including task execution persistence, execution node management, * and progress tracking with mocked Neo4j operations. * * @since 1.0.0 */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { persistTaskExecutionToNeo4j, createExecutionNodeInNeo4j, updateExecutionNodeProgress, updateExecutionNodeInNeo4j, } from './persistence.js'; import type { IGraphManager } from '../../types/index.js'; import type { ExecutionResult, TaskDefinition } from '../../orchestrator/task-executor.js'; describe('Orchestration Persistence', () => { let mockGraphManager: IGraphManager; let mockSession: any; let mockDriver: any; beforeEach(() => { // Create mock session mockSession = { run: vi.fn().mockResolvedValue({ records: [], }), close: vi.fn().mockResolvedValue(undefined), }; // Create mock driver mockDriver = { session: vi.fn().mockReturnValue(mockSession), }; // Create mock graph manager mockGraphManager = { getDriver: vi.fn().mockReturnValue(mockDriver), } as any; }); describe('persistTaskExecutionToNeo4j', () => { it('should persist successful task execution with QC data', async () => { const executionId = 'exec-1234567890'; const taskId = 'task-1'; const result: ExecutionResult = { taskId: 'task-1', status: 'success', output: '✅ Task completed successfully', duration: 5000, preamblePath: '/path/to/preamble.md', tokens: { input: 1000, output: 500 }, toolCalls: 3, qcVerification: { passed: true, score: 95, feedback: 'All checks passed', issues: [], requiredFixes: [], }, }; const task: TaskDefinition = { id: 'task-1', title: 'Test Task', agentRoleDescription: 'Test Worker', recommendedModel: 'gpt-4', prompt: 'Do test', dependencies: [], estimatedDuration: '5 min', qcRole: 'Test QC', maxRetries: 2, }; const nodeId = await persistTaskExecutionToNeo4j( mockGraphManager, executionId, taskId, result, task ); expect(nodeId).toBe('exec-1234567890-task-1'); expect(mockSession.run).toHaveBeenCalledOnce(); expect(mockSession.close).toHaveBeenCalledOnce(); }); it('should persist failed task with error details', async () => { const result: ExecutionResult = { taskId: 'task-2', status: 'failure', output: '', error: 'API timeout after 30s', duration: 30000, preamblePath: '/path/to/preamble.md', tokens: { input: 800, output: 50 }, toolCalls: 1, }; const task: TaskDefinition = { id: 'task-2', title: 'Failed Task', agentRoleDescription: 'Test Worker', recommendedModel: 'gpt-4', prompt: 'Do test', dependencies: [], estimatedDuration: '5 min', maxRetries: 2, }; await persistTaskExecutionToNeo4j( mockGraphManager, 'exec-123', 'task-2', result, task ); const callArgs = mockSession.run.mock.calls[0][1]; expect(callArgs.status).toBe('failure'); expect(callArgs.error).toBe('API timeout after 30s'); }); it('should handle session errors gracefully', async () => { mockSession.run.mockRejectedValueOnce(new Error('Neo4j connection failed')); const result: ExecutionResult = { taskId: 'task-1', status: 'success', output: 'Test', duration: 1000, preamblePath: '/test.md', }; const task: TaskDefinition = { id: 'task-1', title: 'Test', agentRoleDescription: 'Worker', recommendedModel: 'gpt-4', prompt: 'Test', dependencies: [], estimatedDuration: '1 min', maxRetries: 2, }; await expect( persistTaskExecutionToNeo4j(mockGraphManager, 'exec-1', 'task-1', result, task) ).rejects.toThrow('Neo4j connection failed'); expect(mockSession.close).toHaveBeenCalled(); }); }); describe('createExecutionNodeInNeo4j', () => { it('should create execution node with initial values', async () => { const executionId = 'exec-1234567890'; const planId = 'plan-1234567890'; const totalTasks = 5; const startTime = Date.now(); await createExecutionNodeInNeo4j( mockGraphManager, executionId, planId, totalTasks, startTime ); expect(mockSession.run).toHaveBeenCalledOnce(); const callArgs = mockSession.run.mock.calls[0][1]; expect(callArgs.executionId).toBe(executionId); expect(callArgs.planId).toBe(planId); expect(callArgs.tasksTotal.toNumber()).toBe(5); }); it('should throw error on session failure', async () => { mockSession.run.mockRejectedValueOnce(new Error('Database error')); await expect( createExecutionNodeInNeo4j(mockGraphManager, 'exec-1', 'plan-1', 3, Date.now()) ).rejects.toThrow('Database error'); }); }); describe('updateExecutionNodeProgress', () => { it('should update progress after successful task', async () => { const result: ExecutionResult = { taskId: 'task-1', status: 'success', output: 'Done', duration: 5000, preamblePath: '/test.md', tokens: { input: 1000, output: 500 }, toolCalls: 3, }; await updateExecutionNodeProgress( mockGraphManager, 'exec-123', result, 0, // tasksFailed 1 // tasksSuccessful ); const callArgs = mockSession.run.mock.calls[0][1]; expect(callArgs.status).toBe('running'); expect(callArgs.successful.toNumber()).toBe(1); expect(callArgs.failed.toNumber()).toBe(0); }); it('should mark execution as failed when task fails', async () => { const result: ExecutionResult = { taskId: 'task-2', status: 'failure', output: '', error: 'Failed', duration: 1000, preamblePath: '/test.md', }; await updateExecutionNodeProgress( mockGraphManager, 'exec-123', result, 1, // tasksFailed 1 // tasksSuccessful ); const callArgs = mockSession.run.mock.calls[0][1]; expect(callArgs.status).toBe('failed'); }); it('should not throw on session errors', async () => { mockSession.run.mockRejectedValueOnce(new Error('Update failed')); const result: ExecutionResult = { taskId: 'task-1', status: 'success', output: 'Done', duration: 1000, preamblePath: '/test.md', }; // Should not throw await expect( updateExecutionNodeProgress(mockGraphManager, 'exec-1', result, 0, 1) ).resolves.toBeUndefined(); }); }); describe('updateExecutionNodeInNeo4j', () => { it('should finalize execution with completed status', async () => { const results: ExecutionResult[] = [ { taskId: 'task-1', status: 'success', output: 'Done', duration: 5000, preamblePath: '/test.md', }, { taskId: 'task-2', status: 'success', output: 'Done', duration: 3000, preamblePath: '/test.md', }, ]; await updateExecutionNodeInNeo4j( mockGraphManager, 'exec-123', results, Date.now(), false ); const callArgs = mockSession.run.mock.calls[0][1]; expect(callArgs.status).toBe('completed'); }); it('should finalize execution with failed status when tasks failed', async () => { const results: ExecutionResult[] = [ { taskId: 'task-1', status: 'success', output: 'Done', duration: 5000, preamblePath: '/test.md', }, { taskId: 'task-2', status: 'failure', output: '', error: 'Failed', duration: 1000, preamblePath: '/test.md', }, ]; await updateExecutionNodeInNeo4j( mockGraphManager, 'exec-123', results, Date.now(), false ); const callArgs = mockSession.run.mock.calls[0][1]; expect(callArgs.status).toBe('failed'); }); it('should finalize execution with cancelled status', async () => { const results: ExecutionResult[] = [ { taskId: 'task-1', status: 'success', output: 'Done', duration: 5000, preamblePath: '/test.md', }, ]; await updateExecutionNodeInNeo4j( mockGraphManager, 'exec-123', results, Date.now(), true // cancelled ); const callArgs = mockSession.run.mock.calls[0][1]; expect(callArgs.status).toBe('cancelled'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/orneryd/Mimir'

If you have feedback or need assistance with the MCP directory API, please join our Discord server