Add JSON Logic log operator#3561
Merged
Merged
Conversation
…e fixtures Adds per-operator JSON predicate fixtures (exact copies of the iOS #6885 corpus) under src/test/resources/predicate-fixtures, run by a shared parameterized runner. Deletes the migrated arithmetic/comparison/equality/ logic suites and trims the accessor/evaluator/string-array suites to the cases not expressible as predicate->boolean. Test-only change. Co-authored-by: Cursor <cursoragent@cursor.com>
Expresses the "empty-string key resolves to the whole scope and is not
missing" case as a `{"!!":{"missing":[""]}}` -> false fixture (the string
coercion the other missing fixtures use can't distinguish [] from [""]).
Removes the now-covered Kotlin test and bumps the fixture count to 237.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds fixtures for the empty-segment dot-path splits, var default-vs-null-leaf behavior, and the non-numeric missing_some threshold (7 new cases), and drops the now-redundant Kotlin tests. AccessorOperatorsTest keeps only the cases that cannot be a predicate->boolean fixture (top-level array scope or a whole-object return). Fixture count is now 244. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Mirror the iOS rules-engine implementation: Kotlin some/all operators matching json-logic-js semantics, shared JSON predicate fixtures, and pinned fixture count bumped to 271. Co-authored-by: Cursor <cursoragent@cursor.com>
place operator docs on each op instead of duplicating at the type level; drop "not vacuous truth" wording Co-authored-by: Cursor <cursoragent@cursor.com>
…-logic-iteration-operators
Implement variadic min/max with JS Math.min/Math.max semantics: operands coerce via Number() (toNumberOrNull), empty input yields ±∞, and NaN operands poison the result. Wire into the operator dispatcher and add byte-identical min.json/max.json fixtures from purchases-ios, bumping the pinned fixture count to 305. Co-authored-by: Cursor <cursoragent@cursor.com>
Completes the json-logic-js iteration family on top of some/all.
none/map/filter rebind vars to the current item; reduce rebinds to
{current, accumulator}. Non-array sources: none -> true, map/filter
-> [], reduce -> seed unchanged. Coverage is the shared declarative
JSON fixtures (none/map/filter/reduce.json, byte-identical to iOS),
bumping the pinned fixture count to 345. Android counterpart of
RevenueCat/purchases-ios#6834.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Label each operator grouping in Operators.dispatch per PR review feedback. Co-authored-by: Cursor <cursoragent@cursor.com>
Move the string/array group after comparison so the Android dispatcher mirrors the iOS Operators.dispatch grouping order. Co-authored-by: Cursor <cursoragent@cursor.com>
Rename the all-match fixture and add cases where only the first or only the middle item matches, addressing PR review feedback that the existing some coverage was order dependent. Fixtures kept byte-identical with iOS. Co-authored-by: Cursor <cursoragent@cursor.com>
khepri guards all with notEmpty, so it returns false for an empty array like json-logic-js; drop the inaccurate claim that khepri returns true. Fixture kept byte-identical with iOS. Co-authored-by: Cursor <cursoragent@cursor.com>
…-logic-iteration-operators
…teration-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # rules-engine-internal/src/test/kotlin/com/revenuecat/purchases/rules/PredicateFixtureLoaderTest.kt
…erators' into pallares/json-logic-min-max-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # rules-engine-internal/src/main/kotlin/com/revenuecat/purchases/rules/operators/Operators.kt # rules-engine-internal/src/test/kotlin/com/revenuecat/purchases/rules/PredicateFixtureLoaderTest.kt
…in-max-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # rules-engine-internal/src/main/kotlin/com/revenuecat/purchases/rules/operators/Operators.kt # rules-engine-internal/src/test/kotlin/com/revenuecat/purchases/rules/PredicateFixtureLoaderTest.kt
Add a dedicated "Min and max" grouping comment so the min/max cases are not lumped under arithmetic, matching the iOS dispatcher. Co-authored-by: Cursor <cursoragent@cursor.com>
…ators' into pallares/json-logic-iteration-mapping-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # rules-engine-internal/src/test/kotlin/com/revenuecat/purchases/rules/PredicateFixtureLoaderTest.kt
Self-documenting replacement for the {"/": [-10,0]} workaround; constants
seeded only by the conformance harness (reservedConstants), engine untouched;
NaN still uses x != x; fixtures kept byte-identical with iOS.
Co-authored-by: Cursor <cursoragent@cursor.com>
substr.json and missing_some.json now use the seeded "+Infinity" and "-Infinity" variables instead of division-by-zero operands. NaN cases and evaluator.json are unchanged. Fixtures are byte-identical to iOS. Co-authored-by: Cursor <cursoragent@cursor.com>
Mirror iOS predicate fixture updates: remove the redundant "comes from the test-only" clause from 7 fixture descriptions across min, max, substr, and missing_some. Fixtures kept byte-identical with iOS. Co-authored-by: Cursor <cursoragent@cursor.com>
…nfinity-fixture-vars Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # rules-engine-internal/src/test/resources/predicate-fixtures/max.json # rules-engine-internal/src/test/resources/predicate-fixtures/min.json
…ture-vars' into pallares/json-logic-iteration-mapping-operators
Co-authored-by: Cursor <cursoragent@cursor.com>
…ument) Co-authored-by: Cursor <cursoragent@cursor.com>
2 tasks
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Cover logging ±Infinity, NaN, empty object, and empty array. Empty array and empty args were indistinguishable under substring matching (they log "" and "null"), so `expectedLogs` now asserts the exact, ordered list of emitted messages. `expectedWarnings` is unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
The tag was always the single `[RulesEngine]` constant and never varied at any call site, so it added interface surface without flexibility. For the `log` channel a prefix also defeats the verbatim passthrough. Hosts that bridge the logger prepend their own identifier when they need one. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3561 +/- ##
=======================================
Coverage 80.28% 80.28%
=======================================
Files 377 377
Lines 15386 15386
Branches 2134 2134
=======================================
Hits 12353 12353
Misses 2174 2174
Partials 859 859 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
7549b55 to
99f105e
Compare
tonidero
approved these changes
Jun 9, 2026
| "id": "log_is_identity_for_string", | ||
| "predicate": { "===": [{ "log": "apple" }, "apple"] }, | ||
| "variables": {}, | ||
| "expected": true |
Contributor
There was a problem hiding this comment.
Same as in the iOS PR, I wonder if this should be logging...
Member
Author
There was a problem hiding this comment.
Oh, right. It is logging indeed. But this test was just testing for the log as the identity operator.
However, it doesn't hurt to also check for the expected logs, of course.
Added it in 78d43ef
Co-authored-by: Cursor <cursoragent@cursor.com>
Base automatically changed from
pallares/json-logic-iteration-mapping-operators
to
main
June 9, 2026 12:06
…roid-log-operator Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # rules-engine-internal/src/main/kotlin/com/revenuecat/purchases/rules/operators/Operators.kt # rules-engine-internal/src/test/kotlin/com/revenuecat/purchases/rules/PredicateFixtureLoaderTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Checklist
purchases-iosand hybridsMotivation
Resolves SDK-4368
Add the JSON Logic
logdebug operator so predicates can emit values without affecting outcomes. Companion iOS PR: RevenueCat/purchases-ios#6945Description
RulesEngineLoggerexposes separatewarn(engine diagnostics) andlog(logoperator passthrough) channels. Fixtures assert operator output viaexpectedLogs(exact match), distinct fromexpectedWarnings.Notes
Logger messages carry no
[RulesEngine]tag/prefix anymore. Hosts that bridge the logger (i.e. the SDKs) can prepend their own identifier.Note
Medium Risk
Public
RulesEngineLoggerAPI change (newlogmethod,warnsignature change) affects SDK logger bridges; operator behavior is additive and well-covered by fixtures.Overview
Adds the JSON Logic
logoperator so predicates can emit debug values without changing evaluation: the operand is evaluated, stringified withjsString, sent on a newRulesEngine.logger.logchannel, and returned unchanged (json-logic-js identity behavior).RulesEngineLoggeris split intowarn(engine diagnostics) andlog(operator output). The optional tag parameter and[RulesEngine]prefix are removed;PrintLoggerwrites warnings to stderr and log messages to stdout.Conformance tests gain optional
expectedLogs(exact ordered match, unlike substringexpectedWarnings),CapturingLogger/CapturingLoggerRuleexpose captured logs, andlog.jsonadds 13 fixtures (suite count 347 → 360).Reviewed by Cursor Bugbot for commit 84ce662. Bugbot is set up for automated code reviews on this repo. Configure here.