the-rule-engine
Version:
⚙️ A small fluent DSL for conditional logic (validation, gating, etc)
189 lines (158 loc) • 4.53 kB
JavaScript
// test/ruleEngine.test.js
import { createRuleEngine } from '../src/index.js';
console.log('✅ Running RuleEngine Tests...');
// Helper
function runTest(description, fn) {
try {
const result = fn();
console.log(`✅ ${description}: ${result}`);
} catch (err) {
console.error(`❌ ${description}:`, err.message);
}
}
//
// 1. Basic `when` logic
//
runTest('Simple .when() pass', () => {
const engine = createRuleEngine()
.when(u => u.age >= 18)
.then(() => 'Allowed')
.otherwise(() => 'Blocked');
return engine.evaluate({ age: 20 }) === 'Allowed';
});
runTest('Simple .when() fail', () => {
const engine = createRuleEngine()
.when(u => u.age >= 18)
.then(() => 'Allowed')
.otherwise(() => 'Blocked');
return engine.evaluate({ age: 15 }) === 'Blocked';
});
//
// 2. Using .and() and .or()
//
runTest('.when() + .and()', () => {
const engine = createRuleEngine()
.when(u => u.age >= 18)
.and(u => u.country === 'US')
.then(() => 'Welcome')
.otherwise(() => 'Denied');
return engine.evaluate({ age: 19, country: 'US' }) === 'Welcome';
});
runTest('.when() + .and() fail', () => {
const engine = createRuleEngine()
.when(u => u.age >= 18)
.and(u => u.country === 'US')
.then(() => 'Welcome')
.otherwise(() => 'Denied');
return engine.evaluate({ age: 19, country: 'CA' }) === 'Denied';
});
runTest('.or() logic', () => {
const engine = createRuleEngine()
.when(u => u.age >= 18)
.or(u => u.isAdmin)
.then(() => 'Access')
.otherwise(() => 'No Access');
return engine.evaluate({ age: 16, isAdmin: true }) === 'Access';
});
//
// 3. Group logic
//
runTest('.group() for compound AND', () => {
const engine = createRuleEngine()
.group(g => g
.when(u => u.role === 'editor')
.and(u => u.active === true)
)
.then(() => 'OK')
.otherwise(() => 'Blocked');
return engine.evaluate({ role: 'editor', active: true }) === 'OK';
});
runTest('.orGroup()', () => {
const engine = createRuleEngine()
.when(u => u.age >= 21)
.orGroup(g => g
.when(u => u.role === 'vip')
.and(u => u.premium === true)
)
.then(() => 'Granted')
.otherwise(() => 'Denied');
return engine.evaluate({ role: 'vip', premium: true }) === 'Granted';
});
runTest('.andGroup()', () => {
const engine = createRuleEngine()
.when(u => u.age >= 18)
.andGroup(g => g
.when(u => u.verified)
.and(u => u.subscribed)
)
.then(() => 'Welcome')
.otherwise(() => 'Denied');
return engine.evaluate({ age: 20, verified: true, subscribed: true }) === 'Welcome';
});
//
// 4. Negated group
//
runTest('.notGroup() blocks banned users', () => {
const engine = createRuleEngine()
.when(u => u.role === 'user')
.notGroup(g =>
g.when(u => u.banned).or(u => u.inactive)
)
.then(() => 'Accepted')
.otherwise(() => 'Rejected');
return engine.evaluate({ role: 'user', banned: false, inactive: false }) === 'Accepted'
&& engine.evaluate({ role: 'user', banned: true }) === 'Rejected';
});
//
// 5. Nested ruleEngines
//
runTest('.or() with sub-engine', () => {
const sub = createRuleEngine()
.when(u => u.status === 'gold')
.and(u => u.spending > 1000);
const engine = createRuleEngine()
.when(u => u.age > 25)
.or(sub)
.then(() => 'VIP')
.otherwise(() => 'Standard');
return engine.evaluate({ age: 30 }) === 'VIP'
&& engine.evaluate({ status: 'gold', spending: 1500 }) === 'VIP';
});
//
// 6. .not() with function
//
runTest('.not() with simple rule', () => {
const engine = createRuleEngine()
.when(u => u.active)
.and(u => u.verified)
.not(u => u.suspended)
.then(() => 'Good')
.otherwise(() => 'Bad');
return engine.evaluate({ active: true, verified: true, suspended: false }) === 'Good';
});
//
// 7. .evaluateLogicOnly()
//
runTest('evaluateLogicOnly returns boolean', () => {
const engine = createRuleEngine()
.when(u => u.enabled)
.and(u => u.subscribed);
return engine.evaluateLogicOnly({ enabled: true, subscribed: true }) === true;
});
//
// 8. Edge cases
//
runTest('No then/otherwise returns null', () => {
const engine = createRuleEngine().when(() => true);
return engine.evaluate({}) === null;
});
runTest('Throws on invalid condition', () => {
let threw = false;
try {
createRuleEngine().when('not a function');
} catch {
threw = true;
}
return threw;
});
console.log('\n✅ All RuleEngine tests completed.\n');