Skip to content

Add test cases for undefined operands in comparisons, $match, $merge - #837

Open
janedbal wants to merge 2 commits into
jsonata-js:masterfrom
janedbal:test-suite-undefined-operands
Open

janedbal wants to merge 2 commits into
jsonata-js:masterfrom
janedbal:test-suite-undefined-operands

Conversation

@janedbal

@janedbal janedbal commented Oct 5, 2026 •

Copy link
Copy Markdown

JSONata ports vendor test/test-suite as their conformance bar. The behaviours below have no cases in the suite, and some ports diverge here. This PR adds 35 cases that pin the current reference behaviour. All expected values come from this repo at master.

Behaviours pinned:

  • = / != with a missing operand returns false: missing != null → false. A predicate drops items where the field is missing: items[tag != "b"].id → [1, 4] (comparison-operators/case029–case033).
  • $match with an undefined input returns undefined, also with a chained .groups[0]: $match(missing, /(a)/).groups[0] → undefined (new group function-match).
  • $merge accepts empty objects, including an object constructor whose fields are all undefined: $merge([{"a": nothing}]) → {}, $merge([{}, {"b": 1}]) → {"b": 1} (function-merge/case005–case008).
  • An array constructor on a path keeps nested arrays when it is an object field value, the same as at the top level: {"x": items.[{"q": n}]} with two items → {"x": [[{"q": 1}], [{"q": 2}]]} (flattening/case046–case049).
  • A conditional with no else branch leaves the key out of an object, and leaves the element out of $merge: {"a": flag ? {"b": 1}, "c": 2} with flag: false → {"c": 2}; $merge([{"a": 1}, flag ? {"b": 2}]) → {"a": 1} (conditionals/case009–case012).
  • $now(picture) applies the picture string. All $now() and $millis() calls in one evaluation use the same timestamp, so the cases are deterministic: $now('[Y0001]-[M01]-[D01]') = $fromMillis($millis(), '[Y0001]-[M01]-[D01]') → true (new group function-now).
  • $now() and $millis() return the same value for all calls in one evaluation: ($a := $millis(); $sum([1..10000]); $a = $millis()) → true. The same shape for $now() also gives true (function-now/case004, new group function-millis).
  • $spread returns undefined for an empty array or object, and unwraps a single-key object: $spread([]) → undefined, $spread({}) → undefined, $spread({"a": 1}) → {"a": 1}, $spread({"a": 1, "b": 2}) → [{"a": 1}, {"b": 2}]. An array input stays an array: $spread([{"a": 1}]) → [{"a": 1}] (function-spread/case004–case008).
  • $map over an empty array returns undefined: $map([], function($v){$v}) → undefined, [$map([], function($v){$v})] → [] (hof-map/case012–case013).
  • $keys flattens nested arrays of objects: $keys([[{"a": 1}]]) → "a", $keys([[{"a": 1}], [{"b": 2}]]) → ["a", "b"] (function-keys/case007–case008).

The = / != cases pin the behaviour that #476 and #772 discuss. If that behaviour changes, these cases change with it.

No engine code changes. TZ=GMT npm test passes (lint, 1838 tests, 100% coverage).

Signed-off-by: Jan Nedbal jan.nedbal@shipmonk.com

Co-Authored-By: Claude Code

— Claude

Add language-neutral test suite cases for behaviour that the suite
does not cover yet: != and = with a missing operand, $match with an
undefined input, $merge with empty objects, array constructors on a
path in an object field value, conditionals without an else branch in
an object field value or in $merge, and $now with a picture string.

Co-Authored-By: Claude Code
Signed-off-by: Jan Nedbal <jan.nedbal@shipmonk.com>
Add language-neutral test suite cases for $spread with empty and
single-key inputs, $map over an empty array, $keys over nested arrays
of objects, and $now and $millis. The $now and $millis cases check
that one evaluation uses one timestamp.

Co-Authored-By: Claude Code
Signed-off-by: Jan Nedbal <jan.nedbal@shipmonk.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant