meld
Version:
Meld: A template language for LLM prompts
173 lines (138 loc) • 6.29 kB
text/typescript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { DirectiveService } from './DirectiveService.js';
import { TestContext } from '@tests/utils/TestContext.js';
import { DirectiveError, DirectiveErrorCode } from './errors/DirectiveError.js';
import type { DirectiveNode } from 'meld-spec';
describe('DirectiveService', () => {
let context: TestContext;
let service: DirectiveService;
beforeEach(async () => {
// Initialize test context
context = new TestContext();
await context.initialize();
// Create service instance
service = new DirectiveService();
// Initialize with real services from context
service.initialize(
context.services.validation,
context.services.state,
context.services.path,
context.services.filesystem,
context.services.parser,
context.services.interpreter,
context.services.circularity,
context.services.resolution
);
// Load test fixtures
await context.fixtures.load('directiveTestProject');
});
afterEach(async () => {
await context.cleanup();
});
describe('Service initialization', () => {
it('should initialize with all required services', () => {
expect(service.getSupportedDirectives()).toContain('text');
expect(service.getSupportedDirectives()).toContain('data');
expect(service.getSupportedDirectives()).toContain('path');
});
it('should throw if used before initialization', async () => {
const uninitializedService = new DirectiveService();
const node = context.factory.createTextDirective('test', '"value"', context.factory.createLocation(1, 1));
const execContext = { currentFilePath: 'test.meld', state: context.services.state };
await expect(uninitializedService.processDirective(node, execContext))
.rejects.toThrow('DirectiveService must be initialized before use');
});
});
describe('Directive processing', () => {
describe('Text directives', () => {
it('should process basic text directive', async () => {
// Verify file exists
const exists = await context.fs.exists('test.meld');
console.log('test.meld exists:', exists);
// Parse the fixture file
const content = await context.fs.readFile('test.meld');
console.log('test.meld content:', content);
const nodes = await context.services.parser.parse(content);
console.log('Parsed nodes:', nodes);
const node = nodes[0] as DirectiveNode;
// Create execution context
const execContext = {
currentFilePath: 'test.meld',
state: context.services.state
};
// Process the directive
const result = await service.processDirective(node, execContext);
// Verify the result
expect(result.getTextVar('greeting')).toBe('Hello');
});
it('should process text directive with variable interpolation', async () => {
// Set up initial state with a variable
const state = context.services.state;
state.setTextVar('name', 'World');
// Parse and process
const content = await context.fs.readFile('test-interpolation.meld');
const nodes = await context.services.parser.parse(content);
const node = nodes[0] as DirectiveNode;
const result = await service.processDirective(node, {
currentFilePath: 'test-interpolation.meld',
state
});
expect(result.getTextVar('greeting')).toBe('Hello World');
});
});
describe('Data directives', () => {
it('should process data directive with object value', async () => {
const content = await context.fs.readFile('test-data.meld');
const nodes = await context.services.parser.parse(content);
const node = nodes[0] as DirectiveNode;
const result = await service.processDirective(node, {
currentFilePath: 'test-data.meld',
state: context.services.state
});
expect(result.getDataVar('config')).toEqual({ key: 'value' });
});
it('should process data directive with variable interpolation', async () => {
// Set up initial state
const state = context.services.state;
state.setTextVar('user', 'Alice');
const content = await context.fs.readFile('test-data-interpolation.meld');
const nodes = await context.services.parser.parse(content);
const node = nodes[0] as DirectiveNode;
const result = await service.processDirective(node, {
currentFilePath: 'test-data-interpolation.meld',
state
});
expect(result.getDataVar('config')).toEqual({ greeting: 'Hello Alice' });
});
});
describe('Import directives', () => {
it('should process basic import', async () => {
// Create import directive node with value property
const node = context.factory.createImportDirective('module.meld', context.factory.createLocation(1, 1));
const result = await service.processDirective(node, {
currentFilePath: 'main.meld',
state: context.services.state
});
expect(result.getTextVar('greeting')).toBe('Hello');
});
it('should handle nested imports', async () => {
// Create import directive node with value property
const node = context.factory.createImportDirective('inner.meld', context.factory.createLocation(1, 1));
const result = await service.processDirective(node, {
currentFilePath: 'middle.meld',
state: context.services.state
});
expect(result.getTextVar('greeting')).toBe('Hello');
});
it('should detect circular imports', async () => {
// Create import directive node with value property
const node = context.factory.createImportDirective('b.meld', context.factory.createLocation(1, 1));
await expect(service.processDirective(node, {
currentFilePath: 'a.meld',
state: context.services.state
})).rejects.toThrow(DirectiveError);
});
});
// ... continue with other directive types and error cases
});
});