// @lifecycle canonical - Unit tests for ConditionEvaluator
import { beforeEach, describe, expect, it, jest } from '@jest/globals';
import {
ConditionEvaluator,
type ConditionEvaluationResult,
} from '../../../../src/engine/execution/pipeline/decisions/injection/index.js';
import type {
InjectionCondition,
InjectionDecisionInput,
} from '../../../../src/engine/execution/pipeline/decisions/injection/types.js';
describe('ConditionEvaluator', () => {
let mockLogger: any;
let evaluator: ConditionEvaluator;
beforeEach(() => {
mockLogger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};
evaluator = new ConditionEvaluator(mockLogger);
});
describe('evaluate', () => {
it('should return matched=false when no conditions exist', () => {
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 1,
};
const result = evaluator.evaluate(undefined, input);
expect(result.matched).toBe(false);
expect(result.reason).toContain('No conditions');
});
it('should return matched=false when empty conditions array', () => {
const conditions: InjectionCondition[] = [];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 1,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(false);
});
});
describe('gate-status condition', () => {
it('should match when gate has expected status', () => {
const conditions: InjectionCondition[] = [
{
id: 'gate-check',
when: { type: 'gate-status', gateId: 'quality-check', status: 'pass' },
then: 'inject',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 1,
gateStatuses: new Map([['quality-check', 'pass']]),
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
expect(result.action).toBe('inject');
});
it('should not match when gate status differs', () => {
const conditions: InjectionCondition[] = [
{
id: 'gate-check',
when: { type: 'gate-status', gateId: 'quality-check', status: 'pass' },
then: 'skip',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 1,
gateStatuses: new Map([['quality-check', 'fail']]),
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(false);
});
it('should not match when no gate statuses provided', () => {
const conditions: InjectionCondition[] = [
{
id: 'gate-check',
when: { type: 'gate-status', gateId: 'any-gate', status: 'pass' },
then: 'inject',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 1,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(false);
});
});
describe('step-type condition', () => {
it('should match when step type matches', () => {
const conditions: InjectionCondition[] = [
{
id: 'type-check',
when: { type: 'step-type', stepType: 'analysis' },
then: 'inject',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 1,
stepType: 'analysis',
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
expect(result.action).toBe('inject');
});
it('should not match when step type differs', () => {
const conditions: InjectionCondition[] = [
{
id: 'type-check',
when: { type: 'step-type', stepType: 'analysis' },
then: 'skip',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 1,
stepType: 'execution',
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(false);
});
});
describe('step-number condition', () => {
it('should match with eq comparison', () => {
const conditions: InjectionCondition[] = [
{
id: 'step-eq',
when: { type: 'step-number', comparison: 'eq', value: 3 },
then: 'inject',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 3,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
});
it('should match with gt comparison', () => {
const conditions: InjectionCondition[] = [
{
id: 'step-gt',
when: { type: 'step-number', comparison: 'gt', value: 2 },
then: 'skip',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 5,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
expect(result.action).toBe('skip');
});
it('should not match when comparison fails', () => {
const conditions: InjectionCondition[] = [
{
id: 'step-lt',
when: { type: 'step-number', comparison: 'lt', value: 3 },
then: 'inject',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 5,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(false);
});
});
describe('previous-step-result condition', () => {
it('should match when previous result matches', () => {
const conditions: InjectionCondition[] = [
{
id: 'prev-success',
when: { type: 'previous-step-result', status: 'success' },
then: 'inject',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 2,
previousStepResult: 'success',
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
});
it('should not match when previous result differs', () => {
const conditions: InjectionCondition[] = [
{
id: 'prev-success',
when: { type: 'previous-step-result', status: 'success' },
then: 'skip',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 2,
previousStepResult: 'failure',
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(false);
});
});
describe('chain-position condition', () => {
it('should match first position', () => {
const conditions: InjectionCondition[] = [
{
id: 'first-step',
when: { type: 'chain-position', position: 'first' },
then: 'inject',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 1,
totalSteps: 5,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
});
it('should match last position', () => {
const conditions: InjectionCondition[] = [
{
id: 'last-step',
when: { type: 'chain-position', position: 'last' },
then: 'skip',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 5,
totalSteps: 5,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
expect(result.action).toBe('skip');
});
it('should match middle position', () => {
const conditions: InjectionCondition[] = [
{
id: 'middle-step',
when: { type: 'chain-position', position: 'middle' },
then: 'inherit',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 3,
totalSteps: 5,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
expect(result.action).toBe('inherit');
});
});
describe('always condition', () => {
it('should always match', () => {
const conditions: InjectionCondition[] = [
{
id: 'always-inject',
when: { type: 'always' },
then: 'inject',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 1,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
expect(result.action).toBe('inject');
});
});
describe('first match wins', () => {
it('should return first matching condition', () => {
const conditions: InjectionCondition[] = [
{
id: 'step-1',
when: { type: 'step-number', comparison: 'eq', value: 1 },
then: 'inject',
reason: 'First step special handling',
},
{
id: 'always-skip',
when: { type: 'always' },
then: 'skip',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 1,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
expect(result.action).toBe('inject');
expect(result.matchedCondition?.id).toBe('step-1');
});
it('should skip to second condition if first does not match', () => {
const conditions: InjectionCondition[] = [
{
id: 'step-1',
when: { type: 'step-number', comparison: 'eq', value: 1 },
then: 'inject',
},
{
id: 'always-skip',
when: { type: 'always' },
then: 'skip',
},
];
const input: InjectionDecisionInput = {
injectionType: 'system-prompt',
currentStep: 3,
};
const result = evaluator.evaluate(conditions, input);
expect(result.matched).toBe(true);
expect(result.action).toBe('skip');
expect(result.matchedCondition?.id).toBe('always-skip');
});
});
});