From 12d6e80f9153280a6d4cb998390524c28d367a61 Mon Sep 17 00:00:00 2001 From: regevbr Date: Tue, 2 Mar 2021 18:57:47 +0200 Subject: [PATCH] added validation method --- README.md | 3 +- package.json | 2 +- src/index.ts | 126 ++++++++++---- src/test/index.spec.ts | 374 +++++++++++++++++++++++++++++------------ 4 files changed, 366 insertions(+), 139 deletions(-) diff --git a/README.md b/README.md index 1a659e4..44c5578 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ yarn add json-expression-eval *Please see tests and examples dir for more usages and example (under /src)* ```typescript -import { Expression, evaluate } from 'json-expression-eval'; +import { Expression, evaluate, validate } from 'json-expression-eval'; interface IExampleContext { userId: string; @@ -62,6 +62,7 @@ const expression: Expression = { }, ], }; +validate(expression, exampleContext, functionsTable); // Should not throw console.log(evaluate(expression, exampleContext, functionsTable)); // true ``` diff --git a/package.json b/package.json index ffe4253..7ea0731 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-expression-eval", - "version": "3.0.1", + "version": "3.1.0", "description": "evaluate a json described boolean expression using dynamic functions", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/index.ts b/src/index.ts index 73f76bd..601b7c8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -116,11 +116,15 @@ const _isObject = (obj: any) => { return type === 'function' || type === 'object' && !!obj; }; -const evaluateCompareOp = , K extends keyof RequireOnlyOne>>( - op: RequireOnlyOne>, key: K, param: any): boolean => { +function evaluateCompareOp, K extends keyof RequireOnlyOne>>( + op: RequireOnlyOne>, key: K, param: any, validation: false): boolean; +function evaluateCompareOp, K extends keyof RequireOnlyOne>>( + op: RequireOnlyOne>, key: K, param: any, validation: true): void; +function evaluateCompareOp, K extends keyof RequireOnlyOne>>( + op: RequireOnlyOne>, key: K, param: any, validation: boolean): boolean | void { const value = op[key]; if (!_isObject(value)) { - return param === value; + return validation ? undefined : param === value; } const keys = Object.keys(value); if (keys.length !== 1) { @@ -128,66 +132,124 @@ const evaluateCompareOp = , K extends keyof Requi } const valueKey = keys[0]; if (isGtCompareOp(value)) { - return param > value.gt; + return validation ? undefined : param > value.gt; } else if (isGteCompareOp(value)) { - return param >= value.gte; + return validation ? undefined : param >= value.gte; } else if (isLteCompareOp(value)) { - return param <= value.lte; + return validation ? undefined : param <= value.lte; } else if (isLtCompareOp(value)) { - return param < value.lt; + return validation ? undefined : param < value.lt; } else if (isEqualCompareOp(value)) { - return param === value.eq; + return validation ? undefined : param === value.eq; } else if (isNotEqualCompareOp(value)) { - return param !== value.neq; + return validation ? undefined : param !== value.neq; } else if (_isObject(param) && valueKey in param) { - return evaluateCompareOp(value, valueKey, param[valueKey]); + if (validation) { + evaluateCompareOp(value, valueKey, param[valueKey], true); + return; + } else { + return evaluateCompareOp(value, valueKey, param[valueKey], false); + } } throw new Error(`Invalid expression - unknown op ${valueKey}`); -}; +} -const handleAndOp = >(andExpression: Expression[], context: C, - functionsTable: F): boolean => { +function handleAndOp>(andExpression: Expression[], context: C, + functionsTable: F, validation: false): boolean; +function handleAndOp>(andExpression: Expression[], context: C, + functionsTable: F, validation: true): void; +function handleAndOp>(andExpression: Expression[], context: C, + functionsTable: F, validation: boolean) + : boolean | void { if (andExpression.length === 0) { throw new Error('Invalid expression - and operator must have at least one expression'); } - for (const currExpression of andExpression) { - if (!evaluate(currExpression, context, functionsTable)) { - return false + if (validation) { + andExpression.forEach((currExpression) => validate(currExpression, context, functionsTable)); + } else { + for (const currExpression of andExpression) { + if (!evaluate(currExpression, context, functionsTable)) { + return false + } } + return true; } - return true; -}; +} -const handleOrOp = >(orExpression: Expression[], context: C, - functionsTable: F): boolean => { +function handleOrOp>(orExpression: Expression[], context: C, + functionsTable: F, validation: false): boolean; +function handleOrOp>(orExpression: Expression[], context: C, + functionsTable: F, validation: true): void; +function handleOrOp>(orExpression: Expression[], context: C, + functionsTable: F, validation: boolean) + : boolean | void { if (orExpression.length === 0) { throw new Error('Invalid expression - or operator must have at least one expression'); } - for (const currExpression of orExpression) { - if (evaluate(currExpression, context, functionsTable)) { - return true + if (validation) { + orExpression.forEach((currExpression) => validate(currExpression, context, functionsTable)); + } else { + for (const currExpression of orExpression) { + if (evaluate(currExpression, context, functionsTable)) { + return true + } } + return false; } - return false; -}; +} -export const evaluate = >(expression: Expression, context: C, - functionsTable: F): boolean => { +function _run>(expression: Expression, context: C, + functionsTable: F, validation: false): boolean; +function _run>(expression: Expression, context: C, + functionsTable: F, validation: true): void; +function _run>(expression: Expression, context: C, + functionsTable: F, validation: boolean): boolean | void { const keys = Object.keys(expression); if (keys.length !== 1) { throw new Error('Invalid expression - too may keys'); } const key = keys[0]; if (isAndCompareOp(expression)) { - return handleAndOp(expression.and, context, functionsTable); + if (validation) { + handleAndOp(expression.and, context, functionsTable, true); + return; + } else { + return handleAndOp(expression.and, context, functionsTable, false); + } } else if (isOrCompareOp(expression)) { - return handleOrOp(expression.or, context, functionsTable); + if (validation) { + handleOrOp(expression.or, context, functionsTable, true); + return; + } else { + return handleOrOp(expression.or, context, functionsTable, false); + } } else if (isNotCompareOp(expression)) { - return !evaluate(expression.not, context, functionsTable); + if (validation) { + _run(expression.not, context, functionsTable, true); + return; + } else { + return !_run(expression.not, context, functionsTable, false); + } } else if (key in functionsTable) { - return functionsTable[key](expression[key], context); + return validation ? undefined : functionsTable[key](expression[key], context); } else if (key in context) { - return evaluateCompareOp(expression, key, context[key]); + if (validation) { + evaluateCompareOp(expression, key, context[key], true) + return; + } else { + return evaluateCompareOp(expression, key, context[key], false) + } } throw new Error(`Invalid expression - unknown function ${key}`); +} + +export const evaluate = >(expression: Expression, context: C, + functionsTable: F): boolean => { + return _run(expression, context, functionsTable, false); +}; + +// Throws in case of validation error. Does not run functions or compare fields +export const validate = >(expression: Expression, dummyContext: C, + functionsTable: F): void => { + _run(expression, dummyContext, functionsTable, true); }; diff --git a/src/test/index.spec.ts b/src/test/index.spec.ts index 51e3236..1f34970 100644 --- a/src/test/index.spec.ts +++ b/src/test/index.spec.ts @@ -1,7 +1,7 @@ 'use strict'; import {expect} from 'chai'; -import {evaluate} from '../'; +import {evaluate, validate} from '../'; const functionsTable = { user: (user: string, context: { userId: string }): boolean => { @@ -13,22 +13,26 @@ type ExpressionFunction = typeof functionsTable; describe('evaluator', () => { it('should evaluate short eq compare op true to on nested properties', () => { - expect(evaluate({ + const expression = { nested: { value: 7, }, - }, { + }; + const context = { timesCounter: 5, userId: 'a', nested: { value2: 9, value: 7, }, - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate short eq compare op true to on deeply nested properties', () => { - expect(evaluate({ + const expression = { or: [ { nested: { @@ -43,7 +47,8 @@ describe('evaluator', () => { }, }, ], - }, { + }; + const context = { timesCounter: 5, userId: 'a', nested: { @@ -52,48 +57,73 @@ describe('evaluator', () => { value: 7, }, }, - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate short eq compare op to true', () => { - expect(evaluate({timesCounter: 5}, { + const expression = { + timesCounter: 5, + }; + const context = { timesCounter: 5, userId: 'a', - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate short eq compare op to false', () => { - expect(evaluate({timesCounter: 5}, { + const expression = { + timesCounter: 5, + }; + const context = { timesCounter: 7, userId: 'a', - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate eq compare op to true', () => { - expect(evaluate({timesCounter: {eq: 5}}, { + const expression = { + timesCounter: {eq: 5}, + }; + const context = { timesCounter: 5, userId: 'a', - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate eq compare op to true on nested properties', () => { - expect(evaluate({ + const expression = { nested: { value: { eq: 7, }, }, - }, { + }; + const context = { timesCounter: 5, userId: 'a', nested: { value: 7, }, - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate eq compare op true to on deeply nested properties', () => { - expect(evaluate({ + const expression = { or: [ { nested: { @@ -112,7 +142,8 @@ describe('evaluator', () => { }, }, ], - }, { + }; + const context = { timesCounter: 5, userId: 'a', nested: { @@ -121,221 +152,345 @@ describe('evaluator', () => { value: 7, }, }, - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate eq compare op to false', () => { - expect(evaluate({timesCounter: {eq: 5}}, { + const expression = { + timesCounter: {eq: 5}, + }; + const context = { timesCounter: 7, userId: 'a', - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate neq compare op to true', () => { - expect(evaluate({timesCounter: {neq: 5}}, { + const expression = { + timesCounter: {neq: 5}, + }; + const context = { timesCounter: 8, userId: 'a', - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate eq compare op to false', () => { - expect(evaluate({timesCounter: {neq: 5}}, { + const expression = { + timesCounter: {neq: 5}, + }; + const context = { timesCounter: 5, userId: 'a', - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate gt compare op to true', () => { - expect(evaluate({timesCounter: {gt: 5}}, { + const expression = { + timesCounter: {gt: 5}, + }; + const context = { timesCounter: 8, userId: 'a', - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate gt compare op to false', () => { - expect(evaluate({timesCounter: {gt: 5}}, { + const expression = { + timesCounter: {gt: 5}, + }; + const context = { timesCounter: 3, userId: 'a', - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate gt compare op to false 2', () => { - expect(evaluate({timesCounter: {gt: 5}}, { + const expression = { + timesCounter: {gt: 5}, + }; + const context = { timesCounter: 5, userId: 'a', - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate gte compare op to true', () => { - expect(evaluate({timesCounter: {gte: 5}}, { + const expression = { + timesCounter: {gte: 5}, + }; + const context = { timesCounter: 8, userId: 'a', - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate gte compare op to true 2', () => { - expect(evaluate({timesCounter: {gte: 5}}, { + const expression = { + timesCounter: {gte: 5}, + }; + const context = { timesCounter: 5, userId: 'a', - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate gte compare op to false', () => { - expect(evaluate({timesCounter: {gte: 5}}, { + const expression = { + timesCounter: {gte: 5}, + }; + const context = { timesCounter: 3, userId: 'a', - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate lt compare op to true', () => { - expect(evaluate({timesCounter: {lt: 5}}, { + const expression = { + timesCounter: {lt: 5}, + }; + const context = { timesCounter: 3, userId: 'a', - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate gt compare lt to false', () => { - expect(evaluate({timesCounter: {lt: 5}}, { + const expression = { + timesCounter: {lt: 5}, + }; + const context = { timesCounter: 8, userId: 'a', - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate lt compare op to false 2', () => { - expect(evaluate({timesCounter: {lt: 5}}, { + const expression = { + timesCounter: {lt: 5}, + }; + const context = { timesCounter: 5, userId: 'a', - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate lte compare op to true', () => { - expect(evaluate({timesCounter: {lte: 5}}, { + const expression = { + timesCounter: {lte: 5}, + }; + const context = { timesCounter: 3, userId: 'a', - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + ; + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate lte compare op to true 2', () => { - expect(evaluate({timesCounter: {lte: 5}}, { + const expression = { + timesCounter: {lte: 5}, + }; + const context = { timesCounter: 5, userId: 'a', - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate lte compare op to false', () => { - expect(evaluate({timesCounter: {lte: 5}}, { + const expression = { + timesCounter: {lte: 5}, + }; + const context = { timesCounter: 8, userId: 'a', - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate a single function', () => { - expect(evaluate<{ userId: string, timesCounter: number }, ExpressionFunction>({user: 'r@a.com'}, { + const expression = {user: 'r@a.com'}; + const context = { userId: 'r@a.com', timesCounter: 8, - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate a single not function to false', () => { - expect(evaluate<{ userId: string, timesCounter: number }, ExpressionFunction>({not: {user: 'r@a.com'}}, { + const expression = {not: {user: 'r@a.com'}}; + const context = { userId: 'r@a.com', timesCounter: 8, - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate a single not function to true', () => { - expect(evaluate<{ userId: string, timesCounter: number }, ExpressionFunction>({not: {user: 'a@a.com'}}, { + const expression = {not: {user: 'a@a.com'}}; + const context = { userId: 'r@a.com', timesCounter: 8, - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate a single and function to true', () => { - expect(evaluate<{ userId: string, timesCounter: number }, ExpressionFunction>({and: [{user: 'r@a.com'}]}, { + const expression = {and: [{user: 'r@a.com'}]}; + const context = { userId: 'r@a.com', timesCounter: 8, - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate a single or function to true', () => { - expect(evaluate<{ userId: string, timesCounter: number }, ExpressionFunction>({or: [{user: 'r@a.com'}]}, { + const expression = {or: [{user: 'r@a.com'}]}; + const context = { userId: 'r@a.com', timesCounter: 8, - }, functionsTable)).to.eql(true); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + expect(evaluate(expression, context, functionsTable)).to.eql(true); }); it('should evaluate a single and function to false', () => { - expect(evaluate<{ userId: string, timesCounter: number }, ExpressionFunction>({and: [{user: 't@a.com'}]}, { + const expression = {and: [{user: 't@a.com'}]}; + const context = { userId: 'r@a.com', timesCounter: 8, - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should evaluate a single or function to false', () => { - expect(evaluate<{ userId: string, timesCounter: number }, ExpressionFunction>({or: [{user: 't@a.com'}]}, { + const expression = {or: [{user: 't@a.com'}]}; + const context = { userId: 'r@a.com', timesCounter: 8, - }, functionsTable)).to.eql(false); + }; + expect(validate(expression, context, functionsTable)).to.be.an('undefined'); + expect(evaluate(expression, context, functionsTable)).to.eql(false); }); it('should fail on empty or op', () => { - expect(() => { - evaluate({or: []}, {userId: 'r@a.com', timesCounter: 8}, functionsTable); - }).to.throw('Invalid expression - or operator must have at least one expression'); + const expression = {or: []}; + const context = {userId: 'r@a.com', timesCounter: 8}; + expect(() => validate(expression, context, functionsTable)) + .to.throw('Invalid expression - or operator must have at least one expression'); + expect(() => evaluate(expression, context, functionsTable)) + .to.throw('Invalid expression - or operator must have at least one expression'); }); it('should fail on empty and op', () => { - expect(() => { - evaluate({and: []}, {userId: 'r@a.com', timesCounter: 8}, functionsTable); - }).to.throw('Invalid expression - and operator must have at least one expression'); + const expression = {and: []}; + const context = {userId: 'r@a.com', timesCounter: 8}; + expect(() => validate(expression, context, functionsTable)) + .to.throw('Invalid expression - and operator must have at least one expression'); + expect(() => evaluate(expression, context, functionsTable)) + .to.throw('Invalid expression - and operator must have at least one expression'); }); it('should fail on non existing function', () => { - expect(() => { - // @ts-ignore - evaluate({and: [{dummy: 1}]}, {userId: 'r@a.com', timesCounter: 8}, functionsTable); - }).to.throw('Invalid expression - unknown function dummy'); + const expression: any = {and: [{dummy: 1}]}; + const context = {userId: 'r@a.com', timesCounter: 8}; + expect(() => validate(expression, context, functionsTable)) + .to.throw('Invalid expression - unknown function dummy'); + expect(() => evaluate(expression, context, functionsTable)) + .to.throw('Invalid expression - unknown function dummy'); }); it('should fail on non existing function 2', () => { - expect(() => { - // @ts-ignore - evaluate({dummy: 1}, {userId: 'r@a.com', timesCounter: 8}, functionsTable); - }).to.throw('Invalid expression - unknown function dummy'); + const expression: any = {dummy: 1}; + const context = {userId: 'r@a.com', timesCounter: 8}; + expect(() => validate(expression, context, functionsTable)) + .to.throw('Invalid expression - unknown function dummy'); + expect(() => evaluate(expression, context, functionsTable)) + .to.throw('Invalid expression - unknown function dummy'); }); it('should fail on non existing op', () => { - expect(() => { - // @ts-ignore - evaluate({userId: {bk: 1}}, {userId: 'r@a.com', timesCounter: 8}, functionsTable); - }).to.throw('Invalid expression - unknown op bk'); + const expression: any = {userId: {bk: 1}}; + const context = {userId: 'r@a.com', timesCounter: 8}; + expect(() => validate(expression, context, functionsTable)) + .to.throw('Invalid expression - unknown op bk'); + expect(() => evaluate(expression, context, functionsTable)) + .to.throw('Invalid expression - unknown op bk'); }); it('should fail too many keys to op', () => { - expect(() => { - // @ts-ignore - evaluate({userId: {eq: 1, nq: 2}}, { - userId: 'r@a.com', - timesCounter: 8, - }, functionsTable); - }).to.throw('Invalid expression - too may keys'); + const expression: any = {userId: {eq: 1, nq: 2}}; + const context = {userId: 'r@a.com', timesCounter: 8}; + expect(() => validate(expression, context, functionsTable)) + .to.throw('Invalid expression - too may keys'); + expect(() => evaluate(expression, context, functionsTable)) + .to.throw('Invalid expression - too may keys'); }); it('should fail too many keys', () => { - expect(() => { - // @ts-ignore - evaluate({dummy: 1, user: 'a'}, {userId: 'r@a.com', timesCounter: 8}, functionsTable); - }).to.throw('Invalid expression - too may keys'); + const expression: any = {timesCounter: 1, userId: 'a'}; + const context = {userId: 'r@a.com', timesCounter: 8}; + expect(() => validate(expression, context, functionsTable)) + .to.throw('Invalid expression - too may keys'); + expect(() => evaluate(expression, context, functionsTable)) + .to.throw('Invalid expression - too may keys'); }); it('should fail too many keys 2', () => { - expect(() => { - // @ts-ignore - evaluate({or: [{dummy: 1, user: 'a'}]}, { - userId: 'r@a.com', - timesCounter: 8, - }, functionsTable); - }).to.throw('Invalid expression - too may keys'); + const expression: any = {or: [{userId: 1, timesCounter: 'a'}]}; + const context = {userId: 'r@a.com', timesCounter: 8}; + expect(() => validate(expression, context, functionsTable)) + .to.throw('Invalid expression - too may keys'); + expect(() => evaluate(expression, context, functionsTable)) + .to.throw('Invalid expression - too may keys'); }); describe('short circuit', () => { @@ -347,7 +502,8 @@ describe('evaluator', () => { return arg }, }; - evaluate({or: [ + const expression = { + or: [ { eval: false, }, @@ -360,8 +516,11 @@ describe('evaluator', () => { { eval: false, }, - ]}, {}, fnTable); - + ], + }; + validate(expression, context, fnTable); + expect(fnCounter).to.eql(0); + evaluate(expression, context, fnTable); expect(fnCounter).to.eql(3); }); @@ -373,7 +532,8 @@ describe('evaluator', () => { return arg }, }; - evaluate({and: [ + const expression = { + and: [ { eval: true, }, @@ -386,8 +546,12 @@ describe('evaluator', () => { { eval: true, }, - ]}, {}, fnTable); - + ], + }; + const context = {}; + validate(expression, context, fnTable); + expect(fnCounter).to.eql(0); + evaluate(expression, context, fnTable); expect(fnCounter).to.eql(3); }); });