Add JSON Logic min and max operators#3552
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>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3552 +/- ##
=======================================
Coverage 80.15% 80.15%
=======================================
Files 371 371
Lines 15210 15210
Branches 2110 2110
=======================================
Hits 12192 12192
Misses 2168 2168
Partials 850 850 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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>
tonidero
approved these changes
Jun 8, 2026
tonidero
left a comment
Contributor
There was a problem hiding this comment.
I think it looks good! Just an idea to try to improve the tests, but can be applied separately.
This was referenced Jun 8, 2026
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
Adds
min/maxso audience predicates can compare against numeric extrema. Resolves SDK-4347. Android counterpart of RevenueCat/purchases-ios#6825.Description
Number()(Value.toNumberOrNull, like-///%, notparseFloatas in+/*); no iteration/projectionMath.min()/Math.max()min.json/max.json, byte-identical to iOS), run by the existing fixture runnerNote
Medium Risk
Changes predicate evaluation semantics for audience rules; malformed operands intentionally yield NaN/±∞ so thresholds fail closed, but new operators can alter which users match existing predicates once deployed.
Overview
Adds JSON Logic
minandmaxso audience predicates can compute numeric extrema over flat operand lists, aligned withMath.min/Math.max(Android counterpart to iOS).Implementation lives in new
MinMaxOperators: operands are evaluated viaevalArgs, coerced withValue.toNumberOrNull(same path as-///%), folded with NaN poisoning, and empty args return ±∞.Operators.dispatchroutes"min"and"max"to these handlers.Conformance is covered by shared
min.json/max.jsonfixtures (coercion, empty args, fail-closed thresholds, vars);PredicateFixtureLoaderTestexpected count is bumped 273 → 307.Reviewed by Cursor Bugbot for commit 6940dad. Bugbot is set up for automated code reviews on this repo. Configure here.