UNPKG

meld

Version:

Meld: A template language for LLM prompts

287 lines (243 loc) 9.48 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { StateDebuggerService } from './StateDebuggerService'; import { IStateVisualizationService } from '../StateVisualizationService/IStateVisualizationService'; import { IStateHistoryService } from '../StateHistoryService/IStateHistoryService'; import { IStateTrackingService } from '../StateTrackingService/IStateTrackingService'; import { StateDiagnostic, DebugSessionConfig } from './IStateDebuggerService'; describe('StateDebuggerService', () => { // Mock data const mockMetadata = { type: 'test', childStates: ['child1', 'child2'], lastModified: Date.now(), parentState: null, variables: {}, source: 'test' }; const mockHistory = { transformations: [{ type: 'test', timestamp: Date.now() }], operations: [] }; // Mock services const mockVisualizationService = { exportStateGraph: vi.fn().mockReturnValue('graph-data'), generateHierarchyView: vi.fn(), generateTransitionDiagram: vi.fn(), generateRelationshipGraph: vi.fn(), generateTimeline: vi.fn(), getMetrics: vi.fn(), } as unknown as IStateVisualizationService; const mockHistoryService = { getStateHistory: vi.fn().mockResolvedValue(mockHistory), } as unknown as IStateHistoryService; const mockTrackingService = { getStateMetadata: vi.fn().mockResolvedValue(mockMetadata), } as unknown as IStateTrackingService; let debugService: StateDebuggerService; let testSessionId: string; const testStateId = 'test-state-123'; beforeEach(() => { vi.clearAllMocks(); mockHistoryService.getStateHistory.mockResolvedValue(mockHistory); mockTrackingService.getStateMetadata.mockResolvedValue(mockMetadata); debugService = new StateDebuggerService( mockVisualizationService, mockHistoryService, mockTrackingService ); }); describe('Session Management', () => { it('should create a new debug session', () => { const config: DebugSessionConfig = { captureConfig: { capturePoints: ['pre-transform', 'post-transform'], includeFields: ['nodes'], format: 'full' } }; const sessionId = debugService.startSession(config); expect(sessionId).toBeDefined(); expect(typeof sessionId).toBe('string'); }); it('should end a debug session and return results', async () => { const config: DebugSessionConfig = { captureConfig: { capturePoints: ['pre-transform'], includeFields: ['nodes'], format: 'full' }, visualization: { format: 'mermaid' } }; const sessionId = debugService.startSession(config); const result = await debugService.endSession(sessionId); expect(result).toMatchObject({ sessionId, startTime: expect.any(Number), endTime: expect.any(Number), diagnostics: expect.any(Array), snapshots: expect.any(Map), }); expect(mockVisualizationService.exportStateGraph).toHaveBeenCalledWith( config.visualization ); }); it('should throw error when ending non-existent session', async () => { await expect(debugService.endSession('invalid-id')) .rejects .toThrow('No debug session found'); }); }); describe('State Analysis', () => { it('should analyze state and return diagnostics', async () => { // Setup mock to trigger warnings mockHistoryService.getStateHistory.mockResolvedValueOnce({ transformations: Array(11).fill({ type: 'test' }), operations: [] }); mockTrackingService.getStateMetadata.mockResolvedValueOnce({ type: 'test', childStates: Array(21).fill('child'), lastModified: Date.now(), parentState: null, variables: {} }); const diagnostics = await debugService.analyzeState(testStateId); expect(diagnostics).toHaveLength(2); // Two warnings expect(diagnostics.at(0).type).toBe('warning'); expect(diagnostics.at(0).message).toContain('transformations'); expect(diagnostics.at(1).type).toBe('warning'); expect(diagnostics.at(1).message).toContain('child states'); }); it('should run custom analyzers during analysis', async () => { const customAnalyzer = vi.fn().mockResolvedValue([{ stateId: testStateId, timestamp: Date.now(), type: 'info', message: 'Custom analysis' }]); debugService.registerAnalyzer(customAnalyzer); const diagnostics = await debugService.analyzeState(testStateId); expect(customAnalyzer).toHaveBeenCalledWith(testStateId); expect(diagnostics).toContainEqual(expect.objectContaining({ message: 'Custom analysis' })); }); it('should handle missing state data', async () => { mockHistoryService.getStateHistory.mockResolvedValueOnce(null); mockTrackingService.getStateMetadata.mockResolvedValueOnce(null); const diagnostics = await debugService.analyzeState(testStateId); expect(diagnostics).toHaveLength(1); expect(diagnostics.at(0).type).toBe('error'); expect(diagnostics.at(0).message).toContain('Failed to retrieve state'); }); }); describe('Operation Tracing', () => { it('should trace successful operations', async () => { const operation = vi.fn().mockResolvedValue('success'); const { result, diagnostics } = await debugService.traceOperation( testStateId, operation ); expect(result).toBe('success'); expect(diagnostics).toEqual(expect.any(Array)); expect(mockTrackingService.getStateMetadata).toHaveBeenCalled(); expect(mockHistoryService.getStateHistory).toHaveBeenCalled(); }); it('should handle failed operations', async () => { const error = new Error('Operation failed'); const operation = vi.fn().mockRejectedValue(error); try { await debugService.traceOperation(testStateId, operation); fail('Expected operation to throw'); } catch (e: any) { expect(e).toMatchObject({ error, diagnostics: expect.arrayContaining([ expect.objectContaining({ type: 'error', message: 'Operation failed' }) ]) }); } }); it('should handle missing state data during tracing', async () => { mockHistoryService.getStateHistory.mockResolvedValue(null); mockTrackingService.getStateMetadata.mockResolvedValue(null); const operation = vi.fn().mockResolvedValue('success'); try { await debugService.traceOperation(testStateId, operation); fail('Expected operation to throw'); } catch (e: any) { expect(e.error.message).toContain('Failed to retrieve state data'); } }); }); describe('State Snapshots', () => { it('should get full state snapshot', async () => { const snapshot = await debugService.getStateSnapshot(testStateId, 'full'); expect(snapshot).toMatchObject({ metadata: mockMetadata, history: mockHistory, children: expect.any(Array) }); expect(snapshot.children).toHaveLength(mockMetadata.childStates.length); }); it('should get summary state snapshot', async () => { const snapshot = await debugService.getStateSnapshot(testStateId, 'summary'); expect(snapshot).toMatchObject({ id: testStateId, type: mockMetadata.source, childCount: mockMetadata.childStates.length, transformationCount: mockHistory.transformations.length, lastModified: expect.any(Number) }); }); it('should handle missing state data in snapshots', async () => { mockHistoryService.getStateHistory.mockResolvedValue(null); mockTrackingService.getStateMetadata.mockResolvedValue(null); await expect(debugService.getStateSnapshot(testStateId, 'full')) .rejects .toThrow('Failed to retrieve state data'); }); }); describe('Debug Reports', () => { it('should generate debug report', async () => { const config: DebugSessionConfig = { captureConfig: { capturePoints: ['pre-transform'], includeFields: ['nodes'], format: 'full' } }; const sessionId = debugService.startSession(config); const report = await debugService.generateDebugReport(sessionId); expect(report).toContain('Debug Session Report'); expect(report).toContain('Duration:'); expect(report).toContain('Diagnostics:'); expect(report).toContain('Metrics:'); expect(report).toContain('Snapshots:'); }); it('should throw error for invalid session', async () => { await expect(debugService.generateDebugReport('invalid-id')) .rejects .toThrow('No debug session found'); }); }); describe('Session Cleanup', () => { it('should clear session data', async () => { const config: DebugSessionConfig = { captureConfig: { capturePoints: ['pre-transform'], includeFields: ['nodes'], format: 'full' } }; const sessionId = debugService.startSession(config); debugService.clearSession(sessionId); await expect(debugService.generateDebugReport(sessionId)) .rejects .toThrow('No debug session found'); }); }); });