UNPKG

meld

Version:

Meld: A template language for LLM prompts

221 lines (189 loc) 7.06 kB
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; import { DirectiveNode, TextNode } from 'meld-spec'; import { EmbedDirectiveHandler } from '@services/pipeline/DirectiveService/handlers/execution/EmbedDirectiveHandler.js'; import { DirectiveError } from '@services/pipeline/DirectiveService/errors/DirectiveError.js'; // Mock dependencies const mockValidationService = { validate: vi.fn() }; const mockResolutionService = { resolveInContext: vi.fn(), extractSection: vi.fn() }; const mockStateService = { clone: vi.fn(), createChildState: vi.fn(), mergeChildState: vi.fn(), isTransformationEnabled: vi.fn().mockReturnValue(true), // Transformation mode enabled transformNode: vi.fn() }; const mockCircularityService = { beginImport: vi.fn(), endImport: vi.fn(), isInStack: vi.fn() }; const mockFileSystemService = { exists: vi.fn(), readFile: vi.fn() }; const mockParserService = { parse: vi.fn() }; const mockInterpreterService = { interpret: vi.fn() }; const mockLogger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }; // Helper to create a directive node function createEmbedDirectiveNode(path?: string): DirectiveNode { return { type: 'Directive', directive: { kind: 'embed', path: path }, location: { start: { line: 1, column: 1 }, end: { line: 1, column: 20 } } } as DirectiveNode; } describe('EmbedDirectiveHandler Transformation Fixes', () => { let handler: EmbedDirectiveHandler; let clonedState: any; let childState: any; let parentState: any; beforeEach(() => { // Reset mocks vi.clearAllMocks(); // Setup mock states childState = { setTextVar: vi.fn(), setDataVar: vi.fn(), setPathVar: vi.fn(), setCommand: vi.fn(), getAllTextVars: vi.fn().mockReturnValue({ 'testVar': 'testValue' }), getAllDataVars: vi.fn().mockReturnValue({ 'dataVar': { test: 'data' } }), getAllPathVars: vi.fn().mockReturnValue({ 'pathVar': '/test/path' }), getAllCommands: vi.fn().mockReturnValue({ 'cmdVar': 'echo test' }), isTransformationEnabled: vi.fn().mockReturnValue(true), transformNode: vi.fn() }; clonedState = { setTextVar: vi.fn(), setDataVar: vi.fn(), setPathVar: vi.fn(), setCommand: vi.fn(), createChildState: vi.fn().mockReturnValue(childState), mergeChildState: vi.fn(), isTransformationEnabled: vi.fn().mockReturnValue(true), transformNode: vi.fn() }; parentState = { setTextVar: vi.fn(), setDataVar: vi.fn(), setPathVar: vi.fn(), setCommand: vi.fn(), transformNode: vi.fn() }; mockStateService.clone.mockReturnValue(clonedState); mockStateService.createChildState.mockReturnValue(childState); mockInterpreterService.interpret.mockResolvedValue(childState); // Create handler handler = new EmbedDirectiveHandler( mockValidationService as any, mockResolutionService as any, mockStateService as any, mockCircularityService as any, mockFileSystemService as any, mockParserService as any, mockInterpreterService as any, mockLogger as any ); }); afterEach(() => { vi.resetAllMocks(); }); describe('Transformation Mode', () => { it('should return a replacement node with file content', async () => { // Create a directive node with a path const node = createEmbedDirectiveNode('test.md'); const context = { currentFilePath: 'test.meld', state: mockStateService, parentState: parentState // Use the properly defined parent state }; // Setup mocks mockResolutionService.resolveInContext.mockResolvedValue('test.md'); mockFileSystemService.exists.mockResolvedValue(true); mockFileSystemService.readFile.mockResolvedValue('Test content'); mockParserService.parse.mockResolvedValue([]); // Execute const result = await handler.execute(node, context); // Verify replacement node expect(result.replacement).toBeDefined(); expect((result.replacement as TextNode).type).toBe('Text'); expect((result.replacement as TextNode).content).toBe('Test content'); }); it('should propagate variables to parent state in transformation mode', async () => { // Create a directive node with a path const node = createEmbedDirectiveNode('test.md'); const context = { currentFilePath: 'test.meld', state: mockStateService, parentState: parentState }; // Setup mocks mockResolutionService.resolveInContext.mockResolvedValue('test.md'); mockFileSystemService.exists.mockResolvedValue(true); mockFileSystemService.readFile.mockResolvedValue('Test content'); mockParserService.parse.mockResolvedValue([]); // Execute await handler.execute(node, context); // Verify variables were propagated to parent state expect(parentState.setTextVar).toHaveBeenCalledWith('testVar', 'testValue'); expect(parentState.setDataVar).toHaveBeenCalledWith('dataVar', { test: 'data' }); expect(parentState.setPathVar).toHaveBeenCalledWith('pathVar', '/test/path'); expect(parentState.setCommand).toHaveBeenCalledWith('cmdVar', 'echo test'); }); }); describe('Error Handling in Transformation Mode', () => { it('should throw DirectiveError when file does not exist', async () => { // Create a directive node with a path const node = createEmbedDirectiveNode('nonexistent.md'); const context = { currentFilePath: 'test.meld', state: mockStateService, parentState: parentState }; // Setup mocks mockResolutionService.resolveInContext.mockResolvedValue('nonexistent.md'); mockFileSystemService.exists.mockResolvedValue(false); // Execute and expect error await expect(handler.execute(node, context)).rejects.toThrow(DirectiveError); await expect(handler.execute(node, context)).rejects.toThrow(/File not found/); }); it('should always call endImport even if an error occurs in transformation mode', async () => { // Create a directive node with a path const node = createEmbedDirectiveNode('test.md'); const context = { currentFilePath: 'test.meld', state: mockStateService, parentState: parentState }; // Setup mocks mockResolutionService.resolveInContext.mockResolvedValue('test.md'); mockFileSystemService.exists.mockResolvedValue(true); mockFileSystemService.readFile.mockRejectedValue(new Error('Read error')); // Execute and expect error await expect(handler.execute(node, context)).rejects.toThrow(); // Verify that endImport was called expect(mockCircularityService.beginImport).toHaveBeenCalledWith('test.md'); expect(mockCircularityService.endImport).toHaveBeenCalledWith('test.md'); }); }); });