Skip to content

Add JSON Logic min and max operators#3552

Merged
ajpallares merged 21 commits into
mainfrom
pallares/json-logic-min-max-operators
Jun 8, 2026
Merged

Add JSON Logic min and max operators#3552
ajpallares merged 21 commits into
mainfrom
pallares/json-logic-min-max-operators

Conversation

@ajpallares

@ajpallares ajpallares commented Jun 7, 2026

Copy link
Copy Markdown
Member

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-ios and hybrids

Motivation

Adds min/max so audience predicates can compare against numeric extrema. Resolves SDK-4347. Android counterpart of RevenueCat/purchases-ios#6825.

Description

  • Variadic flat-list shape per json-logic-js; operands coerce via JS Number() (Value.toNumberOrNull, like -///%, not parseFloat as in +/*); no iteration/projection
  • Empty input → ±∞, mirroring Math.min() / Math.max()
  • Non-numeric operands → NaN poisoning so a malformed predicate fails closed against a numeric threshold
  • Coverage is the shared declarative JSON fixtures (min.json / max.json, byte-identical to iOS), run by the existing fixture runner

Note

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 min and max so audience predicates can compute numeric extrema over flat operand lists, aligned with Math.min / Math.max (Android counterpart to iOS).

Implementation lives in new MinMaxOperators: operands are evaluated via evalArgs, coerced with Value.toNumberOrNull (same path as - / / / %), folded with NaN poisoning, and empty args return ±∞. Operators.dispatch routes "min" and "max" to these handlers.

Conformance is covered by shared min.json / max.json fixtures (coercion, empty args, fail-closed thresholds, vars); PredicateFixtureLoaderTest expected 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.

ajpallares and others added 10 commits June 5, 2026 14:11
…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>
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

codecov Bot commented Jun 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.15%. Comparing base (a9acd9f) to head (6940dad).

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

ajpallares and others added 7 commits June 8, 2026 09:45
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>
…teration-operators

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	rules-engine-internal/src/test/kotlin/com/revenuecat/purchases/rules/PredicateFixtureLoaderTest.kt
Base automatically changed from pallares/json-logic-iteration-operators to main June 8, 2026 10:15
ajpallares and others added 4 commits June 8, 2026 12:24
…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>
@ajpallares ajpallares marked this pull request as ready for review June 8, 2026 10:58
@ajpallares ajpallares requested a review from a team as a code owner June 8, 2026 10:58

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it looks good! Just an idea to try to improve the tests, but can be applied separately.

@ajpallares ajpallares added this pull request to the merge queue Jun 8, 2026
Merged via the queue into main with commit cad9ffe Jun 8, 2026
50 checks passed
@ajpallares ajpallares deleted the pallares/json-logic-min-max-operators branch June 8, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants