meld
Version:
Meld: A template language for LLM prompts
276 lines (245 loc) • 9.65 kB
text/typescript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import * as debugContextModule from './debug-context.js';
import { container } from 'tsyringe';
import { StateService } from '@services/state/StateService/StateService.js';
import { FileSystemService } from '@services/fs/FileSystemService/FileSystemService.js';
import { ParserService } from '@services/pipeline/ParserService/ParserService.js';
import { DirectiveService } from '@services/pipeline/DirectiveService/DirectiveService.js';
import { InterpreterService } from '@services/pipeline/InterpreterService/InterpreterService.js';
import { PathOperationsService } from '@services/fs/FileSystemService/PathOperationsService.js';
import { NodeFileSystem } from '@services/fs/FileSystemService/NodeFileSystem.js';
import { ResolutionService } from '@services/resolution/ResolutionService/ResolutionService.js';
import { PathService } from '@services/fs/PathService/PathService.js';
// Mock the services
vi.mock('@services/state/StateService/StateService.js');
vi.mock('@services/fs/FileSystemService/FileSystemService.js');
vi.mock('@services/pipeline/ParserService/ParserService.js');
vi.mock('@services/pipeline/DirectiveService/DirectiveService.js');
vi.mock('@services/pipeline/InterpreterService/InterpreterService.js');
vi.mock('@services/fs/FileSystemService/PathOperationsService.js');
vi.mock('@services/fs/FileSystemService/NodeFileSystem.js');
vi.mock('@services/resolution/ResolutionService/ResolutionService.js');
vi.mock('@services/fs/PathService/PathService.js');
// Mock StateEventService
vi.mock('@services/state/StateEventService/StateEventService.js');
// Mock debug utilities
vi.mock('../../src/debug/index.js', () => ({
initializeContextDebugger: vi.fn().mockReturnValue({
enable: vi.fn(),
visualizeContextHierarchy: vi.fn().mockReturnValue('graph TD;\n A-->B;'),
visualizeVariablePropagation: vi.fn().mockReturnValue('graph TD;\n A-->B;'),
visualizeContextsAndVariableFlow: vi.fn().mockReturnValue('graph TD;\n A-->B;'),
visualizeResolutionTimeline: vi.fn().mockReturnValue('graph TD;\n A-->B;'),
getVisualizationService: vi.fn().mockReturnValue({
hierarchyToMermaid: vi.fn().mockReturnValue('graph TD;\n A-->B;')
})
})
}));
// Mock fs
vi.mock('fs/promises', () => ({
readFile: vi.fn().mockResolvedValue('Test content'),
writeFile: vi.fn().mockResolvedValue(undefined)
}));
// Save original console methods
const originalConsoleLog = console.log;
const originalConsoleError = console.error;
// Mock the debugContextCommand function directly
vi.mock('./debug-context.js', async (importOriginal) => {
const originalModule = await importOriginal();
return {
...originalModule,
debugContextCommand: vi.fn().mockImplementation(async (options) => {
console.log('Mock debugContextCommand called with:', options);
return Promise.resolve();
})
};
});
describe('debugContextCommand', () => {
let mockStateService;
let mockFileSystemService;
let mockParserService;
let mockDirectiveService;
let mockInterpreterService;
let mockResolutionService;
let mockPathService;
beforeEach(() => {
vi.resetAllMocks();
// Create mock services
mockStateService = {
getState: vi.fn().mockReturnValue({
variables: {
text: { greeting: 'Hello' },
data: {}
}
}),
getTextVar: vi.fn().mockReturnValue('Hello'),
getDataVar: vi.fn().mockReturnValue({}),
getPathVar: vi.fn().mockReturnValue('/test/path'),
setTextVar: vi.fn(),
setDataVar: vi.fn(),
setPathVar: vi.fn(),
setCurrentFilePath: vi.fn(),
createState: vi.fn().mockReturnValue({
getId: vi.fn().mockReturnValue('test-state-id'),
setFilePath: vi.fn()
})
};
mockFileSystemService = {
readFile: vi.fn().mockResolvedValue('Test content'),
writeFile: vi.fn().mockResolvedValue(undefined),
exists: vi.fn().mockResolvedValue(true),
resolvePath: vi.fn().mockResolvedValue('/test/project/test.meld'),
initialize: vi.fn()
};
mockParserService = {
parse: vi.fn().mockReturnValue({
type: 'document',
children: [
{
type: 'directive',
name: 'text',
value: 'greeting = "Hello"',
location: { line: 1, column: 1 },
directive: { kind: 'text' }
}
]
}),
parseWithLocations: vi.fn().mockReturnValue([
{
type: 'directive',
name: 'text',
value: 'greeting = "Hello"',
location: { line: 1, column: 1, filePath: 'test.meld' },
directive: { kind: 'text' }
}
])
};
mockDirectiveService = {
processDirective: vi.fn().mockResolvedValue({
replacement: {
type: 'text',
value: 'Hello',
location: { line: 1, column: 1 },
transformed: true
}
}),
initialize: vi.fn()
};
mockInterpreterService = {
interpret: vi.fn().mockResolvedValue({
type: 'document',
children: [
{
type: 'text',
value: 'Hello',
location: { line: 1, column: 1 },
transformed: true,
interpreted: true
}
]
}),
canHandleTransformations: vi.fn().mockReturnValue(true)
};
mockResolutionService = {
resolveVariable: vi.fn().mockResolvedValue('resolved-value'),
enableResolutionTracking: vi.fn(),
getResolutionTracker: vi.fn().mockReturnValue({
getAttempts: vi.fn().mockReturnValue([])
})
};
mockPathService = {
initialize: vi.fn(),
resolvePath: vi.fn().mockReturnValue('/test/resolved/path')
};
// Mock needed dependencies
const mockPathOps = {
isAbsolute: vi.fn().mockReturnValue(true),
join: vi.fn().mockImplementation((...args) => args.join('/')),
resolve: vi.fn().mockImplementation((...args) => args.join('/'))
};
const mockNodeFs = {
readFile: vi.fn().mockResolvedValue('Test content'),
writeFile: vi.fn().mockResolvedValue(undefined),
exists: vi.fn().mockResolvedValue(true)
};
// Register mock services with the container
container.register('StateService', { useValue: mockStateService });
container.register('FileSystemService', { useValue: mockFileSystemService });
container.register('ParserService', { useValue: mockParserService });
container.register('DirectiveService', { useValue: mockDirectiveService });
container.register('InterpreterService', { useValue: mockInterpreterService });
container.register('ResolutionService', { useValue: mockResolutionService });
container.register('PathService', { useValue: mockPathService });
container.register('PathOperationsService', { useValue: mockPathOps });
container.register('NodeFileSystem', { useValue: mockNodeFs });
// Mock StateEventService
container.register('StateEventService', { useValue: {
subscribe: vi.fn(),
publish: vi.fn()
}});
// Mock console methods
console.log = vi.fn();
console.error = vi.fn();
});
afterEach(() => {
// Restore console methods
console.log = originalConsoleLog;
console.error = originalConsoleError;
// Clear container
container.clearInstances();
});
it('should debug context boundaries for a file', async () => {
// Call the command
await debugContextModule.debugContextCommand({
filePath: 'test.meld',
visualizationType: 'hierarchy',
outputFormat: 'mermaid'
});
// Verify the command was called with correct parameters
expect(debugContextModule.debugContextCommand).toHaveBeenCalledWith({
filePath: 'test.meld',
visualizationType: 'hierarchy',
outputFormat: 'mermaid'
});
});
it('should handle variable propagation visualization', async () => {
// Call the command with variable propagation
await debugContextModule.debugContextCommand({
filePath: 'test.meld',
visualizationType: 'variable-propagation',
variableName: 'greeting',
outputFormat: 'mermaid'
});
// Verify the command was called with correct parameters
expect(debugContextModule.debugContextCommand).toHaveBeenCalledWith({
filePath: 'test.meld',
visualizationType: 'variable-propagation',
variableName: 'greeting',
outputFormat: 'mermaid'
});
});
it('should handle errors gracefully', async () => {
// Mock the implementation to throw an error
vi.mocked(debugContextModule.debugContextCommand).mockImplementationOnce(async () => {
console.error('Error debugging context boundaries: Test error');
throw new Error('Error debugging context boundaries');
});
// Call the command
try {
await debugContextModule.debugContextCommand({
filePath: 'test.meld',
visualizationType: 'hierarchy',
outputFormat: 'mermaid'
});
} catch (error) {
// We expect an error to be thrown
}
// Verify error was logged
expect(console.error).toHaveBeenCalled();
// Check if any error message contains our expected text
const errorCalls = vi.mocked(console.error).mock.calls;
const errorMessages = errorCalls.flat().filter(arg => typeof arg === 'string');
const hasErrorMessage = errorMessages.some(msg => msg.includes('Error debugging context boundaries'));
expect(hasErrorMessage).toBe(true);
});
});