@@ -148,6 +148,72 @@ void whenNoTargetingKeyAndRolloutIs100_evaluatesToTrue() {
148148 assertThat (subject .evaluate ("feature-x" , false , new MutableContext ()).getValue ()).isTrue ();
149149 }
150150
151+ @ Test
152+ void whenTargetingKeyFallsWithinRolloutPercentage_andSegmentMatchesRequiredSegments_evaluatesToTrue () {
153+ // "evaluation-key:targeting-key" is known to hash to bucket 13
154+ // rollout=13 → within; segment license=trial matches → true
155+ var toggles = new FeatureToggles (
156+ List .of (new FeatureToggleEvaluation ("feature-x" , true , "evaluation-key" , List .of (new Segment ("license" , "trial" )), 13 )),
157+ new byte [0 ]
158+ );
159+ var subject = new OctopusContext (toggles );
160+ var ctx = new MutableContext ();
161+ ctx .setTargetingKey ("targeting-key" );
162+ ctx .add ("license" , "trial" );
163+ assertThat (subject .evaluate ("feature-x" , false , ctx ).getValue ()).isTrue ();
164+ }
165+
166+ @ Test
167+ void whenTargetingKeyFallsWithinRolloutPercentage_andSegmentValueDoesNotMatchRequiredSegment_evaluatesToFalse () {
168+ // "evaluation-key:targeting-key" is known to hash to bucket 13
169+ // rollout=99 → within; but required segment is license=enterprise, context has license=trial → false
170+ var toggles = new FeatureToggles (
171+ List .of (new FeatureToggleEvaluation ("feature-x" , true , "evaluation-key" , List .of (new Segment ("license" , "enterprise" )), 99 )),
172+ new byte [0 ]
173+ );
174+ var subject = new OctopusContext (toggles );
175+ var ctx = new MutableContext ();
176+ ctx .setTargetingKey ("targeting-key" );
177+ ctx .add ("license" , "trial" );
178+ assertThat (subject .evaluate ("feature-x" , false , ctx ).getValue ()).isFalse ();
179+ }
180+
181+ @ Test
182+ void whenTargetingKeyFallsOutsideRolloutPercentage_andSegmentValueDoesNotMatchRequiredSegment_evaluatesToFalse () {
183+ // "evaluation-key:targeting-key" is known to hash to bucket 13 > rollout=12 → outside → false
184+ var toggles = new FeatureToggles (
185+ List .of (new FeatureToggleEvaluation ("feature-x" , true , "evaluation-key" , List .of (new Segment ("license" , "enterprise" )), 12 )),
186+ new byte [0 ]
187+ );
188+ var subject = new OctopusContext (toggles );
189+ var ctx = new MutableContext ();
190+ ctx .setTargetingKey ("targeting-key" );
191+ ctx .add ("license" , "trial" );
192+ assertThat (subject .evaluate ("feature-x" , false , ctx ).getValue ()).isFalse ();
193+ }
194+
195+ @ Test
196+ void whenFeatureHasRequiredSegment_toleratesNullValuesInContext () {
197+ var toggles = new FeatureToggles (
198+ List .of (new FeatureToggleEvaluation ("feature-x" , true , "evaluation-key" , List .of (new Segment ("license" , "trial" )), 100 )),
199+ new byte [0 ]
200+ );
201+ var subject = new OctopusContext (toggles );
202+
203+ // null value for the segment key → does not match
204+ var ctxNullLicense = new MutableContext ();
205+ ctxNullLicense .add ("license" , (String ) null );
206+ assertThat (subject .evaluate ("feature-x" , false , ctxNullLicense ).getValue ()).isFalse ();
207+
208+ // unrelated segment key → does not match
209+ var ctxOtherSegment = new MutableContext ();
210+ ctxOtherSegment .add ("other" , "segment" );
211+ assertThat (subject .evaluate ("feature-x" , false , ctxOtherSegment ).getValue ()).isFalse ();
212+
213+ // null context → does not match
214+ assertThat (subject .evaluate ("feature-x" , false , null ).getValue ()).isFalse ();
215+ }
216+
151217 @ TestFactory
152218 Iterable <DynamicTest > shouldCorrectlyDynamicallyEvaluateSegmentsWhenSupplied () {
153219 return Arrays .asList (
0 commit comments