Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/jsonpath invalid evaluation #68

Merged
merged 24 commits into from
Sep 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
549dfa5
Move `JsonPath.ts` file to new `jsonPath` directory
nibble-4bits Sep 14, 2023
d1295fe
Remove optionality from `context` parameter
nibble-4bits Sep 14, 2023
81a878b
Create util function to verify if a date complies with RFC3339 grammar
nibble-4bits Sep 14, 2023
0efdfa9
Create abstract JSONPath constraint class to verify if a JSONPath ful…
nibble-4bits Sep 15, 2023
344a0e6
Create constraint to verify if a JSONPath result is an array
nibble-4bits Sep 15, 2023
f477934
Create constraint to verify if a JSONPath result is a boolean
nibble-4bits Sep 15, 2023
d27ed3b
Create constraint to verify if a JSONPath points to a defined value
nibble-4bits Sep 15, 2023
b8e97a1
Create constraint to verify if a JSONPath result is an integer
nibble-4bits Sep 15, 2023
09dfc86
Create constraint to verify if a JSONPath result is a number
nibble-4bits Sep 15, 2023
df56140
Create constraint to verify if a JSONPath result is an RFC3339 timestamp
nibble-4bits Sep 15, 2023
493d34d
Create constraint to verify if a JSONPath result is a string
nibble-4bits Sep 15, 2023
275d350
Validate constraints when evaluating JSONPath
nibble-4bits Sep 15, 2023
ede9594
Add constraints to JSONPath queries in `Wait` state
nibble-4bits Sep 15, 2023
c21c4ed
Add constraints to JSONPath query in `Map` state
nibble-4bits Sep 15, 2023
20c5e41
Add constraints to JSONPath queries in `Choice` state
nibble-4bits Sep 15, 2023
3225043
Create util function to quote a JSON value
nibble-4bits Sep 15, 2023
15286a3
Quote expected value in JSONPath constraints error messages
nibble-4bits Sep 15, 2023
55ad82f
Refer to paths as `Paths` instead of `JSONPaths` in constraints error…
nibble-4bits Sep 15, 2023
d75c34e
Make `IsPresent` operator ignore the default defined value constraint
nibble-4bits Sep 15, 2023
598ed3e
Update test assertion to account for array constraint of `ItemsPath` …
nibble-4bits Sep 15, 2023
7387ebf
Rename function to `stringifyJSONValue`
nibble-4bits Sep 16, 2023
dc64d94
Add test for util function `isRFC3339Date`
nibble-4bits Sep 16, 2023
4ed3146
Rename function to `isRFC3339Timestamp`
nibble-4bits Sep 16, 2023
6a4148d
Add tests for JSONPath constraints and invalid evaluation
nibble-4bits Sep 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions __tests__/InputOutputProcessing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ describe('Input processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processInputPath(undefined, input);
const result = processInputPath(undefined, input, context);

expect(result).toEqual(input);
});
Expand All @@ -54,8 +55,9 @@ describe('Input processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processInputPath('$.movies', input);
const result = processInputPath('$.movies', input, context);

expect(result).toEqual([
{
Expand Down Expand Up @@ -89,8 +91,9 @@ describe('Input processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processInputPath(null, input);
const result = processInputPath(null, input, context);

expect(result).toEqual({});
});
Expand All @@ -108,8 +111,9 @@ describe('Input processing', () => {
},
};
const input = {};
const context = {};

const result = processPayloadTemplate(parameters, input);
const result = processPayloadTemplate(parameters, input, context);

expect(result).toEqual({
field1: 50,
Expand Down Expand Up @@ -149,8 +153,9 @@ describe('Input processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processPayloadTemplate(parameters, input);
const result = processPayloadTemplate(parameters, input, context);

expect(result).toEqual({
field1: 50,
Expand Down Expand Up @@ -192,8 +197,9 @@ describe('Input processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processPayloadTemplate(parameters, input);
const result = processPayloadTemplate(parameters, input, context);

expect(result).toEqual({
field1: 50,
Expand Down Expand Up @@ -247,8 +253,9 @@ describe('Input processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processPayloadTemplate(parameters, input);
const result = processPayloadTemplate(parameters, input, context);

expect(result).toEqual({
field1: 50,
Expand Down Expand Up @@ -279,8 +286,9 @@ describe('Output processing', () => {
},
};
const input = {};
const context = {};

const result = processPayloadTemplate(resultSelector, input);
const result = processPayloadTemplate(resultSelector, input, context);

expect(result).toEqual({
field1: 50,
Expand Down Expand Up @@ -320,8 +328,9 @@ describe('Output processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processPayloadTemplate(resultSelector, input);
const result = processPayloadTemplate(resultSelector, input, context);

expect(result).toEqual({
field1: 50,
Expand Down Expand Up @@ -363,8 +372,9 @@ describe('Output processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processPayloadTemplate(resultSelector, input);
const result = processPayloadTemplate(resultSelector, input, context);

expect(result).toEqual({
field1: 50,
Expand Down Expand Up @@ -418,8 +428,9 @@ describe('Output processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processPayloadTemplate(resultSelector, input);
const result = processPayloadTemplate(resultSelector, input, context);

expect(result).toEqual({
field1: 50,
Expand Down Expand Up @@ -621,8 +632,9 @@ describe('Output processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processOutputPath(undefined, input);
const result = processOutputPath(undefined, input, context);

expect(result).toEqual(input);
});
Expand All @@ -645,8 +657,9 @@ describe('Output processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processOutputPath('$.movies', input);
const result = processOutputPath('$.movies', input, context);

expect(result).toEqual([
{
Expand Down Expand Up @@ -680,8 +693,9 @@ describe('Output processing', () => {
lastUpdated: '2020-05-27T08:00:00Z',
},
};
const context = {};

const result = processOutputPath(null, input);
const result = processOutputPath(null, input, context);

expect(result).toEqual({});
});
Expand Down
62 changes: 62 additions & 0 deletions __tests__/jsonPath/JsonPath.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { jsonPathQuery } from '../../src/stateMachine/jsonPath/JsonPath';
import { IntegerConstraint } from '../../src/stateMachine/jsonPath/constraints/IntegerConstraint';

afterEach(() => {
jest.clearAllMocks();
});

describe('jsonPathQuery', () => {
const testObject = {
a: 1,
b: 2,
c: [3, 4, 5],
d: { e: 6 },
};
const context = {
executionId: 'id-12345',
};

test('should query `json` parameter if path expression starts with `$`', () => {
const pathExpression = '$.a';

const result = jsonPathQuery(pathExpression, testObject, context);
expect(result).toBe(1);
});

test('should query Context Object if path expression starts with `$$`', () => {
const pathExpression = '$$.executionId';

const result = jsonPathQuery(pathExpression, testObject, context);
expect(result).toBe('id-12345');
});

describe('constraints', () => {
test('should return value if path is valid', () => {
const pathExpression = '$.d.e';

const result = jsonPathQuery(pathExpression, testObject, context);
expect(result).toBe(6);
});

test('should check default `DefinedValueConstraint` constraint', () => {
const pathExpression = '$.nonexistent';

const testFn = () => jsonPathQuery(pathExpression, testObject, context);
expect(testFn).toThrow("Path expression '$.nonexistent' does not point to a value");
});

test('should not check default `DefinedValueConstraint` constraint if `ignoreDefinedValueConstraint` option is true', () => {
const pathExpression = '$.nonexistent';

const testFn = () => jsonPathQuery(pathExpression, testObject, context, { ignoreDefinedValueConstraint: true });
expect(testFn).not.toThrow();
});

test('should check for any constraints provided by the caller', () => {
const pathExpression = '$.c';

const testFn = () => jsonPathQuery(pathExpression, testObject, context, { constraints: [IntegerConstraint] });
expect(testFn).toThrow("Path expression '$.c' evaluated to [3,4,5], but expected an integer");
});
});
});
27 changes: 27 additions & 0 deletions __tests__/jsonPath/constraints/ArrayConstraint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { StatesRuntimeError } from '../../../src/error/predefined/StatesRuntimeError';
import { ArrayConstraint } from '../../../src/stateMachine/jsonPath/constraints/ArrayConstraint';

afterEach(() => {
jest.clearAllMocks();
});

describe('ArrayConstraint', () => {
test('should not throw if passed value is an array', () => {
const value = ['item', 1, { a: 1, b: 2 }, [], true];

const constraint = new ArrayConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).not.toThrow();
});

test('should throw if passed value is not an array', () => {
const value = 55;

const constraint = new ArrayConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).toThrow("Path expression '' evaluated to 55, but expected an array");
expect(testFunc).toThrow(StatesRuntimeError);
});
});
27 changes: 27 additions & 0 deletions __tests__/jsonPath/constraints/BooleanConstraint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { StatesRuntimeError } from '../../../src/error/predefined/StatesRuntimeError';
import { BooleanConstraint } from '../../../src/stateMachine/jsonPath/constraints/BooleanConstraint';

afterEach(() => {
jest.clearAllMocks();
});

describe('BooleanConstraint', () => {
test('should not throw if passed value is a boolean', () => {
const value = true;

const constraint = new BooleanConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).not.toThrow();
});

test('should throw if passed value is not a boolean', () => {
const value = 'hello';

const constraint = new BooleanConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).toThrow('Path expression \'\' evaluated to "hello", but expected a boolean');
expect(testFunc).toThrow(StatesRuntimeError);
});
});
27 changes: 27 additions & 0 deletions __tests__/jsonPath/constraints/DefinedValueConstraint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { DefinedValueConstraint } from '../../../src/stateMachine/jsonPath/constraints/DefinedValueConstraint';
import { StatesRuntimeError } from '../../../src/error/predefined/StatesRuntimeError';

afterEach(() => {
jest.clearAllMocks();
});

describe('DefinedValueConstraint', () => {
test('should not throw if passed value is not undefined', () => {
const value = {};

const constraint = new DefinedValueConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).not.toThrow();
});

test('should throw if passed value is undefined', () => {
const value = undefined;

const constraint = new DefinedValueConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).toThrow("Path expression '' does not point to a value");
expect(testFunc).toThrow(StatesRuntimeError);
});
});
81 changes: 81 additions & 0 deletions __tests__/jsonPath/constraints/IntegerConstraint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { IntegerConstraint } from '../../../src/stateMachine/jsonPath/constraints/IntegerConstraint';
import { StatesRuntimeError } from '../../../src/error/predefined/StatesRuntimeError';

afterEach(() => {
jest.clearAllMocks();
});

describe('IntegerConstraint', () => {
test('should not throw if passed value is an integer', () => {
const value = 12345;

const constraint = new IntegerConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).not.toThrow();
});

test('should throw if passed value is not a number', () => {
const value = 'hello, world!';

const constraint = new IntegerConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).toThrow('Path expression \'\' evaluated to "hello, world!", but expected an integer');
expect(testFunc).toThrow(StatesRuntimeError);
});

test('should throw if passed value is a number but not an integer', () => {
const value = 123.45;

const constraint = new IntegerConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).toThrow("Path expression '' evaluated to 123.45, but expected an integer");
expect(testFunc).toThrow(StatesRuntimeError);
});

describe('IntegerConstraint.greaterThanOrEqual', () => {
test('should not throw if passed value is greater than the constraint value', () => {
const value = 12345;

const GreaterThanOrEqualConstraint = IntegerConstraint.greaterThanOrEqual(12000);
const constraint = new GreaterThanOrEqualConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).not.toThrow();
});

test('should not throw if passed value is equal to the constraint value', () => {
const value = 12345;

const GreaterThanOrEqualConstraint = IntegerConstraint.greaterThanOrEqual(12345);
const constraint = new GreaterThanOrEqualConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).not.toThrow();
});

test('should throw if passed value is less than the constraint value', () => {
const value = 8000;

const GreaterThanOrEqualConstraint = IntegerConstraint.greaterThanOrEqual(12345);
const constraint = new GreaterThanOrEqualConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).toThrow("Path expression '' evaluated to 8000, but expected an integer >= 12345");
expect(testFunc).toThrow(StatesRuntimeError);
});

test('should throw if passed value is not an integer', () => {
const value = 123.45;

const GreaterThanOrEqualConstraint = IntegerConstraint.greaterThanOrEqual(12345);
const constraint = new GreaterThanOrEqualConstraint('');

const testFunc = () => constraint.test(value);
expect(testFunc).toThrow("Path expression '' evaluated to 123.45, but expected an integer");
expect(testFunc).toThrow(StatesRuntimeError);
});
});
});
Loading