diff --git a/core-api/src/main/java/com/optimizely/ab/Optimizely.java b/core-api/src/main/java/com/optimizely/ab/Optimizely.java index c8ca3d55b..acd9d05fd 100644 --- a/core-api/src/main/java/com/optimizely/ab/Optimizely.java +++ b/core-api/src/main/java/com/optimizely/ab/Optimizely.java @@ -1445,6 +1445,11 @@ public int addNotificationHandler(Class clazz, NotificationHandler han } public List fetchQualifiedSegments(String userId, @Nonnull List segmentOptions) { + ProjectConfig projectConfig = getProjectConfig(); + if (projectConfig == null) { + logger.error("Optimizely instance is not valid, failing fetchQualifiedSegments call."); + return null; + } if (odpManager != null) { synchronized (odpManager) { return odpManager.getSegmentManager().getQualifiedSegments(userId, segmentOptions); @@ -1455,6 +1460,12 @@ public List fetchQualifiedSegments(String userId, @Nonnull List segmentOptions) { + ProjectConfig projectConfig = getProjectConfig(); + if (projectConfig == null) { + logger.error("Optimizely instance is not valid, failing fetchQualifiedSegments call."); + callback.onCompleted(null); + return; + } if (odpManager == null) { logger.error("Audience segments fetch failed (ODP is not enabled)."); callback.onCompleted(null); @@ -1478,6 +1489,11 @@ public ODPManager getODPManager() { * @param data a dictionary for associated data. The default event data will be added to this data before sending to the ODP server. */ public void sendODPEvent(@Nullable String type, @Nonnull String action, @Nullable Map identifiers, @Nullable Map data) { + ProjectConfig projectConfig = getProjectConfig(); + if (projectConfig == null) { + logger.error("Optimizely instance is not valid, failing sendODPEvent call."); + return; + } if (odpManager != null) { if (action == null || action.trim().isEmpty()) { logger.error("ODP action is not valid (cannot be empty)."); @@ -1492,6 +1508,11 @@ public void sendODPEvent(@Nullable String type, @Nonnull String action, @Nullabl } public void identifyUser(@Nonnull String userId) { + ProjectConfig projectConfig = getProjectConfig(); + if (projectConfig == null) { + logger.error("Optimizely instance is not valid, failing identifyUser call."); + return; + } ODPManager odpManager = getODPManager(); if (odpManager != null) { odpManager.getEventManager().identifyUser(userId); diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java index 700780f75..260de9945 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java @@ -4733,6 +4733,7 @@ public void initODPManagerWithProjectConfig() throws IOException { @Test public void sendODPEvent() { ProjectConfigManager mockProjectConfigManager = mock(ProjectConfigManager.class); + Mockito.when(mockProjectConfigManager.getConfig()).thenReturn(validProjectConfig); ODPEventManager mockODPEventManager = mock(ODPEventManager.class); ODPManager mockODPManager = mock(ODPManager.class); @@ -4762,6 +4763,33 @@ public void sendODPEvent() { assertEquals(data, eventArgument.getValue().getData()); } + @Test + public void sendODPEventInvalidConfig() { + ProjectConfigManager mockProjectConfigManager = mock(ProjectConfigManager.class); + Mockito.when(mockProjectConfigManager.getConfig()).thenReturn(null); + ODPEventManager mockODPEventManager = mock(ODPEventManager.class); + ODPManager mockODPManager = mock(ODPManager.class); + + Mockito.when(mockODPManager.getEventManager()).thenReturn(mockODPEventManager); + Optimizely optimizely = Optimizely.builder() + .withConfigManager(mockProjectConfigManager) + .withODPManager(mockODPManager) + .build(); + + verify(mockODPEventManager).start(); + + Map identifiers = new HashMap<>(); + identifiers.put("id1", "value1"); + identifiers.put("id2", "value2"); + + Map data = new HashMap<>(); + data.put("sdk", "java"); + data.put("revision", 52); + + optimizely.sendODPEvent("fullstack", "identify", identifiers, data); + logbackVerifier.expectMessage(Level.ERROR, "Optimizely instance is not valid, failing sendODPEvent call."); + } + @Test @SuppressFBWarnings(value = "NP_NONNULL_PARAM_VIOLATION", justification = "Testing nullness contract violation") public void sendODPEventErrorNullAction() { @@ -4887,7 +4915,7 @@ public void sendODPEventEmptyType() { @Test public void sendODPEventError() { ProjectConfigManager mockProjectConfigManager = mock(ProjectConfigManager.class); - + Mockito.when(mockProjectConfigManager.getConfig()).thenReturn(validProjectConfig); Optimizely optimizely = Optimizely.builder() .withConfigManager(mockProjectConfigManager) .build(); @@ -4907,6 +4935,7 @@ public void sendODPEventError() { @Test public void identifyUser() { ProjectConfigManager mockProjectConfigManager = mock(ProjectConfigManager.class); + Mockito.when(mockProjectConfigManager.getConfig()).thenReturn(validProjectConfig); ODPEventManager mockODPEventManager = mock(ODPEventManager.class); ODPManager mockODPManager = mock(ODPManager.class); diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyUserContextTest.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyUserContextTest.java index 8f8bae834..7c479f147 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyUserContextTest.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyUserContextTest.java @@ -1628,6 +1628,8 @@ public void fetchQualifiedSegments() { Mockito.when(mockODPManager.getSegmentManager()).thenReturn(mockODPSegmentManager); Optimizely optimizely = Optimizely.builder() + .withDatafile(datafile) + .withEventProcessor(new ForwardingEventProcessor(eventHandler, null)) .withODPManager(mockODPManager) .build(); @@ -1640,9 +1642,33 @@ public void fetchQualifiedSegments() { verify(mockODPSegmentManager).getQualifiedSegments("test-user", Collections.singletonList(ODPSegmentOption.RESET_CACHE)); } + @Test + public void fetchQualifiedSegmentsErrorWhenConfigIsInvalid() { + ProjectConfigManager mockProjectConfigManager = mock(ProjectConfigManager.class); + Mockito.when(mockProjectConfigManager.getConfig()).thenReturn(null); + ODPEventManager mockODPEventManager = mock(ODPEventManager.class); + ODPSegmentManager mockODPSegmentManager = mock(ODPSegmentManager.class); + ODPManager mockODPManager = mock(ODPManager.class); + + Mockito.when(mockODPManager.getEventManager()).thenReturn(mockODPEventManager); + Mockito.when(mockODPManager.getSegmentManager()).thenReturn(mockODPSegmentManager); + + Optimizely optimizely = Optimizely.builder() + .withConfigManager(mockProjectConfigManager) + .withODPManager(mockODPManager) + .build(); + + OptimizelyUserContext userContext = optimizely.createUserContext("test-user"); + + assertFalse(userContext.fetchQualifiedSegments()); + logbackVerifier.expectMessage(Level.ERROR, "Optimizely instance is not valid, failing fetchQualifiedSegments call."); + } + @Test public void fetchQualifiedSegmentsError() { Optimizely optimizely = Optimizely.builder() + .withDatafile(datafile) + .withEventProcessor(new ForwardingEventProcessor(eventHandler, null)) .build(); OptimizelyUserContext userContext = optimizely.createUserContext("test-user"); @@ -1667,6 +1693,8 @@ public void fetchQualifiedSegmentsAsync() throws InterruptedException { Mockito.when(mockODPManager.getSegmentManager()).thenReturn(mockODPSegmentManager); Optimizely optimizely = Optimizely.builder() + .withDatafile(datafile) + .withEventProcessor(new ForwardingEventProcessor(eventHandler, null)) .withODPManager(mockODPManager) .build(); @@ -1698,6 +1726,8 @@ public void fetchQualifiedSegmentsAsync() throws InterruptedException { @Test public void fetchQualifiedSegmentsAsyncError() throws InterruptedException { Optimizely optimizely = Optimizely.builder() + .withDatafile(datafile) + .withEventProcessor(new ForwardingEventProcessor(eventHandler, null)) .build(); OptimizelyUserContext userContext = optimizely.createUserContext("test-user"); @@ -1713,6 +1743,57 @@ public void fetchQualifiedSegmentsAsyncError() throws InterruptedException { logbackVerifier.expectMessage(Level.ERROR, "Audience segments fetch failed (ODP is not enabled)."); } + @Test + public void fetchQualifiedSegmentsAsyncErrorWhenConfigIsInvalid() throws InterruptedException { + ProjectConfigManager mockProjectConfigManager = mock(ProjectConfigManager.class); + Mockito.when(mockProjectConfigManager.getConfig()).thenReturn(null); + ODPEventManager mockODPEventManager = mock(ODPEventManager.class); + ODPSegmentManager mockODPSegmentManager = mock(ODPSegmentManager.class); + ODPManager mockODPManager = mock(ODPManager.class); + + Mockito.when(mockODPManager.getEventManager()).thenReturn(mockODPEventManager); + Mockito.when(mockODPManager.getSegmentManager()).thenReturn(mockODPSegmentManager); + + Optimizely optimizely = Optimizely.builder() + .withConfigManager(mockProjectConfigManager) + .withODPManager(mockODPManager) + .build(); + + OptimizelyUserContext userContext = optimizely.createUserContext("test-user"); + + CountDownLatch countDownLatch = new CountDownLatch(1); + userContext.fetchQualifiedSegments((Boolean isFetchSuccessful) -> { + assertFalse(isFetchSuccessful); + countDownLatch.countDown(); + }); + + countDownLatch.await(); + assertEquals(null, userContext.getQualifiedSegments()); + logbackVerifier.expectMessage(Level.ERROR, "Optimizely instance is not valid, failing fetchQualifiedSegments call."); + } + + @Test + public void identifyUserErrorWhenConfigIsInvalid() { + ODPEventManager mockODPEventManager = mock(ODPEventManager.class); + ODPSegmentManager mockODPSegmentManager = mock(ODPSegmentManager.class); + ODPManager mockODPManager = mock(ODPManager.class); + ProjectConfigManager mockProjectConfigManager = mock(ProjectConfigManager.class); + Mockito.when(mockProjectConfigManager.getConfig()).thenReturn(null); + Mockito.when(mockODPManager.getEventManager()).thenReturn(mockODPEventManager); + Mockito.when(mockODPManager.getSegmentManager()).thenReturn(mockODPSegmentManager); + + Optimizely optimizely = Optimizely.builder() + .withConfigManager(mockProjectConfigManager) + .withODPManager(mockODPManager) + .build(); + + optimizely.createUserContext("test-user"); + verify(mockODPEventManager, never()).identifyUser("test-user"); + Mockito.reset(mockODPEventManager); + + logbackVerifier.expectMessage(Level.ERROR, "Optimizely instance is not valid, failing identifyUser call."); + } + @Test public void identifyUser() { ODPEventManager mockODPEventManager = mock(ODPEventManager.class); @@ -1723,6 +1804,8 @@ public void identifyUser() { Mockito.when(mockODPManager.getSegmentManager()).thenReturn(mockODPSegmentManager); Optimizely optimizely = Optimizely.builder() + .withDatafile(datafile) + .withEventProcessor(new ForwardingEventProcessor(eventHandler, null)) .withODPManager(mockODPManager) .build();