Add internal RulesEngine evaluate API surface#6966
Merged
ajpallares merged 82 commits intoJun 10, 2026
Conversation
…tures
Convert the hand-written operator unit tests for operators available at the
string+array level into declarative JSON predicate fixtures sharing khepri's
conformance format, run by a shared runner that auto-discovers every file in
PredicateFixtures/. `expected` is a khepri-compatible superset (Bool or
{ "error": ... }) plus optional `description` and `expectedWarnings`.
Adds a test-only Value: Decodable conformance so predicates and variables
decode straight into the engine's value model; production Value is unchanged.
Tests that can't be expressed as predicate->Bool stay in Swift; ValueTests
untouched. Test-only change.
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 40901e5)
Load the in-repo fixtures once and feed the decoded cases straight into the parameterized test, instead of collecting IDs and re-reading every file to find the matching case per ID. PredicateConformanceFixtureCase gains Identifiable + Sendable (for the arguments) and a CustomTestStringConvertible extension so each case still displays by id. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Asserting an exact warning count coupled the fixtures to engine internals. Match warnings by substring only (count-agnostic); an empty `contains` now asserts that no warning is emitted, preserving the "does not warn" cases. The three count-only fixtures gain the "missing variable" substring they actually emit. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the khepri single-file plumbing (defaultFixtureURL, fixtureURL, the no-arg loadCases(), the env var, and the unused fixtureNotFound error). This PR only runs the in-repo PredicateFixtures/ directory; the khepri conformance loader lands with its test in the conformance PR. Co-authored-by: Cursor <cursoragent@cursor.com>
Implements the json-logic-js some/all iteration predicates. Operator coverage is expressed as JSON predicate fixtures (some.json / all.json) run by the shared fixture runner introduced downstack, replacing hand-written unit tests. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit d11529c)
Implements the json-logic-js min/max operators (variadic flat list, ±∞ for empty input, NaN propagation for non-numeric operands). Coverage is expressed as JSON predicate fixtures (min.json / max.json) run by the shared fixture runner, replacing hand-written unit tests. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit e73eabe)
Completes the json-logic-js iteration family (none/map/filter/reduce) on top of some/all. Coverage is expressed as JSON predicate fixtures (none.json / map.json / filter.json / reduce.json) run by the shared fixture runner, replacing hand-written unit tests. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 46d6925)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # RevenueCat.xcodeproj/project.pbxproj
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # RevenueCat.xcodeproj/project.pbxproj
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # RevenueCat.xcodeproj/project.pbxproj
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # RevenueCat.xcodeproj/project.pbxproj
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 [""].
Bumps the pinned fixture count to 237. Keeps the corpus in sync with the
purchases-android counterpart.
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), bumping the pinned count to 244. Also adds the var-null-path Swift test so the non-fixturizable cases match the purchases-android counterpart. Keeps the fixture corpus byte-identical across both repos. Co-authored-by: Cursor <cursoragent@cursor.com>
…-logic-iteration-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
…json-logic-min-max-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
…on-logic-iteration-mapping-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
Exposes the JSON Logic engine's evaluate entry point (and Value / RuleError) at the RulesEngineInternal module level for SDK call sites. No SDK wiring yet. SDK-4308. Co-authored-by: Cursor <cursoragent@cursor.com>
Add doc comments to public Value enum cases and RuleError.description so missing_docs passes under --strict in CI and pre-commit. Co-authored-by: Cursor <cursoragent@cursor.com>
❌ CI Job Failed —
|
Nest the error type under the RulesEngine namespace and update all module and test references. Co-authored-by: Cursor <cursoragent@cursor.com>
Stops misreporting unexpected errors as parse failures, and trims evaluate's doc comment. Co-authored-by: Cursor <cursoragent@cursor.com>
Match the file name to the RulesEngine namespace it declares. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
fromJSONObject already throws EvaluationError, so the rethrow catch is unnecessary. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Brings back the lock-guarded `LoggerStorage` so the default logger can be replaced (host-SDK wiring will expose this in a follow-up PR), while keeping everything internal to the module. Co-authored-by: Cursor <cursoragent@cursor.com>
…8-ios-rules-engine-api-surface
Lets SDK call sites install their own logger via the module-internal public surface. Co-authored-by: Cursor <cursoragent@cursor.com>
rickvdl
approved these changes
Jun 10, 2026
| 709B11FAA2FC6B4929062F25 /* RulesEngineInternal.swift */, | ||
| 709B11FAA2FC6B4929062F25 /* RulesEngine.swift */, | ||
| 9AA7D8F9033399B294E4D4DB /* Logger.swift */, | ||
| 7F1B03A2D59CC0822C02186A /* RuleError.swift */, |
Member
There was a problem hiding this comment.
This file was deleted but is still listed here
|
|
||
| @testable import RulesEngineInternal | ||
|
|
||
| final class RulesEngineEvaluateTests: XCTestCase { |
Member
There was a problem hiding this comment.
I guess we could use Swift testing for this now? Ofc fine to not use it :)
Member
Author
There was a problem hiding this comment.
Oh true! Better to be consistent in the module. Done in 2566ff4
Thanks!
Rename the stale RuleError.swift reference to EvaluationError.swift and register RulesEngineEvaluateTests.swift in the RulesEngineInternalTests target, fixing the Danger project-sync failure. Co-authored-by: Cursor <cursoragent@cursor.com>
Addresses review feedback on #6966 suggesting Swift Testing for the new test file, matching the style of PredicateFixtureTests. Co-authored-by: Cursor <cursoragent@cursor.com>
Value+JSON.swift moved from the test helpers to the RulesEngineInternal module, but the project still compiled it from the old test path. Point the reference at the module source and also register the previously untracked RulesEngineEvaluate.swift in the module target. Co-authored-by: Cursor <cursoragent@cursor.com>
…-rules-engine-api-surface Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # RulesEngineInternal/Logger.swift # RulesEngineInternal/RulesEngine.swift
Member
Author
|
@RCGitBot please test |
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.
Motivation
Expose the minimal entry point for the JSON Logic rules engine so SDK call sites can evaluate predicates. Resolves SDK-4308.
Description
ValuepublicRulesEngine.evaluate(predicate:variables:)public method to theRulesEngineInternalmodule, with failures surfaced asRulesEngine.EvaluationError.evaluatetakes the predicate as a JSON string plus a native[String: Value]scope and returnsResult<Bool, RulesEngine.EvaluationError>.Note on API tests
The public APIs of
RulesEngineInternalare not pinned by API tests because they are not meant for public usage. The SDK should never leak any of these APIs in its public API (enforced by #6778)Note
Low Risk
Changes are confined to the internal rules module and add API surface without SDK integration; evaluation behavior is covered by new and updated tests.
Overview
Exposes a minimal RulesEngineInternal entry point so future SDK code can evaluate JSON Logic predicates without reaching into the evaluator directly.
Public API:
RulesEngineandValueare now public, along withRulesEngineLoggerandRulesEngine.setLogger.RuleErroris replaced by publicRulesEngine.EvaluationError(adds anunknowncase andCustomStringConvertibledescriptions). NewRulesEngine.evaluate(predicate:variables:)accepts predicate JSON as aStringand a[String: Value]scope, returningResult<Bool, EvaluationError>.Parsing:
Value+JSONis compiled into the engine target (moved out of test-only helpers) and is the production path for predicate strings; parse failures map toEvaluationError.parse.Tests / project: Adds
RulesEngineEvaluateTestsfor the new API; existing tests and operators are updated to useRulesEngine.EvaluationError. Xcode project references are renamed/rewired (RulesEngine.swift,EvaluationError.swift,RulesEngineEvaluate.swift). No SDK call-site wiring in this PR.Reviewed by Cursor Bugbot for commit 157f86d. Bugbot is set up for automated code reviews on this repo. Configure here.