cmte
Version:
Design by Committee™ except it's just you and LLMs
126 lines (109 loc) • 5.78 kB
JavaScript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
// Remove static import: import { OutputReferenceResolver } from "../output-reference-resolver.js";
import { logger } from '../../../utils/logger.js'; // Corrected path
// Mock the logger module
vi.mock('../../../utils/logger.js', () => ({
logger: {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn()
}
}));
// Helper function - uses dynamically imported class
function setupResolver(ResolverClass) {
const resolver = new ResolverClass();
resolver.registerOutput('set1', 'task_noniter', 'original_noniter');
resolver.registerIteratedOutput('set1', 'task1', 'item1', 'result1_iter');
resolver.registerIteratedOutput('set1', 'task1', 'item2', 'result2_iter');
resolver.registerIteratedOutput('set1', 'task1', 'item3', 'result3_iter');
resolver.registerOutput('set2', 'task2', 'result2_set2');
resolver.registerOutput('set3', 'task_obj', { nested: { value: 'nested_value' } });
resolver.registerIteratedOutput('set3', 'task_iter_obj', 'key1', { itemVal: 'iter_val1' });
resolver.registerIteratedOutput('set3', 'task_iter_obj', 'key2', { itemVal: 'iter_val2' });
return resolver;
}
describe('OutputReferenceResolver', () => {
let OutputReferenceResolver; // For dynamic import
let resolver;
beforeEach(async () => {
// Ensure mocks are applied *before* dynamic import
vi.clearAllMocks();
// Dynamically import the class AFTER mocks are set up for the logger
const module = await import("../output-reference-resolver.js");
OutputReferenceResolver = module.OutputReferenceResolver;
// Setup resolver instance
resolver = setupResolver(OutputReferenceResolver);
});
// No afterEach needed for vi.mock (it's handled by Vitest)
it('throws error for invalid reference format', () => {
expect(() => resolver.resolveReference('invalid-format')).toThrow('Invalid output reference format');
// Check the initial debug log
expect(logger.debug).toHaveBeenCalledWith('Resolving reference: invalid-format');
});
it('getAllOutputs returns copy of outputs', () => {
const outputs = resolver.getAllOutputs();
outputs.set1.task_noniter = 'modified';
expect(resolver.resolveReference('set1.task_noniter')).toBe('original_noniter');
});
it('clear removes all outputs and iteration context', () => {
resolver.setIterationContext('some_key');
resolver.clear();
expect(resolver.getAllOutputs()).toEqual({});
expect(resolver.currentIteration).toBeNull();
expect(() => resolver.resolveReference('set1.task1[item1]')).toThrow('Set not found');
});
describe('basic references', () => {
it('resolves basic task reference', () => {
expect(resolver.resolveReference('set1.task_noniter')).toBe('original_noniter');
// Check the initial debug log for this call
expect(logger.debug).toHaveBeenCalledWith('Resolving reference: set1.task_noniter');
});
// ... (Keep other basic reference tests, they should pass now) ...
it('throws error for non-existent set', () => {
expect(() => resolver.resolveReference('nonExistentSet.task_noniter')).toThrow('Set not found');
});
it('throws error for non-existent task', () => {
expect(() => resolver.resolveReference('set1.nonExistentTask')).toThrow('Task output not found');
});
it('throws error when referencing iterated output without key', () => {
expect(() => resolver.resolveReference('set1.task1')).toThrow('Ambiguous reference');
});
});
describe('iteration references', () => {
it('resolves specific iteration reference', () => {
expect(resolver.resolveReference('set1.task1[item2]')).toBe('result2_iter');
// Check the initial debug log for this call
expect(logger.debug).toHaveBeenCalledWith('Resolving reference: set1.task1[item2]');
});
it('resolves wildcard iteration reference', () => {
expect(resolver.resolveReference('set1.task1[*]')).toEqual(['result1_iter', 'result2_iter', 'result3_iter']);
// Check the initial debug log for this call
expect(logger.debug).toHaveBeenCalledWith('Resolving reference: set1.task1[*]');
});
it('resolves [this] reference in iteration context', () => {
resolver.setIterationContext('item2');
expect(resolver.resolveReference('set1.task1[this]')).toBe('result2_iter');
// Check the initial debug log for this call (includes context)
expect(logger.debug).toHaveBeenCalledWith('Resolving reference: set1.task1[this] (Current Context: item2)');
resolver.clearIterationContext();
});
// ... (Keep other iteration tests, they should pass now) ...
it('throws error for [this] reference outside iteration context', () => {
expect(() => resolver.resolveReference('set1.task1[this]')).toThrow('Cannot use [this] iteration key');
});
it('throws error for non-existent iteration', () => {
expect(() => resolver.resolveReference('set1.task1[nonExistentItem]')).toThrow('Iteration key \'nonExistentItem\' not found');
});
it('throws error when using iteration key on non-iterated output', () => {
expect(() => resolver.resolveReference('set1.task_noniter[item1]')).toThrow('Cannot use iteration key');
});
});
// ... (Keep property path tests, they should pass now) ...
describe('property path resolution', () => {
it('resolves nested property on non-iterated output', () => { /* ... */ });
it('resolves nested property on specific iterated output', () => { /* ... */ });
it('resolves nested property using [this]', () => { /* ... */ });
it('throws reasonable error if property path is invalid', () => { /* ... */ });
});
});