|
23 | 23 | import com.optimizely.ab.config.Attribute;
|
24 | 24 | import com.optimizely.ab.config.EventType;
|
25 | 25 | import com.optimizely.ab.config.Experiment;
|
| 26 | +import com.optimizely.ab.config.FeatureFlag; |
26 | 27 | import com.optimizely.ab.config.LiveVariable;
|
27 | 28 | import com.optimizely.ab.config.LiveVariableUsageInstance;
|
28 | 29 | import com.optimizely.ab.config.ProjectConfig;
|
|
36 | 37 | import com.optimizely.ab.event.LogEvent;
|
37 | 38 | import com.optimizely.ab.event.internal.EventBuilder;
|
38 | 39 | import com.optimizely.ab.event.internal.EventBuilderV2;
|
| 40 | +import com.optimizely.ab.event.internal.payload.Feature; |
39 | 41 | import com.optimizely.ab.internal.LogbackVerifier;
|
40 | 42 | import com.optimizely.ab.notification.NotificationListener;
|
41 | 43 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
|
97 | 99 | import static org.hamcrest.Matchers.hasEntry;
|
98 | 100 | import static org.hamcrest.Matchers.hasKey;
|
99 | 101 | import static org.junit.Assert.assertEquals;
|
| 102 | +import static org.junit.Assert.assertFalse; |
100 | 103 | import static org.junit.Assert.assertNotNull;
|
101 | 104 | import static org.junit.Assert.assertNull;
|
102 | 105 | import static org.junit.Assume.assumeTrue;
|
@@ -2371,6 +2374,166 @@ public void getFeatureVariableValueReturnsVariationValueWhenUserGetsBucketedToVa
|
2371 | 2374 | assertEquals(expectedValue, value);
|
2372 | 2375 | }
|
2373 | 2376 |
|
| 2377 | + /** |
| 2378 | + * Verify {@link Optimizely#isFeatureEnabled(String, String)} calls into |
| 2379 | + * {@link Optimizely#isFeatureEnabled(String, String, Map)} and they both |
| 2380 | + * return False |
| 2381 | + * when the APIs are called with an feature key that is not in the datafile. |
| 2382 | + * @throws Exception |
| 2383 | + */ |
| 2384 | + @Test |
| 2385 | + public void isFeatureEnabledReturnsFalseWhenFeatureFlagKeyIsInvalid() throws Exception { |
| 2386 | + |
| 2387 | + String invalidFeatureKey = "nonexistent feature key"; |
| 2388 | + |
| 2389 | + Optimizely spyOptimizely = spy(Optimizely.builder(validDatafile, mockEventHandler) |
| 2390 | + .withConfig(validProjectConfig) |
| 2391 | + .withDecisionService(mockDecisionService) |
| 2392 | + .build()); |
| 2393 | + |
| 2394 | + assertFalse(spyOptimizely.isFeatureEnabled(invalidFeatureKey, genericUserId)); |
| 2395 | + |
| 2396 | + logbackVerifier.expectMessage( |
| 2397 | + Level.INFO, |
| 2398 | + "No feature flag was found for key \"" + invalidFeatureKey + "\"." |
| 2399 | + ); |
| 2400 | + verify(spyOptimizely, times(1)).isFeatureEnabled( |
| 2401 | + eq(invalidFeatureKey), |
| 2402 | + eq(genericUserId), |
| 2403 | + eq(Collections.<String, String>emptyMap()) |
| 2404 | + ); |
| 2405 | + verify(mockDecisionService, never()).getVariation( |
| 2406 | + any(Experiment.class), |
| 2407 | + anyString(), |
| 2408 | + anyMapOf(String.class, String.class)); |
| 2409 | + verify(mockEventHandler, never()).dispatchEvent(any(LogEvent.class)); |
| 2410 | + } |
| 2411 | + |
| 2412 | + /** |
| 2413 | + * Verify {@link Optimizely#isFeatureEnabled(String, String)} calls into |
| 2414 | + * {@link Optimizely#isFeatureEnabled(String, String, Map)} and they both |
| 2415 | + * return False |
| 2416 | + * when the user is not bucketed into any variation for the feature. |
| 2417 | + * @throws Exception |
| 2418 | + */ |
| 2419 | + @Test |
| 2420 | + public void isFeatureEnabledReturnsFalseWhenUserIsNotBucketedIntoAnyVariation() throws Exception { |
| 2421 | + assumeTrue(datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString())); |
| 2422 | + |
| 2423 | + String validFeatureKey = FEATURE_MULTI_VARIATE_FEATURE_KEY; |
| 2424 | + |
| 2425 | + Optimizely spyOptimizely = spy(Optimizely.builder(validDatafile, mockEventHandler) |
| 2426 | + .withConfig(validProjectConfig) |
| 2427 | + .withDecisionService(mockDecisionService) |
| 2428 | + .build()); |
| 2429 | + |
| 2430 | + doReturn(null).when(mockDecisionService).getVariationForFeature( |
| 2431 | + any(FeatureFlag.class), |
| 2432 | + anyString(), |
| 2433 | + anyMapOf(String.class, String.class) |
| 2434 | + ); |
| 2435 | + |
| 2436 | + assertFalse(spyOptimizely.isFeatureEnabled(validFeatureKey, genericUserId)); |
| 2437 | + |
| 2438 | + logbackVerifier.expectMessage( |
| 2439 | + Level.INFO, |
| 2440 | + "Feature \"" + validFeatureKey + |
| 2441 | + "\" is not enabled for user \"" + genericUserId + "\"." |
| 2442 | + ); |
| 2443 | + verify(spyOptimizely).isFeatureEnabled( |
| 2444 | + eq(validFeatureKey), |
| 2445 | + eq(genericUserId), |
| 2446 | + eq(Collections.<String, String>emptyMap()) |
| 2447 | + ); |
| 2448 | + verify(mockDecisionService).getVariationForFeature( |
| 2449 | + eq(FEATURE_FLAG_MULTI_VARIATE_FEATURE), |
| 2450 | + eq(genericUserId), |
| 2451 | + eq(Collections.<String, String>emptyMap()) |
| 2452 | + ); |
| 2453 | + verify(mockEventHandler, never()).dispatchEvent(any(LogEvent.class)); |
| 2454 | + } |
| 2455 | + |
| 2456 | + /** |
| 2457 | + * Verify {@link Optimizely#isFeatureEnabled(String, String)} calls into |
| 2458 | + * {@link Optimizely#isFeatureEnabled(String, String, Map)} and they both |
| 2459 | + * return True |
| 2460 | + * when the user is bucketed into a variation for the feature. |
| 2461 | + * An impression event should not be dispatched since the user was not bucketed into an Experiment. |
| 2462 | + * @throws Exception |
| 2463 | + */ |
| 2464 | + @Test |
| 2465 | + public void isFeatureEnabledReturnsTrueButDoesNotSendWhenUserIsBucketedIntoVariationWithoutExperiment() throws Exception { |
| 2466 | + assumeTrue(datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString())); |
| 2467 | + |
| 2468 | + String validFeatureKey = FEATURE_MULTI_VARIATE_FEATURE_KEY; |
| 2469 | + |
| 2470 | + Optimizely spyOptimizely = spy(Optimizely.builder(validDatafile, mockEventHandler) |
| 2471 | + .withConfig(validProjectConfig) |
| 2472 | + .withDecisionService(mockDecisionService) |
| 2473 | + .build()); |
| 2474 | + |
| 2475 | + doReturn(new Variation("variationId", "variationKey")).when(mockDecisionService).getVariationForFeature( |
| 2476 | + eq(FEATURE_FLAG_MULTI_VARIATE_FEATURE), |
| 2477 | + eq(genericUserId), |
| 2478 | + eq(Collections.<String, String>emptyMap()) |
| 2479 | + ); |
| 2480 | + |
| 2481 | + assertTrue(spyOptimizely.isFeatureEnabled(validFeatureKey, genericUserId)); |
| 2482 | + |
| 2483 | + logbackVerifier.expectMessage( |
| 2484 | + Level.INFO, |
| 2485 | + "The user \"" + genericUserId + |
| 2486 | + "\" is not being experimented on in feature \"" + validFeatureKey + "\"." |
| 2487 | + ); |
| 2488 | + logbackVerifier.expectMessage( |
| 2489 | + Level.INFO, |
| 2490 | + "Feature \"" + validFeatureKey + |
| 2491 | + "\" is enabled for user \"" + genericUserId + "\"." |
| 2492 | + ); |
| 2493 | + verify(spyOptimizely).isFeatureEnabled( |
| 2494 | + eq(validFeatureKey), |
| 2495 | + eq(genericUserId), |
| 2496 | + eq(Collections.<String, String>emptyMap()) |
| 2497 | + ); |
| 2498 | + verify(mockDecisionService).getVariationForFeature( |
| 2499 | + eq(FEATURE_FLAG_MULTI_VARIATE_FEATURE), |
| 2500 | + eq(genericUserId), |
| 2501 | + eq(Collections.<String, String>emptyMap()) |
| 2502 | + ); |
| 2503 | + verify(mockEventHandler, never()).dispatchEvent(any(LogEvent.class)); |
| 2504 | + } |
| 2505 | + |
| 2506 | + /** Integration Test |
| 2507 | + * Verify {@link Optimizely#isFeatureEnabled(String, String, Map)} |
| 2508 | + * returns True |
| 2509 | + * when the user is bucketed into a variation for the feature. |
| 2510 | + * The user is also bucketed into an experiment, so we verify that an event is dispatched. |
| 2511 | + * @throws Exception |
| 2512 | + */ |
| 2513 | + @Test |
| 2514 | + public void isFeatureEnabledReturnsTrueAndDispatchesEventWhenUserIsBucketedIntoAnExperiment() throws Exception { |
| 2515 | + assumeTrue(datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString())); |
| 2516 | + |
| 2517 | + String validFeatureKey = FEATURE_MULTI_VARIATE_FEATURE_KEY; |
| 2518 | + |
| 2519 | + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) |
| 2520 | + .withConfig(validProjectConfig) |
| 2521 | + .build(); |
| 2522 | + |
| 2523 | + assertTrue(optimizely.isFeatureEnabled( |
| 2524 | + validFeatureKey, |
| 2525 | + genericUserId, |
| 2526 | + Collections.singletonMap(ATTRIBUTE_HOUSE_KEY, AUDIENCE_GRYFFINDOR_VALUE) |
| 2527 | + )); |
| 2528 | + |
| 2529 | + logbackVerifier.expectMessage( |
| 2530 | + Level.INFO, |
| 2531 | + "Feature \"" + validFeatureKey + |
| 2532 | + "\" is enabled for user \"" + genericUserId + "\"." |
| 2533 | + ); |
| 2534 | + verify(mockEventHandler, times(1)).dispatchEvent(any(LogEvent.class)); |
| 2535 | + } |
| 2536 | + |
2374 | 2537 | //======== Helper methods ========//
|
2375 | 2538 |
|
2376 | 2539 | private Experiment createUnknownExperiment() {
|
|
0 commit comments