meld
Version:
Meld: A template language for LLM prompts
326 lines (264 loc) • 12.2 kB
text/typescript
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) })
);
});
});
});