Skip to content

test: cover $inGroup/$notInGroup for an absent attribute - #287

Merged
andywhite37 merged 2 commits into
mainfrom
fix/notingroup-absent-attribute
Nov 4, 2026
Merged

andywhite37 merged 2 commits into
mainfrom
fix/notingroup-absent-attribute

Conversation

@vazarkevych

Copy link
Copy Markdown
Collaborator

fix: correct $notInGroup for an absent attribute

$inGroup and $notInGroup both returned false for a missing attribute — a logical contradiction; $notInGroup now returns true, matching the JS SDK

lib only · evaluation behavior change · no new dependencies · JS parity

build
language level
new tests
new dependencies

Branch: fix/notingroup-absent-attribute
Commit: 938d252 (fix)


Summary

ConditionEvaluator.evalSavedGroup returned false for both $inGroup and $notInGroup
whenever the targeted attribute was absent, because of an early actual == null guard. Since the two
operators are logical negations of each other, collapsing both to false is internally inconsistent —
at least one must be wrong.

Per the reference JS SDK (packages/sdk-js/src/mongrule.ts), $notInGroup on an absent attribute
must be true.


The gap

operator attribute group correct before
$inGroup member present true true
$inGroup non-member present false false
$inGroup absent any false false
$notInGroup member present false false
$notInGroup non-member present true true
$notInGroup absent any true false ← wrong
$notInGroup unknown group any true true

The conformance corpus (spec 0.11.3) covers member / non-member / unknown-group / type-mismatch, but
has no absent-attribute $notInGroup case, so the gap passed CI unnoticed.


The fix

  • Drop the actual == null guard and let isIn — which already treats a null attribute as a
    non-member — drive both operators, so they agree with the JS truth table across
    member / non-member / absent / unknown-group inputs.
  • Resolve a non-array saved-group value to an empty group (isJsonArray() check) instead of throwing
    from getAsJsonArray().

$inGroup behavior is unchanged for every input; only $notInGroup on an absent attribute flips
false → true. Lives in ConditionEvaluator, shared by both the GrowthBook and GrowthBookClient
entry points.


Tests

Two new ConditionEvaluatorTest cases (the corpus can't cover this path):

  • test_savedGroupOperatorsMatchReferenceTruthTable — the full $inGroup/$notInGroup truth
    table, including the absent-attribute and unknown-group rows.
  • test_savedGroupOperatorsTolerateNonArrayGroupValue — a non-array saved-group value resolves to
    an empty group instead of throwing.

Both pass on JDK 17.

evalSavedGroup returned false for both $inGroup and $notInGroup whenever
the targeted attribute was absent, because of an early `actual == null`
guard. Since the two operators are logical negations, collapsing both to
false is inconsistent: per the JS SDK (packages/sdk-js/src/mongrule.ts),
$notInGroup on an absent attribute must be true.

Drop the actual-null guard and let isIn (which already treats null as a
non-member) drive both operators, so they match the JS truth table across
member / non-member / absent / unknown-group inputs. Also resolve a
non-array saved-group value to an empty group instead of throwing from
getAsJsonArray(). The conformance corpus has no absent-attribute
$notInGroup case, so this gap is covered by dedicated unit tests.
@greptile-apps

greptile-apps Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge.

Reviews (1) · Last reviewed commit: "fix: correct $notInGroup for an absent a..." · Reviewed by Greptile

…-attribute

# Conflicts:
#	lib/src/main/java/growthbook/sdk/java/evaluators/ConditionEvaluator.java
#	lib/src/test/java/growthbook/sdk/java/ConditionEvaluatorTest.java
@andywhite37 andywhite37 added effort:small Narrow change with limited review and testing work. priority:low Can wait because impact or urgency is limited. risk:low Limited potential impact; does not imply approval or completed testing. labels Nov 4, 2026
@andywhite37 andywhite37 changed the title fix: correct $notInGroup for an absent attribute test: cover $inGroup/$notInGroup for an absent attribute Nov 4, 2026

@andywhite37 andywhite37 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.

Thanks, these tests are useful. The fix already reached main with spec 0.12.0, so I've retitled this to test: to keep it out of the release notes.

@andywhite37
andywhite37 merged commit 673560c into main Nov 4, 2026
8 checks passed
@andywhite37
andywhite37 deleted the fix/notingroup-absent-attribute branch November 4, 2026 22:16
vazarkevych pushed a commit that referenced this pull request Nov 6, 2026
Brings in spec 0.12.0 alignment (#284), the 0.12.0 release and the
$inGroup/$notInGroup absent-attribute tests (#287).

Conflicts:
- GrowthBookClient: keep both new imports (FeatureRule from this branch,
  ReducePayloadOptions from main).
- GrowthBookClientTest: keep main's two sticky-preload regression tests.
  They fail against the scoped async read path here; the next commit
  fixes the code they exposed and scopes them to identifier attributes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort:small Narrow change with limited review and testing work. priority:low Can wait because impact or urgency is limited. risk:low Limited potential impact; does not imply approval or completed testing.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants