meld
Version:
Meld: A template language for LLM prompts
225 lines (189 loc) • 7.86 kB
text/typescript
import { describe, it, expect, beforeEach } from 'vitest';
import { StateFactory } from './StateFactory.js';
import type { StateNode } from './types.js';
describe('StateFactory', () => {
let factory: StateFactory;
beforeEach(() => {
factory = new StateFactory();
});
describe('createState', () => {
it('should create an empty state', () => {
const state = factory.createState();
expect(state.variables.text.size).toBe(0);
expect(state.variables.data.size).toBe(0);
expect(state.variables.path.size).toBe(0);
expect(state.imports.size).toBe(0);
expect(state.nodes.length).toBe(0);
expect(state.filePath).toBeUndefined();
expect(state.parentState).toBeUndefined();
});
it('should create state with options', () => {
const parent = factory.createState();
const state = factory.createState({
parentState: parent,
filePath: '/test/file.md',
source: 'test'
});
expect(state.parentState).toBe(parent);
expect(state.filePath).toBe('/test/file.md');
});
it('should inherit parent state', () => {
// Create parent with some state
const parentBase = factory.createState();
const parent = factory.updateState(parentBase, {
variables: {
text: new Map([['inherited', 'value']]),
data: new Map([['config', { inherited: true }]]),
path: new Map([['root', '/parent']])
},
imports: new Set(['parent.md']),
nodes: [{ type: 'text', value: 'parent' } as any]
});
// Create child state
const child = factory.createState({ parentState: parent });
// Verify inheritance
expect(child.variables.text.get('inherited')).toBe('value');
expect(child.variables.data.get('config')).toEqual({ inherited: true });
expect(child.variables.path.get('root')).toBe('/parent');
expect(child.imports.has('parent.md')).toBe(true);
expect(child.nodes[0].value).toBe('parent');
});
});
describe('createChildState', () => {
it('should create child state with parent reference', () => {
const parent = factory.createState();
const child = factory.createChildState(parent);
expect(child.parentState).toBe(parent);
});
it('should create empty child state that inherits parent values', () => {
// Create parent with some state
const parentBase = factory.createState();
const parent = factory.updateState(parentBase, {
variables: {
text: new Map([['text', 'parent']]),
data: new Map([['data', { value: 'parent' }]]),
path: new Map([['path', '/parent']])
}
});
const child = factory.createChildState(parent);
// Verify child inherits parent values
expect(child.variables.text.get('text')).toBe('parent');
expect(child.variables.data.get('data')).toEqual({ value: 'parent' });
expect(child.variables.path.get('path')).toBe('/parent');
});
});
describe('mergeStates', () => {
it('should merge variables from child to parent', () => {
// Create parent state
const parentBase = factory.createState();
const parent = factory.updateState(parentBase, {
variables: {
text: new Map([['parentText', 'parent']]),
data: new Map([['parentData', { value: 'parent' }]]),
path: new Map([['parentPath', '/parent']])
}
});
// Create child state
const childBase = factory.createState();
const child = factory.updateState(childBase, {
variables: {
text: new Map([['childText', 'child']]),
data: new Map([['childData', { value: 'child' }]]),
path: new Map([['childPath', '/child']])
}
});
const merged = factory.mergeStates(parent, child);
// Check merged variables
expect(merged.variables.text.get('parentText')).toBe('parent');
expect(merged.variables.text.get('childText')).toBe('child');
expect(merged.variables.data.get('parentData')).toEqual({ value: 'parent' });
expect(merged.variables.data.get('childData')).toEqual({ value: 'child' });
expect(merged.variables.path.get('parentPath')).toBe('/parent');
expect(merged.variables.path.get('childPath')).toBe('/child');
});
it('should override parent variables with child values', () => {
// Create parent state
const parentBase = factory.createState();
const parent = factory.updateState(parentBase, {
variables: {
text: new Map([['text', 'parent']])
}
});
// Create child state
const childBase = factory.createState();
const child = factory.updateState(childBase, {
variables: {
text: new Map([['text', 'child']])
}
});
const merged = factory.mergeStates(parent, child);
expect(merged.variables.text.get('text')).toBe('child');
// Verify parent state wasn't modified
expect(parent.variables.text.get('text')).toBe('parent');
});
it('should merge imports and nodes', () => {
// Create parent state
const parentBase = factory.createState();
const parent = factory.updateState(parentBase, {
imports: new Set(['parent.md']),
nodes: [{ type: 'text', value: 'parent' } as any]
});
// Create child state
const childBase = factory.createState();
const child = factory.updateState(childBase, {
imports: new Set(['child.md']),
nodes: [{ type: 'text', value: 'child' } as any]
});
const merged = factory.mergeStates(parent, child);
expect(merged.imports.has('parent.md')).toBe(true);
expect(merged.imports.has('child.md')).toBe(true);
expect(merged.nodes).toHaveLength(2);
expect(merged.nodes[0].value).toBe('parent');
expect(merged.nodes[1].value).toBe('child');
// Verify original states weren't modified
expect(parent.imports.size).toBe(1);
expect(child.imports.size).toBe(1);
expect(parent.nodes).toHaveLength(1);
expect(child.nodes).toHaveLength(1);
});
});
describe('updateState', () => {
it('should update state with new values', () => {
const initial = factory.createState();
const updates: Partial<StateNode> = {
filePath: '/updated/file.md',
variables: {
text: new Map([['text', 'updated']]),
data: new Map([['data', { value: 'updated' }]]),
path: new Map([['path', '/updated']])
}
};
const updated = factory.updateState(initial, updates);
expect(updated.filePath).toBe('/updated/file.md');
expect(updated.variables.text.get('text')).toBe('updated');
expect(updated.variables.data.get('data')).toEqual({ value: 'updated' });
expect(updated.variables.path.get('path')).toBe('/updated');
// Verify original state wasn't modified
expect(initial.variables.text.size).toBe(0);
expect(initial.variables.data.size).toBe(0);
expect(initial.variables.path.size).toBe(0);
});
it('should preserve unmodified values', () => {
// Create initial state with some values
const baseState = factory.createState();
const initial = factory.updateState(baseState, {
variables: {
text: new Map([['preserved', 'value']])
}
});
const updates: Partial<StateNode> = {
filePath: '/updated/file.md'
};
const updated = factory.updateState(initial, updates);
expect(updated.filePath).toBe('/updated/file.md');
expect(updated.variables.text.get('preserved')).toBe('value');
// Verify values are copied, not referenced
expect(updated.variables.text).not.toBe(initial.variables.text);
});
});
});