import { test } from 'node:test'; import assert from 'node:assert/strict'; import { evaluate, check, pathsUsed } from '../src/engine/conditions.js'; const state = { money: 100, turn: 12, skills: { talking: 3 }, flags: { in_debt: false }, notable: ['promoted'], seen: { first_day: 1 }, }; test('compares numbers', () => { assert.equal(evaluate(state, { path: 'money', op: '>=', value: 100 }), true); assert.equal(evaluate(state, { path: 'money', op: '>', value: 100 }), false); assert.equal(evaluate(state, { path: 'skills.talking', op: '<', value: 5 }), true); }); test('compares booleans and equality', () => { assert.equal(evaluate(state, { path: 'flags.in_debt', op: '==', value: false }), true); assert.equal(evaluate(state, { path: 'flags.in_debt', op: '!=', value: true }), true); }); test('array membership works both directions', () => { assert.equal(evaluate(state, { path: 'notable', op: 'has', value: 'promoted' }), true); assert.equal(evaluate(state, { path: 'notable', op: 'not-has', value: 'fired' }), true); assert.equal(evaluate(state, { path: 'skills.talking', op: 'in', value: [1, 2, 3] }), true); assert.equal(evaluate(state, { path: 'skills.talking', op: 'not-in', value: [9] }), true); }); test('ordering against a missing path is false, not a throw', () => { assert.equal(evaluate(state, { path: 'skills.welding', op: '>=', value: 1 }), false); assert.equal(evaluate(state, { path: 'skills.welding', op: '<', value: 1 }), false); }); test('content can read engine bookkeeping', () => { assert.equal(evaluate(state, { path: 'turn', op: '>=', value: 10 }), true); assert.equal(evaluate(state, { path: 'seen.first_day', op: '>=', value: 1 }), true); assert.equal(evaluate(state, { path: 'seen.never_fired', op: '>=', value: 1 }), false); }); test('combinators nest', () => { const condition = { all: [ { path: 'money', op: '>', value: 0 }, { any: [{ path: 'skills.talking', op: '>=', value: 3 }, { path: 'flags.in_debt', op: '==', value: true }] }, { not: { path: 'notable', op: 'has', value: 'fired' } }, ], }; assert.equal(evaluate(state, condition), true); assert.equal(evaluate({ ...state, money: -1 }, condition), false); }); test('check reports which conditions failed', () => { const result = check(state, [ { path: 'money', op: '>=', value: 100 }, { path: 'skills.talking', op: '>=', value: 9 }, ]); assert.equal(result.passed, false); assert.equal(result.failed.length, 1); assert.equal(result.failed[0].path, 'skills.talking'); }); test('an absent gate passes', () => { assert.equal(check(state, undefined).passed, true); assert.equal(check(state, []).passed, true); }); test('an unknown operator is a loud failure', () => { assert.throws(() => evaluate(state, { path: 'money', op: '=~', value: 1 }), /unknown condition operator/); }); test('pathsUsed walks the whole tree', () => { const paths = pathsUsed({ all: [{ path: 'money', op: '>', value: 0 }, { not: { path: 'flags.in_debt', op: '==', value: true } }], }); assert.deepEqual(paths.sort(), ['flags.in_debt', 'money']); });