Browse Source

Add data condition variable (#6239)

* Add data condition variable

* Format

---------

Co-authored-by: Artur Arseniev <artur.catch@hotmail.it>
revert-6247-release-docs-v0.21.14
mohamed yahia 2 years ago
committed by GitHub
parent
commit
5d5ff04c72
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 13
      packages/core/src/data_sources/model/conditional_variables/ConditionStatement.ts
  2. 50
      packages/core/src/data_sources/model/conditional_variables/DataCondition.ts
  3. 15
      packages/core/src/data_sources/model/conditional_variables/LogicalGroupStatement.ts
  4. 53
      packages/core/src/data_sources/model/conditional_variables/evaluateCondition.ts
  5. 54
      packages/core/src/data_sources/model/conditional_variables/operators/GenericOperator.ts
  6. 28
      packages/core/src/data_sources/model/conditional_variables/operators/LogicalOperator.ts
  7. 35
      packages/core/src/data_sources/model/conditional_variables/operators/NumberOperator.ts
  8. 29
      packages/core/src/data_sources/model/conditional_variables/operators/StringOperations.ts
  9. 3
      packages/core/src/data_sources/model/conditional_variables/operators/index.ts
  10. 174
      packages/core/test/specs/data_sources/model/conditional_variables/DataCondition.ts
  11. 150
      packages/core/test/specs/data_sources/model/conditional_variables/operators/GenericOperator.ts
  12. 59
      packages/core/test/specs/data_sources/model/conditional_variables/operators/LogicalOperator.ts
  13. 95
      packages/core/test/specs/data_sources/model/conditional_variables/operators/NumberOperator.ts
  14. 81
      packages/core/test/specs/data_sources/model/conditional_variables/operators/StringOperator.ts

13
packages/core/src/data_sources/model/conditional_variables/ConditionStatement.ts

@ -0,0 +1,13 @@
import { Operator } from './operators';
export class ConditionStatement {
constructor(
private leftValue: any,
private operator: Operator,
private rightValue: any,
) {}
evaluate(): boolean {
return this.operator.evaluate(this.leftValue, this.rightValue);
}
}

50
packages/core/src/data_sources/model/conditional_variables/DataCondition.ts

@ -0,0 +1,50 @@
import { NumberOperation } from './operators/NumberOperator';
import { StringOperation } from './operators/StringOperations';
import { GenericOperation } from './operators/GenericOperator';
import { Model } from '../../../common';
import { LogicalOperation } from './operators/LogicalOperator';
import { evaluateCondition } from './evaluateCondition';
export const ConditionalVariableType = 'conditional-variable';
export type Expression = {
left: any;
operator: GenericOperation | StringOperation | NumberOperation;
right: any;
};
export type LogicGroup = {
logicalOperator: LogicalOperation;
statements: (Expression | LogicGroup | boolean)[];
};
export class DataCondition extends Model {
private conditionResult: boolean;
defaults() {
return {
type: ConditionalVariableType,
condition: false,
};
}
constructor(
private condition: Expression | LogicGroup | boolean,
private ifTrue: any,
private ifFalse: any,
) {
super();
this.conditionResult = this.evaluate();
}
evaluate() {
return evaluateCondition(this.condition);
}
getDataValue(): any {
return this.conditionResult ? this.ifTrue : this.ifFalse;
}
reevaluate(): void {
this.conditionResult = this.evaluate();
}
}

15
packages/core/src/data_sources/model/conditional_variables/LogicalGroupStatement.ts

@ -0,0 +1,15 @@
import { LogicalOperator } from './operators/LogicalOperator';
import { Expression, LogicGroup } from './DataCondition';
import { evaluateCondition } from './evaluateCondition';
export class LogicalGroupStatement {
constructor(
private operator: LogicalOperator,
private statements: (Expression | LogicGroup | boolean)[],
) {}
evaluate(): boolean {
const results = this.statements.map((statement) => evaluateCondition(statement));
return this.operator.evaluate(results);
}
}

53
packages/core/src/data_sources/model/conditional_variables/evaluateCondition.ts

@ -0,0 +1,53 @@
import { ConditionStatement } from './ConditionStatement';
import { Expression, LogicGroup } from './DataCondition';
import { LogicalGroupStatement } from './LogicalGroupStatement';
import { Operator } from './operators';
import { GenericOperation, GenericOperator } from './operators/GenericOperator';
import { LogicalOperator } from './operators/LogicalOperator';
import { NumberOperation, NumberOperator } from './operators/NumberOperator';
import { StringOperation, StringOperator } from './operators/StringOperations';
export function evaluateCondition(condition: any): boolean {
if (typeof condition === 'boolean') {
return condition;
}
if (isLogicGroup(condition)) {
const { logicalOperator, statements } = condition;
const op = new LogicalOperator(logicalOperator);
const logicalGroup = new LogicalGroupStatement(op, statements);
return logicalGroup.evaluate();
}
if (isCondition(condition)) {
const { left, operator, right } = condition;
const op = operatorFactory(left, operator);
const statement = new ConditionStatement(left, op, right);
return statement.evaluate();
}
throw new Error('Invalid condition type.');
}
function operatorFactory(left: any, operator: string): Operator {
if (isOperatorInEnum(operator, GenericOperation)) {
return new GenericOperator(operator as GenericOperation);
} else if (typeof left === 'number') {
return new NumberOperator(operator as NumberOperation);
} else if (typeof left === 'string') {
return new StringOperator(operator as StringOperation);
}
throw new Error(`Unsupported data type: ${typeof left}`);
}
function isOperatorInEnum(operator: string, enumObject: any): boolean {
return Object.values(enumObject).includes(operator);
}
function isLogicGroup(condition: any): condition is LogicGroup {
return condition && typeof condition.logicalOperator !== 'undefined' && Array.isArray(condition.statements);
}
function isCondition(condition: any): condition is Expression {
return condition && typeof condition.left !== 'undefined' && typeof condition.operator === 'string';
}

54
packages/core/src/data_sources/model/conditional_variables/operators/GenericOperator.ts

@ -0,0 +1,54 @@
import DataVariable from '../../DataVariable';
import { Operator } from '.';
export enum GenericOperation {
equals = 'equals',
isTruthy = 'isTruthy',
isFalsy = 'isFalsy',
isDefined = 'isDefined',
isNull = 'isNull',
isUndefined = 'isUndefined',
isArray = 'isArray',
isObject = 'isObject',
isString = 'isString',
isNumber = 'isNumber',
isBoolean = 'isBoolean',
isDefaultValue = 'isDefaultValue', // For Datasource variables
}
export class GenericOperator extends Operator {
constructor(private operator: GenericOperation) {
super();
}
evaluate(left: any, right: any): boolean {
switch (this.operator) {
case 'equals':
return left === right;
case 'isTruthy':
return !!left;
case 'isFalsy':
return !left;
case 'isDefined':
return left !== undefined && left !== null;
case 'isNull':
return left === null;
case 'isUndefined':
return left === undefined;
case 'isArray':
return Array.isArray(left);
case 'isObject':
return typeof left === 'object' && left !== null;
case 'isString':
return typeof left === 'string';
case 'isNumber':
return typeof left === 'number';
case 'isBoolean':
return typeof left === 'boolean';
case 'isDefaultValue':
return left instanceof DataVariable && left.get('default') === right;
default:
throw new Error(`Unsupported generic operator: ${this.operator}`);
}
}
}

28
packages/core/src/data_sources/model/conditional_variables/operators/LogicalOperator.ts

@ -0,0 +1,28 @@
import { Operator } from '.';
export enum LogicalOperation {
and = 'and',
or = 'or',
xor = 'xor',
}
export class LogicalOperator extends Operator {
constructor(private operator: LogicalOperation) {
super();
}
evaluate(statements: boolean[]): boolean {
if (!statements.length) throw new Error('Expected one or more statments, got none');
switch (this.operator) {
case LogicalOperation.and:
return statements.every(Boolean);
case LogicalOperation.or:
return statements.some(Boolean);
case LogicalOperation.xor:
return statements.filter(Boolean).length === 1;
default:
throw new Error(`Unsupported logical operator: ${this.operator}`);
}
}
}

35
packages/core/src/data_sources/model/conditional_variables/operators/NumberOperator.ts

@ -0,0 +1,35 @@
import { Operator } from '.';
export enum NumberOperation {
greaterThan = '>',
lessThan = '<',
greaterThanOrEqual = '>=',
lessThanOrEqual = '<=',
equals = '=',
notEquals = '!=',
}
export class NumberOperator extends Operator {
constructor(private operator: NumberOperation) {
super();
}
evaluate(left: number, right: number): boolean {
switch (this.operator) {
case NumberOperation.greaterThan:
return left > right;
case NumberOperation.lessThan:
return left < right;
case NumberOperation.greaterThanOrEqual:
return left >= right;
case NumberOperation.lessThanOrEqual:
return left <= right;
case NumberOperation.equals:
return left === right;
case NumberOperation.notEquals:
return left !== right;
default:
throw new Error(`Unsupported number operator: ${this.operator}`);
}
}
}

29
packages/core/src/data_sources/model/conditional_variables/operators/StringOperations.ts

@ -0,0 +1,29 @@
import { Operator } from '.';
export enum StringOperation {
contains = 'contains',
startsWith = 'startsWith',
endsWith = 'endsWith',
matchesRegex = 'matchesRegex',
}
export class StringOperator extends Operator {
constructor(private operator: StringOperation) {
super();
}
evaluate(left: string, right: string): boolean {
switch (this.operator) {
case StringOperation.contains:
return left.includes(right);
case StringOperation.startsWith:
return left.startsWith(right);
case StringOperation.endsWith:
return left.endsWith(right);
case StringOperation.matchesRegex:
return new RegExp(right).test(left);
default:
throw new Error(`Unsupported string operator: ${this.operator}`);
}
}
}

3
packages/core/src/data_sources/model/conditional_variables/operators/index.ts

@ -0,0 +1,3 @@
export abstract class Operator {
abstract evaluate(left: any, right: any): boolean;
}

174
packages/core/test/specs/data_sources/model/conditional_variables/DataCondition.ts

@ -0,0 +1,174 @@
import {
DataCondition,
Expression,
LogicGroup,
} from '../../../../../src/data_sources/model/conditional_variables/DataCondition';
import { GenericOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/GenericOperator';
import { LogicalOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/LogicalOperator';
import { NumberOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/NumberOperator';
import { StringOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/StringOperations';
describe('DataCondition', () => {
describe('Basic Functionality Tests', () => {
test('should evaluate a simple boolean condition', () => {
const condition = true;
const dataCondition = new DataCondition(condition, 'Yes', 'No');
expect(dataCondition.getDataValue()).toBe('Yes');
});
test('should return ifFalse when condition evaluates to false', () => {
const condition = false;
const dataCondition = new DataCondition(condition, 'Yes', 'No');
expect(dataCondition.getDataValue()).toBe('No');
});
});
describe('Operator Tests', () => {
test('should evaluate using GenericOperation operators', () => {
const condition: Expression = { left: 5, operator: GenericOperation.equals, right: 5 };
const dataCondition = new DataCondition(condition, 'Equal', 'Not Equal');
expect(dataCondition.getDataValue()).toBe('Equal');
});
test('equals (false)', () => {
const condition: Expression = {
left: 'hello',
operator: GenericOperation.equals,
right: 'world',
};
const dataCondition = new DataCondition(condition, 'true', 'false');
expect(dataCondition.evaluate()).toBe(false);
});
test('should evaluate using StringOperation operators', () => {
const condition: Expression = { left: 'apple', operator: StringOperation.contains, right: 'app' };
const dataCondition = new DataCondition(condition, 'Contains', "Doesn't contain");
expect(dataCondition.getDataValue()).toBe('Contains');
});
test('should evaluate using NumberOperation operators', () => {
const condition: Expression = { left: 10, operator: NumberOperation.lessThan, right: 15 };
const dataCondition = new DataCondition(condition, 'Valid', 'Invalid');
expect(dataCondition.getDataValue()).toBe('Valid');
});
test('should evaluate using LogicalOperation operators', () => {
const logicGroup: LogicGroup = {
logicalOperator: LogicalOperation.and,
statements: [
{ left: true, operator: GenericOperation.equals, right: true },
{ left: 5, operator: NumberOperation.greaterThan, right: 3 },
],
};
const dataCondition = new DataCondition(logicGroup, 'Pass', 'Fail');
expect(dataCondition.getDataValue()).toBe('Pass');
});
});
describe('Edge Case Tests', () => {
test('should throw error for invalid condition type', () => {
const invalidCondition: any = { randomField: 'randomValue' };
expect(() => new DataCondition(invalidCondition, 'Yes', 'No')).toThrow('Invalid condition type.');
});
test('should evaluate complex nested conditions', () => {
const nestedLogicGroup: LogicGroup = {
logicalOperator: LogicalOperation.or,
statements: [
{
logicalOperator: LogicalOperation.and,
statements: [
{ left: 1, operator: NumberOperation.lessThan, right: 5 },
{ left: 'test', operator: GenericOperation.equals, right: 'test' },
],
},
{ left: 10, operator: NumberOperation.greaterThan, right: 100 },
],
};
const dataCondition = new DataCondition(nestedLogicGroup, 'Nested Pass', 'Nested Fail');
expect(dataCondition.getDataValue()).toBe('Nested Pass');
});
});
describe('LogicalGroup Tests', () => {
test('should correctly handle AND logical operator', () => {
const logicGroup: LogicGroup = {
logicalOperator: LogicalOperation.and,
statements: [
{ left: true, operator: GenericOperation.equals, right: true },
{ left: 5, operator: NumberOperation.greaterThan, right: 3 },
],
};
const dataCondition = new DataCondition(logicGroup, 'All true', 'One or more false');
expect(dataCondition.getDataValue()).toBe('All true');
});
test('should correctly handle OR logical operator', () => {
const logicGroup: LogicGroup = {
logicalOperator: LogicalOperation.or,
statements: [
{ left: true, operator: GenericOperation.equals, right: false },
{ left: 5, operator: NumberOperation.greaterThan, right: 3 },
],
};
const dataCondition = new DataCondition(logicGroup, 'At least one true', 'All false');
expect(dataCondition.getDataValue()).toBe('At least one true');
});
test('should correctly handle XOR logical operator', () => {
const logicGroup: LogicGroup = {
logicalOperator: LogicalOperation.xor,
statements: [
{ left: true, operator: GenericOperation.equals, right: true },
{ left: 5, operator: NumberOperation.lessThan, right: 3 },
{ left: false, operator: GenericOperation.equals, right: true },
],
};
const dataCondition = new DataCondition(logicGroup, 'Exactly one true', 'Multiple true or all false');
expect(dataCondition.getDataValue()).toBe('Exactly one true');
});
test('should handle nested logical groups', () => {
const logicGroup: LogicGroup = {
logicalOperator: LogicalOperation.and,
statements: [
{ left: true, operator: GenericOperation.equals, right: true },
{
logicalOperator: LogicalOperation.or,
statements: [
{ left: 5, operator: NumberOperation.greaterThan, right: 3 },
{ left: false, operator: GenericOperation.equals, right: true },
],
},
],
};
const dataCondition = new DataCondition(logicGroup, 'All true', 'One or more false');
expect(dataCondition.getDataValue()).toBe('All true');
});
test('should handle groups with false conditions', () => {
const logicGroup: LogicGroup = {
logicalOperator: LogicalOperation.and,
statements: [
{ left: true, operator: GenericOperation.equals, right: true },
{ left: false, operator: GenericOperation.equals, right: true },
{ left: 5, operator: NumberOperation.greaterThan, right: 3 },
],
};
const dataCondition = new DataCondition(logicGroup, 'All true', 'One or more false');
expect(dataCondition.getDataValue()).toBe('One or more false');
});
});
});

150
packages/core/test/specs/data_sources/model/conditional_variables/operators/GenericOperator.ts

@ -0,0 +1,150 @@
import {
GenericOperator,
GenericOperation,
} from '../../../../../../src/data_sources/model/conditional_variables/operators/GenericOperator';
describe('GenericOperator', () => {
describe('Operator: equals', () => {
test('should return true when values are equal', () => {
const operator = new GenericOperator(GenericOperation.equals);
expect(operator.evaluate(5, 5)).toBe(true);
});
test('should return false when values are not equal', () => {
const operator = new GenericOperator(GenericOperation.equals);
expect(operator.evaluate(5, 10)).toBe(false);
});
});
describe('Operator: isTruthy', () => {
test('should return true for truthy value', () => {
const operator = new GenericOperator(GenericOperation.isTruthy);
expect(operator.evaluate('non-empty', null)).toBe(true);
});
test('should return false for falsy value', () => {
const operator = new GenericOperator(GenericOperation.isTruthy);
expect(operator.evaluate('', null)).toBe(false);
});
});
describe('Operator: isFalsy', () => {
test('should return true for falsy value', () => {
const operator = new GenericOperator(GenericOperation.isFalsy);
expect(operator.evaluate(0, null)).toBe(true);
});
test('should return false for truthy value', () => {
const operator = new GenericOperator(GenericOperation.isFalsy);
expect(operator.evaluate(1, null)).toBe(false);
});
});
describe('Operator: isDefined', () => {
test('should return true for defined value', () => {
const operator = new GenericOperator(GenericOperation.isDefined);
expect(operator.evaluate(10, null)).toBe(true);
});
test('should return false for undefined value', () => {
const operator = new GenericOperator(GenericOperation.isDefined);
expect(operator.evaluate(undefined, null)).toBe(false);
});
});
describe('Operator: isNull', () => {
test('should return true for null value', () => {
const operator = new GenericOperator(GenericOperation.isNull);
expect(operator.evaluate(null, null)).toBe(true);
});
test('should return false for non-null value', () => {
const operator = new GenericOperator(GenericOperation.isNull);
expect(operator.evaluate(0, null)).toBe(false);
});
});
describe('Operator: isUndefined', () => {
test('should return true for undefined value', () => {
const operator = new GenericOperator(GenericOperation.isUndefined);
expect(operator.evaluate(undefined, null)).toBe(true);
});
test('should return false for defined value', () => {
const operator = new GenericOperator(GenericOperation.isUndefined);
expect(operator.evaluate(0, null)).toBe(false);
});
});
describe('Operator: isArray', () => {
test('should return true for array', () => {
const operator = new GenericOperator(GenericOperation.isArray);
expect(operator.evaluate([1, 2, 3], null)).toBe(true);
});
test('should return false for non-array', () => {
const operator = new GenericOperator(GenericOperation.isArray);
expect(operator.evaluate('not an array', null)).toBe(false);
});
});
describe('Operator: isObject', () => {
test('should return true for object', () => {
const operator = new GenericOperator(GenericOperation.isObject);
expect(operator.evaluate({ key: 'value' }, null)).toBe(true);
});
test('should return false for non-object', () => {
const operator = new GenericOperator(GenericOperation.isObject);
expect(operator.evaluate(42, null)).toBe(false);
});
});
describe('Operator: isString', () => {
test('should return true for string', () => {
const operator = new GenericOperator(GenericOperation.isString);
expect(operator.evaluate('Hello', null)).toBe(true);
});
test('should return false for non-string', () => {
const operator = new GenericOperator(GenericOperation.isString);
expect(operator.evaluate(42, null)).toBe(false);
});
});
describe('Operator: isNumber', () => {
test('should return true for number', () => {
const operator = new GenericOperator(GenericOperation.isNumber);
expect(operator.evaluate(42, null)).toBe(true);
});
test('should return false for non-number', () => {
const operator = new GenericOperator(GenericOperation.isNumber);
expect(operator.evaluate('not a number', null)).toBe(false);
});
});
describe('Operator: isBoolean', () => {
test('should return true for boolean', () => {
const operator = new GenericOperator(GenericOperation.isBoolean);
expect(operator.evaluate(true, null)).toBe(true);
});
test('should return false for non-boolean', () => {
const operator = new GenericOperator(GenericOperation.isBoolean);
expect(operator.evaluate(1, null)).toBe(false);
});
});
describe('Edge Case Tests', () => {
test('should handle null as input gracefully', () => {
const operator = new GenericOperator(GenericOperation.isNull);
expect(operator.evaluate(null, null)).toBe(true);
});
test('should throw error for unsupported operator', () => {
const operator = new GenericOperator('unsupported' as GenericOperation);
expect(() => operator.evaluate(1, 2)).toThrow('Unsupported generic operator: unsupported');
});
});
});

59
packages/core/test/specs/data_sources/model/conditional_variables/operators/LogicalOperator.ts

@ -0,0 +1,59 @@
import {
LogicalOperator,
LogicalOperation,
} from '../../../../../../src/data_sources/model/conditional_variables/operators/LogicalOperator';
describe('LogicalOperator', () => {
describe('Operator: and', () => {
test('should return true when all statements are true', () => {
const operator = new LogicalOperator(LogicalOperation.and);
expect(operator.evaluate([true, true, true])).toBe(true);
});
test('should return false when at least one statement is false', () => {
const operator = new LogicalOperator(LogicalOperation.and);
expect(operator.evaluate([true, false, true])).toBe(false);
});
});
describe('Operator: or', () => {
test('should return true when at least one statement is true', () => {
const operator = new LogicalOperator(LogicalOperation.or);
expect(operator.evaluate([false, true, false])).toBe(true);
});
test('should return false when all statements are false', () => {
const operator = new LogicalOperator(LogicalOperation.or);
expect(operator.evaluate([false, false, false])).toBe(false);
});
});
describe('Operator: xor', () => {
test('should return true when exactly one statement is true', () => {
const operator = new LogicalOperator(LogicalOperation.xor);
expect(operator.evaluate([true, false, false])).toBe(true);
});
test('should return false when more than one statement is true', () => {
const operator = new LogicalOperator(LogicalOperation.xor);
expect(operator.evaluate([true, true, false])).toBe(false);
});
test('should return false when no statement is true', () => {
const operator = new LogicalOperator(LogicalOperation.xor);
expect(operator.evaluate([false, false, false])).toBe(false);
});
});
describe('Edge Case Tests', () => {
test('should return false for xor with all false inputs', () => {
const operator = new LogicalOperator(LogicalOperation.xor);
expect(operator.evaluate([false, false])).toBe(false);
});
test('should throw error for unsupported operator', () => {
const operator = new LogicalOperator('unsupported' as LogicalOperation);
expect(() => operator.evaluate([true, false])).toThrow('Unsupported logical operator: unsupported');
});
});
});

95
packages/core/test/specs/data_sources/model/conditional_variables/operators/NumberOperator.ts

@ -0,0 +1,95 @@
import {
NumberOperator,
NumberOperation,
} from '../../../../../../src/data_sources/model/conditional_variables/operators/NumberOperator';
describe('NumberOperator', () => {
describe('Operator: greaterThan', () => {
test('should return true when left is greater than right', () => {
const operator = new NumberOperator(NumberOperation.greaterThan);
expect(operator.evaluate(5, 3)).toBe(true);
});
test('should return false when left is not greater than right', () => {
const operator = new NumberOperator(NumberOperation.greaterThan);
expect(operator.evaluate(2, 3)).toBe(false);
});
});
describe('Operator: lessThan', () => {
test('should return true when left is less than right', () => {
const operator = new NumberOperator(NumberOperation.lessThan);
expect(operator.evaluate(2, 3)).toBe(true);
});
test('should return false when left is not less than right', () => {
const operator = new NumberOperator(NumberOperation.lessThan);
expect(operator.evaluate(5, 3)).toBe(false);
});
});
describe('Operator: greaterThanOrEqual', () => {
test('should return true when left is greater than or equal to right', () => {
const operator = new NumberOperator(NumberOperation.greaterThanOrEqual);
expect(operator.evaluate(3, 3)).toBe(true);
});
test('should return false when left is not greater than or equal to right', () => {
const operator = new NumberOperator(NumberOperation.greaterThanOrEqual);
expect(operator.evaluate(2, 3)).toBe(false);
});
});
describe('Operator: lessThanOrEqual', () => {
test('should return true when left is less than or equal to right', () => {
const operator = new NumberOperator(NumberOperation.lessThanOrEqual);
expect(operator.evaluate(3, 3)).toBe(true);
});
test('should return false when left is not less than or equal to right', () => {
const operator = new NumberOperator(NumberOperation.lessThanOrEqual);
expect(operator.evaluate(5, 3)).toBe(false);
});
});
describe('Operator: equals', () => {
test('should return true when numbers are equal', () => {
const operator = new NumberOperator(NumberOperation.equals);
expect(operator.evaluate(4, 4)).toBe(true);
});
test('should return false when numbers are not equal', () => {
const operator = new NumberOperator(NumberOperation.equals);
expect(operator.evaluate(4, 5)).toBe(false);
});
});
describe('Operator: notEquals', () => {
test('should return true when numbers are not equal', () => {
const operator = new NumberOperator(NumberOperation.notEquals);
expect(operator.evaluate(4, 5)).toBe(true);
});
test('should return false when numbers are equal', () => {
const operator = new NumberOperator(NumberOperation.notEquals);
expect(operator.evaluate(4, 4)).toBe(false);
});
});
describe('Edge Case Tests', () => {
test('should handle boundary values correctly', () => {
const operator = new NumberOperator(NumberOperation.lessThan);
expect(operator.evaluate(Number.MIN_VALUE, 1)).toBe(true);
});
test('should return false for NaN comparisons', () => {
const operator = new NumberOperator(NumberOperation.equals);
expect(operator.evaluate(NaN, NaN)).toBe(false);
});
test('should throw error for unsupported operator', () => {
const operator = new NumberOperator('unsupported' as NumberOperation);
expect(() => operator.evaluate(1, 2)).toThrow('Unsupported number operator: unsupported');
});
});
});

81
packages/core/test/specs/data_sources/model/conditional_variables/operators/StringOperator.ts

@ -0,0 +1,81 @@
import {
StringOperator,
StringOperation,
} from '../../../../../../src/data_sources/model/conditional_variables/operators/StringOperations';
describe('StringOperator', () => {
describe('Operator: contains', () => {
test('should return true when left contains right', () => {
const operator = new StringOperator(StringOperation.contains);
expect(operator.evaluate('hello world', 'world')).toBe(true);
});
test('should return false when left does not contain right', () => {
const operator = new StringOperator(StringOperation.contains);
expect(operator.evaluate('hello world', 'moon')).toBe(false);
});
});
describe('Operator: startsWith', () => {
test('should return true when left starts with right', () => {
const operator = new StringOperator(StringOperation.startsWith);
expect(operator.evaluate('hello world', 'hello')).toBe(true);
});
test('should return false when left does not start with right', () => {
const operator = new StringOperator(StringOperation.startsWith);
expect(operator.evaluate('hello world', 'world')).toBe(false);
});
});
describe('Operator: endsWith', () => {
test('should return true when left ends with right', () => {
const operator = new StringOperator(StringOperation.endsWith);
expect(operator.evaluate('hello world', 'world')).toBe(true);
});
test('should return false when left does not end with right', () => {
const operator = new StringOperator(StringOperation.endsWith);
expect(operator.evaluate('hello world', 'hello')).toBe(false);
});
});
describe('Operator: matchesRegex', () => {
test('should return true when left matches the regex right', () => {
const operator = new StringOperator(StringOperation.matchesRegex);
expect(operator.evaluate('hello world', '^hello')).toBe(true);
});
test('should return false when left does not match the regex right', () => {
const operator = new StringOperator(StringOperation.matchesRegex);
expect(operator.evaluate('hello world', '^world')).toBe(false);
});
});
describe('Edge Case Tests', () => {
test('should return false for contains with empty right string', () => {
const operator = new StringOperator(StringOperation.contains);
expect(operator.evaluate('hello world', '')).toBe(true); // Empty string is included in any string
});
test('should return true for startsWith with empty right string', () => {
const operator = new StringOperator(StringOperation.startsWith);
expect(operator.evaluate('hello world', '')).toBe(true); // Any string starts with an empty string
});
test('should return true for endsWith with empty right string', () => {
const operator = new StringOperator(StringOperation.endsWith);
expect(operator.evaluate('hello world', '')).toBe(true); // Any string ends with an empty string
});
test('should throw error for invalid regex', () => {
const operator = new StringOperator(StringOperation.matchesRegex);
expect(() => operator.evaluate('hello world', '[')).toThrow();
});
test('should throw error for unsupported operator', () => {
const operator = new StringOperator('unsupported' as StringOperation);
expect(() => operator.evaluate('test', 'test')).toThrow('Unsupported string operator: unsupported');
});
});
});
Loading…
Cancel
Save