From 1e7aba1ba8c3dff892f92ab3fafe952b4bec056e Mon Sep 17 00:00:00 2001 From: wadii Date: Thu, 7 May 2026 10:17:52 +0200 Subject: [PATCH] fix: respect rule type for sub-rules in segment evaluation Sub-rules were always evaluated with ALL logic, ignoring the parent rule's type field (ANY/ALL/NONE). Now uses type-aware matching for sub-rules, matching the existing conditions behavior. Bumps engine-test-data from v3.5.0 to v3.7.0 which includes test cases for this fix. Equivalent fix to flagsmith-engine#313. Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitmodules | 2 +- flagengine/engine-test-data | 2 +- flagengine/engine_eval/evaluator.go | 28 +++++++++++++++++++++++++--- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index f6d7a610..5226160f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "flagengine/engine-test-data"] path = flagengine/engine-test-data url = git@github.com:Flagsmith/engine-test-data.git - tag = v3.5.0 + tag = v3.7.0 diff --git a/flagengine/engine-test-data b/flagengine/engine-test-data index 7840a134..4b29dc77 160000 --- a/flagengine/engine-test-data +++ b/flagengine/engine-test-data @@ -1 +1 @@ -Subproject commit 7840a1349b601df3b6b4a089f40864f659801afb +Subproject commit 4b29dc772a764364af2dd504ecefbdf74cf5473f diff --git a/flagengine/engine_eval/evaluator.go b/flagengine/engine_eval/evaluator.go index 78d4d329..8a1802d5 100644 --- a/flagengine/engine_eval/evaluator.go +++ b/flagengine/engine_eval/evaluator.go @@ -58,12 +58,34 @@ func contextMatchesSegmentRule(ec *EngineEvaluationContext, segmentRule *Segment } } - for i := range segmentRule.Rules { - if !contextMatchesSegmentRule(ec, &segmentRule.Rules[i], segmentKey) { + return matchesSubRulesByRuleType(ec, segmentRule.Rules, segmentRule.Type, segmentKey) +} + +// matchesSubRulesByRuleType evaluates sub-rules using the parent rule's type (ALL/ANY/NONE). +func matchesSubRulesByRuleType(ec *EngineEvaluationContext, subRules []SegmentRule, ruleType Type, segmentKey string) bool { + if len(subRules) == 0 { + return true + } + for i := range subRules { + subRuleMatches := contextMatchesSegmentRule(ec, &subRules[i], segmentKey) + switch ruleType { + case All: + if !subRuleMatches { + return false + } + case None: + if subRuleMatches { + return false + } + case Any: + if subRuleMatches { + return true + } + default: return false } } - return true + return ruleType != Any } func matchPercentageSplit(ec *EngineEvaluationContext, segmentCondition *Condition, segmentKey string, contextValue ContextValue) bool {