UNPKG

meld

Version:

Meld: A template language for LLM prompts

156 lines (129 loc) 6 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import type { DirectiveNode, DirectiveContext } from 'meld-spec'; import { RunDirectiveHandler } from './RunDirectiveHandler.js'; import type { IValidationService } from '@services/resolution/ValidationService/IValidationService.js'; import type { IStateService } from '@services/state/StateService/IStateService.js'; import type { IResolutionService } from '@services/resolution/ResolutionService/IResolutionService.js'; import type { IFileSystemService } from '@services/fs/FileSystemService/IFileSystemService.js'; import { createRunDirective, createLocation } from '@tests/utils/testFactories.js'; describe('RunDirectiveHandler Transformation', () => { let handler: RunDirectiveHandler; let validationService: IValidationService; let stateService: IStateService; let resolutionService: IResolutionService; let fileSystemService: IFileSystemService; let clonedState: IStateService; beforeEach(() => { validationService = { validate: vi.fn() } as unknown as IValidationService; clonedState = { setTextVar: vi.fn(), clone: vi.fn().mockReturnThis(), isTransformationEnabled: vi.fn().mockReturnValue(true), transformNode: vi.fn() } as unknown as IStateService; stateService = { setTextVar: vi.fn(), clone: vi.fn().mockReturnValue(clonedState), isTransformationEnabled: vi.fn().mockReturnValue(true) } as unknown as IStateService; resolutionService = { resolveInContext: vi.fn() } as unknown as IResolutionService; fileSystemService = { getCwd: vi.fn().mockReturnValue('/workspace'), executeCommand: vi.fn() } as unknown as IFileSystemService; handler = new RunDirectiveHandler( validationService, resolutionService, stateService, fileSystemService ); // Reset mocks vi.clearAllMocks(); }); describe('transformation behavior', () => { it('should return replacement node with command output when transformation enabled', async () => { const node = createRunDirective('echo test', createLocation(1, 1)); const context = { currentFilePath: 'test.meld', state: stateService }; vi.mocked(validationService.validate).mockResolvedValue(undefined); vi.mocked(resolutionService.resolveInContext).mockResolvedValue('echo test'); vi.mocked(fileSystemService.executeCommand).mockResolvedValue({ stdout: 'test output', stderr: '' }); const result = await handler.execute(node, context); expect(result.replacement).toBeDefined(); expect(result.replacement).toEqual({ type: 'Text', content: 'test output', location: node.location }); expect(result.state).toBe(clonedState); }); it('should handle variable interpolation in command during transformation', async () => { const node = createRunDirective('echo {{message}}', createLocation(1, 1)); const context = { currentFilePath: 'test.meld', state: stateService }; vi.mocked(validationService.validate).mockResolvedValue(undefined); vi.mocked(resolutionService.resolveInContext).mockResolvedValue('echo Hello World'); vi.mocked(fileSystemService.executeCommand).mockResolvedValue({ stdout: 'Hello World', stderr: '' }); const result = await handler.execute(node, context); expect(result.replacement).toBeDefined(); expect(result.replacement).toEqual({ type: 'Text', content: 'Hello World', location: node.location }); expect(result.state).toBe(clonedState); }); it('should handle stderr output in transformation', async () => { const node = createRunDirective('echo error >&2', createLocation(1, 1)); const context = { currentFilePath: 'test.meld', state: stateService }; vi.mocked(validationService.validate).mockResolvedValue(undefined); vi.mocked(resolutionService.resolveInContext).mockResolvedValue('echo error >&2'); vi.mocked(fileSystemService.executeCommand).mockResolvedValue({ stdout: '', stderr: 'error output' }); const result = await handler.execute(node, context); expect(result.replacement).toBeDefined(); expect(result.replacement).toEqual({ type: 'Text', content: 'error output', location: node.location }); expect(result.state).toBe(clonedState); }); it('should handle both stdout and stderr in transformation', async () => { const node = createRunDirective('echo test && echo error >&2', createLocation(1, 1)); const context = { currentFilePath: 'test.meld', state: stateService }; vi.mocked(validationService.validate).mockResolvedValue(undefined); vi.mocked(resolutionService.resolveInContext).mockResolvedValue('echo test && echo error >&2'); vi.mocked(fileSystemService.executeCommand).mockResolvedValue({ stdout: 'test output', stderr: 'error output' }); const result = await handler.execute(node, context); expect(result.replacement).toBeDefined(); expect(result.replacement).toEqual({ type: 'Text', content: 'test output\nerror output', location: node.location }); expect(result.state).toBe(clonedState); }); it('should preserve error handling during transformation', async () => { const node = createRunDirective('invalid-command', createLocation(1, 1)); const context = { currentFilePath: 'test.meld', state: stateService }; vi.mocked(validationService.validate).mockResolvedValue(undefined); vi.mocked(resolutionService.resolveInContext).mockResolvedValue('invalid-command'); vi.mocked(fileSystemService.executeCommand).mockRejectedValue(new Error('Command failed')); await expect(handler.execute(node, context)).rejects.toThrow('Failed to execute command: Command failed'); }); }); });