UNPKG

meld

Version:

Meld: A template language for LLM prompts

326 lines (264 loc) 12.2 kB
import { describe, it, expect, beforeEach, vi, Mock, afterEach } from 'vitest'; import { TestVisualizationManager, TestOutputVerbosity } from './TestVisualizationManager'; import { StateVisualizationFileOutput } from './FileOutputService'; import { CompactStateVisualization } from './CompactStateVisualization'; import { IStateVisualizationService } from './IStateVisualizationService'; import { IStateHistoryService } from '../StateHistoryService/IStateHistoryService'; import { IStateTrackingService } from '../StateTrackingService/IStateTrackingService'; import fs from 'fs'; import path from 'path'; import { serviceLogger } from '@core/utils/logger'; // Mock dependencies vi.mock('fs'); vi.mock('path'); vi.mock('@core/utils/logger', () => ({ serviceLogger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn(), trace: vi.fn(), } })); describe('TestVisualizationManager', () => { let mockVisualizationService: IStateVisualizationService & { [K in keyof IStateVisualizationService]: Mock }; let mockHistoryService: IStateHistoryService & { [K in keyof IStateHistoryService]: Mock }; let mockTrackingService: IStateTrackingService & { [K in keyof IStateTrackingService]: Mock }; let testVisManager: TestVisualizationManager; const TEST_OUTPUT_DIR = './logs/test-visualization'; beforeEach(() => { // Mock path.join to return predictable paths vi.mocked(path.join).mockImplementation((...args) => args.join('/')); // Mock visualization service mockVisualizationService = { generateHierarchyView: vi.fn().mockReturnValue('mocked hierarchy view'), generateTransitionDiagram: vi.fn().mockReturnValue('mocked transition diagram'), generateRelationshipGraph: vi.fn().mockReturnValue('mocked relationship graph'), generateTimeline: vi.fn().mockReturnValue('mocked timeline'), getMetrics: vi.fn().mockReturnValue({ totalStates: 5, statesByType: { new: 2, clone: 1, merge: 2 }, averageTransformationsPerState: 2.5, maxTransformationChainLength: 4, averageChildrenPerState: 1.2, maxTreeDepth: 3, operationFrequency: { create: 5, transform: 10, merge: 2 } }), exportStateGraph: vi.fn().mockReturnValue('mocked state graph'), visualizeContextHierarchy: vi.fn().mockReturnValue('mocked context hierarchy'), visualizeVariablePropagation: vi.fn().mockReturnValue('mocked variable propagation'), visualizeContextsAndVariableFlow: vi.fn().mockReturnValue('mocked contexts and flow'), visualizeResolutionPathTimeline: vi.fn().mockReturnValue('mocked resolution path'), }; // Mock history service mockHistoryService = { recordOperation: vi.fn(), getOperationHistory: vi.fn().mockReturnValue([ { type: 'create', stateId: 'test-state', source: 'new', timestamp: 1000, metadata: { id: 'test-state', source: 'new', createdAt: 1000 } }, { type: 'transform', stateId: 'test-state', source: 'test', timestamp: 2000 } ]), getTransformationChain: vi.fn().mockReturnValue([ { stateId: 'test-state', timestamp: 1000, operation: 'update', source: 'test', before: { value: 1 }, after: { value: 2 } } ]), queryHistory: vi.fn(), getRelatedOperations: vi.fn(), clearHistoryBefore: vi.fn(), }; // Mock tracking service mockTrackingService = { registerState: vi.fn(), addRelationship: vi.fn(), getStateLineage: vi.fn().mockReturnValue(['parent-state', 'test-state']), getStateDescendants: vi.fn().mockReturnValue(['child-state']), getStateMetadata: vi.fn().mockReturnValue({ id: 'test-state', source: 'new', createdAt: 1000, transformationEnabled: true }), getVariableCrossings: vi.fn().mockReturnValue([]), getContextHierarchy: vi.fn().mockReturnValue({ states: [{ id: 'test-state', source: 'new', createdAt: 1000 }], boundaries: [], variableCrossings: [] }), getAllStates: vi.fn().mockReturnValue([ { id: 'test-state', source: 'new', createdAt: 1000 } ]), }; // Create test visualization manager testVisManager = new TestVisualizationManager( mockVisualizationService, mockHistoryService, mockTrackingService, { verbosity: TestOutputVerbosity.Standard, outputToFiles: false, outputDir: TEST_OUTPUT_DIR, defaultFormat: 'mermaid' } ); // Mock fs exists and mkdir vi.mocked(fs.existsSync).mockReturnValue(true); vi.mocked(fs.mkdirSync).mockImplementation(() => undefined); vi.mocked(fs.writeFileSync).mockImplementation(() => undefined); }); afterEach(() => { vi.clearAllMocks(); }); describe('initialization', () => { it('initializes with default options when none provided', () => { const defaultManager = new TestVisualizationManager( mockVisualizationService, mockHistoryService, mockTrackingService ); expect(defaultManager).toBeDefined(); expect(serviceLogger.debug).toHaveBeenCalledWith( 'Test visualization manager initialized', expect.objectContaining({ verbosity: TestOutputVerbosity.Standard }) ); }); it('respects environment variables for verbosity', () => { const originalEnv = process.env.TEST_LOG_LEVEL; try { process.env.TEST_LOG_LEVEL = 'debug'; const envManager = new TestVisualizationManager( mockVisualizationService, mockHistoryService, mockTrackingService ); expect(serviceLogger.debug).toHaveBeenCalledWith( 'Test visualization manager initialized', expect.objectContaining({ verbosity: TestOutputVerbosity.Debug }) ); } finally { process.env.TEST_LOG_LEVEL = originalEnv; } }); }); describe('visualizeState', () => { it('returns null for minimal verbosity', () => { testVisManager.setVerbosity(TestOutputVerbosity.Minimal); const result = testVisManager.visualizeState('test-state'); expect(result).toBeNull(); }); it('generates standard summary for standard verbosity', () => { testVisManager.setVerbosity(TestOutputVerbosity.Standard); const result = testVisManager.visualizeState('test-state'); expect(result).toContain('State test-state'); expect(mockHistoryService.getOperationHistory).toHaveBeenCalledWith('test-state'); }); it('generates detailed output for verbose verbosity', () => { testVisManager.setVerbosity(TestOutputVerbosity.Verbose); const result = testVisManager.visualizeState('test-state'); expect(result).toContain('State test-state'); expect(result).toContain('transforms'); expect(mockHistoryService.getTransformationChain).toHaveBeenCalledWith('test-state'); }); it('generates full debug visualization for debug verbosity', () => { testVisManager.setVerbosity(TestOutputVerbosity.Debug); const result = testVisManager.visualizeState('test-state'); expect(result).toContain('mocked hierarchy view'); expect(result).toContain('mocked transition diagram'); expect(mockVisualizationService.generateHierarchyView).toHaveBeenCalledWith( 'test-state', expect.objectContaining({ format: 'mermaid' }) ); }); it('writes to file when outputToFiles is true', () => { testVisManager.setVerbosity(TestOutputVerbosity.Standard); testVisManager.setOutputMode(true); // Setup fs mock vi.mocked(fs.writeFileSync).mockImplementation(() => undefined); const result = testVisManager.visualizeState('test-state', 'test-label'); expect(result).toMatch(/logs\/test-visualization\/state_test-state_test-label/); expect(fs.writeFileSync).toHaveBeenCalled(); }); }); describe('visualizeStates', () => { it('returns null for minimal verbosity', () => { testVisManager.setVerbosity(TestOutputVerbosity.Minimal); const result = testVisManager.visualizeStates(['test-state']); expect(result).toBeNull(); }); it('visualizes a single state directly', () => { testVisManager.setVerbosity(TestOutputVerbosity.Standard); const spyVisualize = vi.spyOn(testVisManager, 'visualizeState'); testVisManager.visualizeStates(['test-state']); expect(spyVisualize).toHaveBeenCalledWith('test-state', undefined); }); it('generates relationship graph for multiple states in high verbosity', () => { testVisManager.setVerbosity(TestOutputVerbosity.Debug); const result = testVisManager.visualizeStates(['state1', 'state2']); expect(mockVisualizationService.generateRelationshipGraph).toHaveBeenCalledWith( ['state1', 'state2'], expect.objectContaining({ format: 'mermaid' }) ); expect(result).toContain('mocked relationship graph'); }); }); describe('visualizeVariableResolution', () => { it('returns null for minimal verbosity', () => { testVisManager.setVerbosity(TestOutputVerbosity.Minimal); const result = testVisManager.visualizeVariableResolution('testVar'); expect(result).toBeNull(); }); it('generates variable propagation visualization', () => { testVisManager.setVerbosity(TestOutputVerbosity.Standard); const result = testVisManager.visualizeVariableResolution('testVar', 'root-state'); expect(mockVisualizationService.visualizeVariablePropagation).toHaveBeenCalledWith( 'testVar', 'root-state', expect.objectContaining({ format: 'mermaid' }) ); expect(result).toContain('mocked variable propagation'); }); }); describe('generateMetrics', () => { it('returns null when metrics are disabled', () => { testVisManager = new TestVisualizationManager( mockVisualizationService, mockHistoryService, mockTrackingService, { includeMetrics: false } ); const result = testVisManager.generateMetrics(); expect(result).toBeNull(); }); it('generates compact metrics for standard verbosity', () => { testVisManager.setVerbosity(TestOutputVerbosity.Standard); const result = testVisManager.generateMetrics(); expect(result).toContain('State metrics summary'); expect(result).toContain('Total states: 5'); }); it('generates detailed metrics for debug verbosity', () => { testVisManager.setVerbosity(TestOutputVerbosity.Debug); const result = testVisManager.generateMetrics(); expect(mockVisualizationService.getMetrics).toHaveBeenCalled(); expect(result).toContain('"totalStates": 5'); expect(result).toContain('"statesByType"'); }); }); describe('file output operations', () => { it('clears output directory', () => { // Setup fs mocks vi.mocked(fs.existsSync).mockReturnValue(true); vi.mocked(fs.readdirSync).mockReturnValue(['file1.txt', 'file2.txt']); vi.mocked(fs.unlinkSync).mockImplementation(() => undefined); const result = testVisManager.clearOutputFiles(); expect(result).toBe(true); expect(fs.unlinkSync).toHaveBeenCalledTimes(2); expect(serviceLogger.debug).toHaveBeenCalledWith( 'Cleared state visualization output directory', expect.any(Object) ); }); it('handles errors during file operations', () => { // Setup fs mocks to throw vi.mocked(fs.existsSync).mockReturnValue(true); vi.mocked(fs.readdirSync).mockImplementation(() => { throw new Error('Test error'); }); const result = testVisManager.clearOutputFiles(); expect(result).toBe(false); expect(serviceLogger.error).toHaveBeenCalledWith( 'Failed to clear state visualization output directory', expect.objectContaining({ error: expect.any(Error) }) ); }); }); });