chore: add ion package, codesight wiki, work plans, ascli config
New @boocode/ion package (v0.0.1) for inference optimization network. .codesight/ wiki artifacts for codebase documentation. .omo/ work plans for openspec cleanup and enhanced file panel.
This commit is contained in:
149
packages/ion/src/engine/__tests__/condition-evaluator.test.ts
Normal file
149
packages/ion/src/engine/__tests__/condition-evaluator.test.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { evaluateCondition, ConditionError } from '../condition-evaluator.js';
|
||||
|
||||
describe('evaluateCondition', () => {
|
||||
describe('simple boolean conditions', () => {
|
||||
it('evaluates boolean true in a comparison', () => {
|
||||
expect(evaluateCondition('true == true', {})).toBe(true);
|
||||
});
|
||||
|
||||
it('evaluates boolean false in a comparison', () => {
|
||||
expect(evaluateCondition('false == false', {})).toBe(true);
|
||||
});
|
||||
|
||||
it('evaluates true != false', () => {
|
||||
expect(evaluateCondition('true != false', {})).toBe(true);
|
||||
});
|
||||
|
||||
it('evaluates boolean via node reference', () => {
|
||||
const outputs = { flag: { output: true } };
|
||||
expect(evaluateCondition('$flag.output == true', outputs)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('string equality with node references', () => {
|
||||
it('evaluates $nodeId.output == "value" as true when matching', () => {
|
||||
const outputs = { analysis: { output: 'done' } };
|
||||
expect(evaluateCondition('$analysis.output == "done"', outputs)).toBe(true);
|
||||
});
|
||||
|
||||
it('evaluates $nodeId.output == "value" as false when not matching', () => {
|
||||
const outputs = { analysis: { output: 'pending' } };
|
||||
expect(evaluateCondition('$analysis.output == "done"', outputs)).toBe(false);
|
||||
});
|
||||
|
||||
it('evaluates $nodeId.output != "value" correctly', () => {
|
||||
const outputs = { analysis: { output: 'pending' } };
|
||||
expect(evaluateCondition('$analysis.output != "done"', outputs)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('numeric comparisons', () => {
|
||||
it('evaluates $score.output > 5 as true', () => {
|
||||
const outputs = { score: { output: 10 } };
|
||||
expect(evaluateCondition('$score.output > 5', outputs)).toBe(true);
|
||||
});
|
||||
|
||||
it('evaluates $score.output > 5 as false when score is lower', () => {
|
||||
const outputs = { score: { output: 3 } };
|
||||
expect(evaluateCondition('$score.output > 5', outputs)).toBe(false);
|
||||
});
|
||||
|
||||
it('evaluates >= comparison', () => {
|
||||
const outputs = { score: { output: 5 } };
|
||||
expect(evaluateCondition('$score.output >= 5', outputs)).toBe(true);
|
||||
});
|
||||
|
||||
it('evaluates < comparison', () => {
|
||||
const outputs = { score: { output: 3 } };
|
||||
expect(evaluateCondition('$score.output < 5', outputs)).toBe(true);
|
||||
});
|
||||
|
||||
it('evaluates <= comparison', () => {
|
||||
const outputs = { score: { output: 5 } };
|
||||
expect(evaluateCondition('$score.output <= 5', outputs)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AND/OR compounds', () => {
|
||||
it('evaluates AND compound: both true', () => {
|
||||
const outputs = { a: { output: 'x' }, b: { output: 'y' } };
|
||||
expect(evaluateCondition('$a.output == "x" AND $b.output == "y"', outputs)).toBe(true);
|
||||
});
|
||||
|
||||
it('evaluates AND compound: one false', () => {
|
||||
const outputs = { a: { output: 'x' }, b: { output: 'z' } };
|
||||
expect(evaluateCondition('$a.output == "x" AND $b.output == "y"', outputs)).toBe(false);
|
||||
});
|
||||
|
||||
it('evaluates OR compound: one true', () => {
|
||||
const outputs = { a: { output: 'x' }, b: { output: 'z' } };
|
||||
expect(evaluateCondition('$a.output == "x" OR $b.output == "y"', outputs)).toBe(true);
|
||||
});
|
||||
|
||||
it('evaluates OR compound: both false', () => {
|
||||
const outputs = { a: { output: 'z' }, b: { output: 'z' } };
|
||||
expect(evaluateCondition('$a.output == "x" OR $b.output == "y"', outputs)).toBe(false);
|
||||
});
|
||||
|
||||
it('evaluates mixed AND/OR with correct precedence', () => {
|
||||
const outputs = { a: { output: 'x' }, b: { output: 'y' }, c: { output: 'z' } };
|
||||
// false AND true OR true => false OR true => true (AND binds tighter)
|
||||
expect(evaluateCondition('$a.output == "wrong" AND $b.output == "y" OR $c.output == "z"', outputs)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parenthesized expressions', () => {
|
||||
it('evaluates parenthesized expressions', () => {
|
||||
const outputs = { a: { output: 'x' }, b: { output: 'y' } };
|
||||
expect(evaluateCondition('($a.output == "x" OR $a.output == "z") AND $b.output == "y"', outputs)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error handling', () => {
|
||||
it('throws ConditionError on invalid expressions', () => {
|
||||
expect(() => evaluateCondition('!!!invalid', {})).toThrow(ConditionError);
|
||||
});
|
||||
|
||||
it('throws ConditionError on missing node reference', () => {
|
||||
expect(() => evaluateCondition('$missing.output == "x"', {})).toThrow(ConditionError);
|
||||
});
|
||||
|
||||
it('throws ConditionError on node reference without field', () => {
|
||||
expect(() => evaluateCondition('$analysis == "x"', {})).toThrow(ConditionError);
|
||||
});
|
||||
|
||||
it('throws ConditionError on unterminated string', () => {
|
||||
expect(() => evaluateCondition('"unterminated', {})).toThrow(ConditionError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('whitespace handling', () => {
|
||||
it('handles extra whitespace around operators', () => {
|
||||
const outputs = { a: { output: 'x' } };
|
||||
expect(evaluateCondition(' $a.output == "x" ', outputs)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('quoted strings with special characters', () => {
|
||||
it('handles double-quoted strings with spaces', () => {
|
||||
const outputs = { msg: { output: 'hello world' } };
|
||||
expect(evaluateCondition('$msg.output == "hello world"', outputs)).toBe(true);
|
||||
});
|
||||
|
||||
it('handles single-quoted strings', () => {
|
||||
const outputs = { msg: { output: 'hello' } };
|
||||
expect(evaluateCondition("$msg.output == 'hello'", outputs)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('empty condition', () => {
|
||||
it('returns true for empty string', () => {
|
||||
expect(evaluateCondition('', {})).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for whitespace-only string', () => {
|
||||
expect(evaluateCondition(' ', {})).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user