meld
Version:
Meld: A template language for LLM prompts
198 lines (176 loc) • 5.71 kB
text/typescript
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { StateHistoryService } from './StateHistoryService';
import { IStateEventService, StateEvent } from '../StateEventService/IStateEventService';
import { StateOperation, StateTransformation } from './IStateHistoryService';
describe('StateHistoryService', () => {
let mockEventService: IStateEventService;
let historyService: StateHistoryService;
let eventHandlers: Map<string, Function[]>;
beforeEach(() => {
eventHandlers = new Map();
mockEventService = {
on: vi.fn((type, handler) => {
if (!eventHandlers.has(type)) {
eventHandlers.set(type, []);
}
eventHandlers.get(type)!.push(handler);
}),
off: vi.fn(),
emit: vi.fn(),
getHandlers: vi.fn(),
};
historyService = new StateHistoryService(mockEventService);
});
describe('Event Subscription', () => {
it('subscribes to all relevant state events', () => {
expect(mockEventService.on).toHaveBeenCalledWith('create', expect.any(Function));
expect(mockEventService.on).toHaveBeenCalledWith('clone', expect.any(Function));
expect(mockEventService.on).toHaveBeenCalledWith('transform', expect.any(Function));
expect(mockEventService.on).toHaveBeenCalledWith('merge', expect.any(Function));
});
});
describe('Operation Recording', () => {
it('records operations when events are received', () => {
const event: StateEvent = {
type: 'create',
stateId: 'state1',
source: 'test',
timestamp: Date.now(),
};
// Simulate event emission
eventHandlers.get('create');
const history = historyService.getOperationHistory('state1');
expect(history).toHaveLength(1);
expect(history[0]).toMatchObject({
type: 'create',
stateId: 'state1',
source: 'test',
});
});
it('records transformation details separately', () => {
const event: StateEvent = {
type: 'transform',
stateId: 'state1',
source: 'test',
timestamp: Date.now(),
details: {
operation: 'update',
before: { value: 1 },
after: { value: 2 },
},
};
// Simulate event emission
eventHandlers.get('transform');
const transformations = historyService.getTransformationChain('state1');
expect(transformations).toHaveLength(1);
expect(transformations[0]).toMatchObject({
stateId: 'state1',
operation: 'update',
before: { value: 1 },
after: { value: 2 },
});
});
});
describe('History Querying', () => {
beforeEach(() => {
// Setup some test data
const operations: StateOperation[] = [
{
type: 'create',
stateId: 'state1',
source: 'test',
timestamp: 1000,
},
{
type: 'transform',
stateId: 'state1',
source: 'test',
timestamp: 2000,
},
{
type: 'clone',
stateId: 'state2',
source: 'other',
timestamp: 3000,
},
];
operations.forEach(op => historyService.recordOperation(op));
});
it('filters operations by state ID', () => {
const history = historyService.queryHistory({ stateIds: ['state1'] });
expect(history).toHaveLength(2);
expect(history.every(op => op.stateId === 'state1')).toBe(true);
});
it('filters operations by type', () => {
const history = historyService.queryHistory({ types: ['create'] });
expect(history).toHaveLength(1);
expect(history.at(0).type).toBe('create');
});
it('filters operations by source', () => {
const history = historyService.queryHistory({ source: 'other' });
expect(history).toHaveLength(1);
expect(history.at(0).source).toBe('other');
});
it('filters operations by time range', () => {
const history = historyService.queryHistory({
timeRange: {
start: 1500,
end: 2500,
},
});
expect(history).toHaveLength(1);
expect(history[0].timestamp).toBe(2000);
});
});
describe('Related Operations', () => {
it('finds operations within time window', () => {
const operations: StateOperation[] = [
{
type: 'create',
stateId: 'state1',
source: 'test',
timestamp: 1000,
},
{
type: 'create',
stateId: 'state2',
source: 'test',
timestamp: 1100,
},
{
type: 'create',
stateId: 'state3',
source: 'test',
timestamp: 2000,
},
];
operations.forEach(op => historyService.recordOperation(op));
const related = historyService.getRelatedOperations(operations[0], 200);
expect(related).toHaveLength(1);
expect(related[0].stateId).toBe('state2');
});
});
describe('History Cleanup', () => {
it('clears history before specified timestamp', () => {
const operations: StateOperation[] = [
{
type: 'create',
stateId: 'state1',
source: 'test',
timestamp: 1000,
},
{
type: 'create',
stateId: 'state2',
source: 'test',
timestamp: 2000,
},
];
operations.forEach(op => historyService.recordOperation(op));
historyService.clearHistoryBefore(1500);
const history = historyService.queryHistory({});
expect(history).toHaveLength(1);
expect(history[0].timestamp).toBe(2000);
});
});
});