Skip to content

Commit

Permalink
More well-typedness test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
jg-rp committed Sep 17, 2023
1 parent 8b946dc commit c1eeb69
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 13 deletions.
9 changes: 8 additions & 1 deletion src/path/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,14 @@ export class JSONPathEnvironment {
}
break;
case FunctionExpressionType.NodesType:
if (!(arg instanceof JSONPathQuery)) {
if (
!(
arg instanceof JSONPathQuery ||
(arg instanceof FunctionExtension &&
this.functionRegister.get(arg.name)?.returnType ===
FunctionExpressionType.NodesType)
)
) {
throw new JSONPathTypeError(
`${token.value}() argument ${idx} must be of NodesType`,
arg.token,
Expand Down
4 changes: 4 additions & 0 deletions src/path/lex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,10 @@ function lexInsideFilter(l: Lexer): StateFn | null {
case "":
case "]":
l.filterLevel -= 1;
if (l.parenStack.length === 1) {
l.error("unbalanced parentheses");
return null;
}
l.backup();
return lexInsideBracketedSelection;
case ",":
Expand Down
6 changes: 0 additions & 6 deletions src/path/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,6 @@ export class Parser {

args.push(func.bind(this)(stream));

// Without this, that closing parenthesis of the inner function
// can incorrectly close the outer function.
if (args.at(-1) instanceof FunctionExtension) {
stream.next();
}

if (stream.peek.kind !== TokenKind.RPAREN) {
if (stream.peek.kind === TokenKind.RBRACKET) break;
stream.expectPeek(TokenKind.COMMA);
Expand Down
2 changes: 1 addition & 1 deletion tests/path/ietf_compare.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Filter expression comparison examples.
* Filter expression comparison tests from IETF spec examples.
*
* The test cases defined here are taken from version 20 of the JSONPath
* internet draft, draft-ietf-jsonpath-base-20. In accordance with
Expand Down
64 changes: 59 additions & 5 deletions tests/path/ietf_function_well_typedness.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Function well-typedness examples.
* Function well-typedness tests from IETF spec examples.
*
* The test cases defined here are taken from version 20 of the JSONPath
* internet draft, draft-ietf-jsonpath-base-20. In accordance with
Expand Down Expand Up @@ -37,7 +37,12 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import { JSONPathTypeError, compile } from "../../src";
import {
FilterFunction,
FunctionExpressionType,
JSONPathEnvironment,
JSONPathTypeError,
} from "../../src";

type TestCase = {
description: string;
Expand Down Expand Up @@ -66,18 +71,67 @@ const TEST_CASES: TestCase[] = [
path: "$[?count(1) == 1]",
valid: false,
},
{
description: "nested function, LogicalType -> NodesType",
path: "$[?count(foo(@.*)) == 1]",
valid: true,
},
{
description: "match, singular query, string literal",
path: "$[?match(@.timezone, 'Europe/.*')]",
valid: true,
},
{
description: "match, singular query, string literal, compared",
path: "$[?match(@.timezone, 'Europe/.*') == true]",
valid: false,
},
{
description: "value, comparison",
path: "$[?value(@..color) == 'red']",
valid: true,
},
{
description: "value, existence",
path: "$[?value(@..color)]",
valid: false,
},
{
description: "function, logical return type, existence",
path: "$[?bar(@.a)]",
valid: true,
},
];

// TODO: tests with mock functions
class MockFooFunction implements FilterFunction {
argTypes = [FunctionExpressionType.NodesType];
returnType = FunctionExpressionType.NodesType;

call(nodes: NodeList): NodeList {
return nodes;
}
}

class MockBarFunction implements FilterFunction {
argTypes = [FunctionExpressionType.NodesType];
returnType = FunctionExpressionType.LogicalType;

call(): boolean {
return false;
}
}

describe("IETF function well-typedness examples", () => {
const env = new JSONPathEnvironment();
env.functionRegister.set("foo", new MockFooFunction());
env.functionRegister.set("bar", new MockBarFunction());
test.each<TestCase>(TEST_CASES)(
"$description",
({ path, valid }: TestCase) => {
if (!valid) {
expect(() => compile(path)).toThrow(JSONPathTypeError);
expect(() => env.compile(path)).toThrow(JSONPathTypeError);
} else {
compile(path);
env.compile(path);
}
},
);
Expand Down

0 comments on commit c1eeb69

Please sign in to comment.