From 72e91e7f7a2ec99b12b72906dd583535a26e2b59 Mon Sep 17 00:00:00 2001 From: Gavin Whelan Date: Fri, 22 Nov 2019 22:17:28 +0000 Subject: [PATCH] [ch56562] More consistency with .NET and fix flaky tests. (#149) --- .../com/launchdarkly/client/Components.java | 19 +- .../client/DefaultEventProcessor.java | 20 +-- .../client/DiagnosticAccumulator.java | 6 +- .../launchdarkly/client/DiagnosticEvent.java | 2 - .../EventProcessorFactoryWithDiagnostics.java | 6 + .../com/launchdarkly/client/LDClient.java | 28 ++- .../com/launchdarkly/client/LDConfig.java | 10 +- .../launchdarkly/client/StreamProcessor.java | 4 +- ...UpdateProcessorFactoryWithDiagnostics.java | 6 + .../client/DefaultEventProcessorTest.java | 167 ++++++++++-------- .../client/DiagnosticAccumulatorTest.java | 32 +--- .../client/DiagnosticEventTest.java | 2 - .../com/launchdarkly/client/LDClientTest.java | 100 ++++++++++- .../client/StreamProcessorTest.java | 8 +- 14 files changed, 264 insertions(+), 146 deletions(-) create mode 100644 src/main/java/com/launchdarkly/client/EventProcessorFactoryWithDiagnostics.java create mode 100644 src/main/java/com/launchdarkly/client/UpdateProcessorFactoryWithDiagnostics.java diff --git a/src/main/java/com/launchdarkly/client/Components.java b/src/main/java/com/launchdarkly/client/Components.java index 65c993869..e46dfd733 100644 --- a/src/main/java/com/launchdarkly/client/Components.java +++ b/src/main/java/com/launchdarkly/client/Components.java @@ -88,13 +88,18 @@ public FeatureStore createFeatureStore() { } } - private static final class DefaultEventProcessorFactory implements EventProcessorFactory { + private static final class DefaultEventProcessorFactory implements EventProcessorFactoryWithDiagnostics { @Override public EventProcessor createEventProcessor(String sdkKey, LDConfig config) { + return createEventProcessor(sdkKey, config, null); + } + + public EventProcessor createEventProcessor(String sdkKey, LDConfig config, + DiagnosticAccumulator diagnosticAccumulator) { if (config.offline || !config.sendEvents) { return new EventProcessor.NullEventProcessor(); } else { - return new DefaultEventProcessor(sdkKey, config); + return new DefaultEventProcessor(sdkKey, config, diagnosticAccumulator); } } } @@ -105,12 +110,18 @@ public EventProcessor createEventProcessor(String sdkKey, LDConfig config) { } } - private static final class DefaultUpdateProcessorFactory implements UpdateProcessorFactory { + private static final class DefaultUpdateProcessorFactory implements UpdateProcessorFactoryWithDiagnostics { // Note, logger uses LDClient class name for backward compatibility private static final Logger logger = LoggerFactory.getLogger(LDClient.class); @Override public UpdateProcessor createUpdateProcessor(String sdkKey, LDConfig config, FeatureStore featureStore) { + return createUpdateProcessor(sdkKey, config, featureStore, null); + } + + @Override + public UpdateProcessor createUpdateProcessor(String sdkKey, LDConfig config, FeatureStore featureStore, + DiagnosticAccumulator diagnosticAccumulator) { if (config.offline) { logger.info("Starting LaunchDarkly client in offline mode"); return new UpdateProcessor.NullUpdateProcessor(); @@ -121,7 +132,7 @@ public UpdateProcessor createUpdateProcessor(String sdkKey, LDConfig config, Fea DefaultFeatureRequestor requestor = new DefaultFeatureRequestor(sdkKey, config); if (config.stream) { logger.info("Enabling streaming API"); - return new StreamProcessor(sdkKey, config, requestor, featureStore, null); + return new StreamProcessor(sdkKey, config, requestor, featureStore, null, diagnosticAccumulator); } else { logger.info("Disabling streaming API"); logger.warn("You should only disable the streaming API if instructed to do so by LaunchDarkly support"); diff --git a/src/main/java/com/launchdarkly/client/DefaultEventProcessor.java b/src/main/java/com/launchdarkly/client/DefaultEventProcessor.java index 639229484..637aa251c 100644 --- a/src/main/java/com/launchdarkly/client/DefaultEventProcessor.java +++ b/src/main/java/com/launchdarkly/client/DefaultEventProcessor.java @@ -50,7 +50,7 @@ final class DefaultEventProcessor implements EventProcessor { private final AtomicBoolean closed = new AtomicBoolean(false); private volatile boolean inputCapacityExceeded = false; - DefaultEventProcessor(String sdkKey, LDConfig config) { + DefaultEventProcessor(String sdkKey, LDConfig config, DiagnosticAccumulator diagnosticAccumulator) { inbox = new ArrayBlockingQueue<>(config.capacity); ThreadFactory threadFactory = new ThreadFactoryBuilder() @@ -60,7 +60,7 @@ final class DefaultEventProcessor implements EventProcessor { .build(); scheduler = Executors.newSingleThreadScheduledExecutor(threadFactory); - new EventDispatcher(sdkKey, config, inbox, threadFactory, closed); + new EventDispatcher(sdkKey, config, inbox, threadFactory, closed, diagnosticAccumulator); Runnable flusher = new Runnable() { public void run() { @@ -75,7 +75,7 @@ public void run() { }; this.scheduler.scheduleAtFixedRate(userKeysFlusher, config.userKeysFlushInterval, config.userKeysFlushInterval, TimeUnit.SECONDS); - if (!config.diagnosticOptOut) { + if (!config.diagnosticOptOut && diagnosticAccumulator != null) { Runnable diagnosticsTrigger = new Runnable() { public void run() { postMessageAsync(MessageType.DIAGNOSTIC, null); @@ -207,6 +207,7 @@ static final class EventDispatcher { private final Random random = new Random(); private final AtomicLong lastKnownPastTime = new AtomicLong(0); private final AtomicBoolean disabled = new AtomicBoolean(false); + private final DiagnosticAccumulator diagnosticAccumulator; private final ExecutorService diagnosticExecutor; private final SendDiagnosticTaskFactory sendDiagnosticTaskFactory; @@ -215,8 +216,10 @@ static final class EventDispatcher { private EventDispatcher(String sdkKey, LDConfig config, final BlockingQueue inbox, ThreadFactory threadFactory, - final AtomicBoolean closed) { + final AtomicBoolean closed, + DiagnosticAccumulator diagnosticAccumulator) { this.config = config; + this.diagnosticAccumulator = diagnosticAccumulator; this.busyFlushWorkersCount = new AtomicInteger(0); OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder(); @@ -270,14 +273,11 @@ public void handleResponse(Response response, Date responseDate) { flushWorkers.add(task); } - if (!config.diagnosticOptOut) { + if (!config.diagnosticOptOut && diagnosticAccumulator != null) { // Set up diagnostics - long currentTime = System.currentTimeMillis(); - DiagnosticId diagnosticId = new DiagnosticId(sdkKey); - config.diagnosticAccumulator.start(diagnosticId, currentTime); this.sendDiagnosticTaskFactory = new SendDiagnosticTaskFactory(sdkKey, config, httpClient); diagnosticExecutor = Executors.newSingleThreadExecutor(threadFactory); - DiagnosticEvent.Init diagnosticInitEvent = new DiagnosticEvent.Init(currentTime, diagnosticId, config); + DiagnosticEvent.Init diagnosticInitEvent = new DiagnosticEvent.Init(diagnosticAccumulator.dataSinceDate, diagnosticAccumulator.diagnosticId, config); diagnosticExecutor.submit(sendDiagnosticTaskFactory.createSendDiagnosticTask(diagnosticInitEvent)); } else { diagnosticExecutor = null; @@ -334,7 +334,7 @@ private void runMainLoop(BlockingQueue inbox, private void sendAndResetDiagnostics(EventBuffer outbox) { long droppedEvents = outbox.getAndClearDroppedCount(); long eventsInQueue = outbox.getEventsInQueueCount(); - DiagnosticEvent diagnosticEvent = config.diagnosticAccumulator.createEventAndReset(droppedEvents, deduplicatedUsers, eventsInQueue); + DiagnosticEvent diagnosticEvent = diagnosticAccumulator.createEventAndReset(droppedEvents, deduplicatedUsers, eventsInQueue); deduplicatedUsers = 0; diagnosticExecutor.submit(sendDiagnosticTaskFactory.createSendDiagnosticTask(diagnosticEvent)); } diff --git a/src/main/java/com/launchdarkly/client/DiagnosticAccumulator.java b/src/main/java/com/launchdarkly/client/DiagnosticAccumulator.java index 1ee9b33a0..96068bc1a 100644 --- a/src/main/java/com/launchdarkly/client/DiagnosticAccumulator.java +++ b/src/main/java/com/launchdarkly/client/DiagnosticAccumulator.java @@ -2,12 +2,12 @@ class DiagnosticAccumulator { + final DiagnosticId diagnosticId; volatile long dataSinceDate; - volatile DiagnosticId diagnosticId; - void start(DiagnosticId diagnosticId, long dataSinceDate) { + DiagnosticAccumulator(DiagnosticId diagnosticId) { this.diagnosticId = diagnosticId; - this.dataSinceDate = dataSinceDate; + this.dataSinceDate = System.currentTimeMillis(); } DiagnosticEvent.Statistics createEventAndReset(long droppedEvents, long deduplicatedUsers, long eventsInQueue) { diff --git a/src/main/java/com/launchdarkly/client/DiagnosticEvent.java b/src/main/java/com/launchdarkly/client/DiagnosticEvent.java index 40c25eb50..284365723 100644 --- a/src/main/java/com/launchdarkly/client/DiagnosticEvent.java +++ b/src/main/java/com/launchdarkly/client/DiagnosticEvent.java @@ -55,7 +55,6 @@ static class DiagnosticConfiguration { private final boolean usingRelayDaemon; private final boolean offline; private final boolean allAttributesPrivate; - private final boolean eventReportingDisabled; private final long pollingIntervalMillis; private final long startWaitMillis; private final int samplingInterval; @@ -80,7 +79,6 @@ static class DiagnosticConfiguration { this.usingRelayDaemon = config.useLdd; this.offline = config.offline; this.allAttributesPrivate = config.allAttributesPrivate; - this.eventReportingDisabled = !config.sendEvents; this.pollingIntervalMillis = config.pollingIntervalMillis; this.startWaitMillis = config.startWaitMillis; this.samplingInterval = config.samplingInterval; diff --git a/src/main/java/com/launchdarkly/client/EventProcessorFactoryWithDiagnostics.java b/src/main/java/com/launchdarkly/client/EventProcessorFactoryWithDiagnostics.java new file mode 100644 index 000000000..fac2c631a --- /dev/null +++ b/src/main/java/com/launchdarkly/client/EventProcessorFactoryWithDiagnostics.java @@ -0,0 +1,6 @@ +package com.launchdarkly.client; + +interface EventProcessorFactoryWithDiagnostics extends EventProcessorFactory { + EventProcessor createEventProcessor(String sdkKey, LDConfig config, + DiagnosticAccumulator diagnosticAccumulator); +} diff --git a/src/main/java/com/launchdarkly/client/LDClient.java b/src/main/java/com/launchdarkly/client/LDClient.java index a6013bb82..67990f22e 100644 --- a/src/main/java/com/launchdarkly/client/LDClient.java +++ b/src/main/java/com/launchdarkly/client/LDClient.java @@ -78,14 +78,32 @@ public LDClient(String sdkKey, LDConfig config) { this.shouldCloseFeatureStore = true; } this.featureStore = new FeatureStoreClientWrapper(store); - + EventProcessorFactory epFactory = this.config.eventProcessorFactory == null ? Components.defaultEventProcessor() : this.config.eventProcessorFactory; - this.eventProcessor = epFactory.createEventProcessor(sdkKey, this.config); - UpdateProcessorFactory upFactory = this.config.updateProcessorFactory == null ? - Components.defaultUpdateProcessor() : this.config.updateProcessorFactory; - this.updateProcessor = upFactory.createUpdateProcessor(sdkKey, this.config, featureStore); + Components.defaultUpdateProcessor() : this.config.updateProcessorFactory; + + DiagnosticAccumulator diagnosticAccumulator = null; + // Do not create accumulator if config has specified is opted out, or if epFactory doesn't support diagnostics + if (!this.config.diagnosticOptOut && epFactory instanceof EventProcessorFactoryWithDiagnostics) { + diagnosticAccumulator = new DiagnosticAccumulator(new DiagnosticId(sdkKey)); + } + + if (epFactory instanceof EventProcessorFactoryWithDiagnostics) { + EventProcessorFactoryWithDiagnostics epwdFactory = ((EventProcessorFactoryWithDiagnostics) epFactory); + this.eventProcessor = epwdFactory.createEventProcessor(sdkKey, this.config, diagnosticAccumulator); + } else { + this.eventProcessor = epFactory.createEventProcessor(sdkKey, this.config); + } + + if (upFactory instanceof UpdateProcessorFactoryWithDiagnostics) { + UpdateProcessorFactoryWithDiagnostics upwdFactory = ((UpdateProcessorFactoryWithDiagnostics) upFactory); + this.updateProcessor = upwdFactory.createUpdateProcessor(sdkKey, this.config, featureStore, diagnosticAccumulator); + } else { + this.updateProcessor = upFactory.createUpdateProcessor(sdkKey, this.config, featureStore); + } + Future startFuture = updateProcessor.start(); if (this.config.startWaitMillis > 0L) { if (!this.config.offline && !this.config.useLdd) { diff --git a/src/main/java/com/launchdarkly/client/LDConfig.java b/src/main/java/com/launchdarkly/client/LDConfig.java index 18d0c4cc7..cf4fe15ff 100644 --- a/src/main/java/com/launchdarkly/client/LDConfig.java +++ b/src/main/java/com/launchdarkly/client/LDConfig.java @@ -83,8 +83,6 @@ public final class LDConfig { final TimeUnit connectTimeoutUnit; final int socketTimeout; final TimeUnit socketTimeoutUnit; - - DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(); protected LDConfig(Builder builder) { this.baseURI = builder.baseURI; @@ -628,7 +626,7 @@ public Builder inlineUsersInEvents(boolean inlineUsersInEvents) { /** * Sets the interval at which periodic diagnostic data is sent. The default is every 15 minutes (900,000 - * milliseconds) and the minimum value is 6000. + * milliseconds) and the minimum value is 60,000. * * @see #diagnosticOptOut(boolean) * @@ -658,8 +656,8 @@ public Builder diagnosticOptOut(boolean diagnosticOptOut) { } /** - * For use by wrapper libraries to set an identifying name for the wrapper being used. This will be sent in - * User-Agent headers during requests to the LaunchDarkly servers to allow recording metrics on the usage of + * For use by wrapper libraries to set an identifying name for the wrapper being used. This will be included in a + * header during requests to the LaunchDarkly servers to allow recording metrics on the usage of * these wrapper libraries. * * @param wrapperName an identifying name for the wrapper library @@ -672,7 +670,7 @@ public Builder wrapperName(String wrapperName) { /** * For use by wrapper libraries to report the version of the library in use. If {@link #wrapperName(String)} is not - * set, this field will be ignored. Otherwise the version string will be included in the User-Agent headers along + * set, this field will be ignored. Otherwise the version string will be included in a header along * with the wrapperName during requests to the LaunchDarkly servers. * * @param wrapperVersion Version string for the wrapper library diff --git a/src/main/java/com/launchdarkly/client/StreamProcessor.java b/src/main/java/com/launchdarkly/client/StreamProcessor.java index fb0d2c09d..477c37a93 100644 --- a/src/main/java/com/launchdarkly/client/StreamProcessor.java +++ b/src/main/java/com/launchdarkly/client/StreamProcessor.java @@ -40,6 +40,7 @@ final class StreamProcessor implements UpdateProcessor { private final LDConfig config; private final String sdkKey; private final FeatureRequestor requestor; + private final DiagnosticAccumulator diagnosticAccumulator; private final EventSourceCreator eventSourceCreator; private volatile EventSource es; private final AtomicBoolean initialized = new AtomicBoolean(false); @@ -51,11 +52,12 @@ public static interface EventSourceCreator { } StreamProcessor(String sdkKey, LDConfig config, FeatureRequestor requestor, FeatureStore featureStore, - EventSourceCreator eventSourceCreator) { + EventSourceCreator eventSourceCreator, DiagnosticAccumulator diagnosticAccumulator) { this.store = featureStore; this.config = config; this.sdkKey = sdkKey; this.requestor = requestor; + this.diagnosticAccumulator = diagnosticAccumulator; this.eventSourceCreator = eventSourceCreator != null ? eventSourceCreator : new DefaultEventSourceCreator(); } diff --git a/src/main/java/com/launchdarkly/client/UpdateProcessorFactoryWithDiagnostics.java b/src/main/java/com/launchdarkly/client/UpdateProcessorFactoryWithDiagnostics.java new file mode 100644 index 000000000..a7a63bb31 --- /dev/null +++ b/src/main/java/com/launchdarkly/client/UpdateProcessorFactoryWithDiagnostics.java @@ -0,0 +1,6 @@ +package com.launchdarkly.client; + +interface UpdateProcessorFactoryWithDiagnostics extends UpdateProcessorFactory { + UpdateProcessor createUpdateProcessor(String sdkKey, LDConfig config, FeatureStore featureStore, + DiagnosticAccumulator diagnosticAccumulator); +} diff --git a/src/test/java/com/launchdarkly/client/DefaultEventProcessorTest.java b/src/test/java/com/launchdarkly/client/DefaultEventProcessorTest.java index 08c27eebd..2ec5c644e 100644 --- a/src/test/java/com/launchdarkly/client/DefaultEventProcessorTest.java +++ b/src/test/java/com/launchdarkly/client/DefaultEventProcessorTest.java @@ -13,6 +13,10 @@ import java.util.Date; import java.util.concurrent.TimeUnit; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; + import static com.launchdarkly.client.TestHttpUtil.httpsServerWithSelfSignedCert; import static com.launchdarkly.client.TestHttpUtil.makeStartedServer; import static com.launchdarkly.client.TestUtil.hasJsonProperty; @@ -24,14 +28,11 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.samePropertyValuesAs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; - @SuppressWarnings("javadoc") public class DefaultEventProcessorTest { private static final String SDK_KEY = "SDK_KEY"; @@ -51,7 +52,7 @@ public void identifyEventIsQueued() throws Exception { Event e = EventFactory.DEFAULT.newIdentifyEvent(user); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(e); } @@ -68,7 +69,7 @@ public void userIsFilteredInIdentifyEvent() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { LDConfig config = baseConfig(server).allAttributesPrivate(true).build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, config)) { + try (DefaultEventProcessor ep = createBasicProcessor(config)) { ep.sendEvent(e); } @@ -86,7 +87,7 @@ public void individualFeatureEventIsQueuedWithIndexEvent() throws Exception { simpleEvaluation(1, LDValue.of("value")), LDValue.ofNull()); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(fe); } @@ -108,7 +109,7 @@ public void userIsFilteredInIndexEvent() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { LDConfig config = baseConfig(server).allAttributesPrivate(true).build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, config)) { + try (DefaultEventProcessor ep = createBasicProcessor(config)) { ep.sendEvent(fe); } @@ -130,7 +131,7 @@ public void featureEventCanContainInlineUser() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { LDConfig config = baseConfig(server).inlineUsersInEvents(true).build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, config)) { + try (DefaultEventProcessor ep = createBasicProcessor(config)) { ep.sendEvent(fe); } @@ -151,7 +152,7 @@ public void userIsFilteredInFeatureEvent() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { LDConfig config = baseConfig(server).inlineUsersInEvents(true).allAttributesPrivate(true).build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, config)) { + try (DefaultEventProcessor ep = createBasicProcessor(config)) { ep.sendEvent(fe); } @@ -171,7 +172,7 @@ public void featureEventCanContainReason() throws Exception { EvaluationDetail.fromValue(LDValue.of("value"), 1, reason), LDValue.ofNull()); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(fe); } @@ -193,7 +194,7 @@ public void indexEventIsStillGeneratedIfInlineUsersIsTrueButFeatureEventIsNotTra try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { LDConfig config = baseConfig(server).inlineUsersInEvents(true).build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, config)) { + try (DefaultEventProcessor ep = createBasicProcessor(config)) { ep.sendEvent(fe); } @@ -213,7 +214,7 @@ public void eventKindIsDebugIfFlagIsTemporarilyInDebugMode() throws Exception { simpleEvaluation(1, LDValue.of("value")), LDValue.ofNull()); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(fe); } @@ -235,7 +236,7 @@ public void eventCanBeBothTrackedAndDebugged() throws Exception { simpleEvaluation(1, LDValue.of("value")), LDValue.ofNull()); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(fe); } @@ -262,7 +263,7 @@ public void debugModeExpiresBasedOnClientTimeIfClientTimeIsLaterThanServerTime() simpleEvaluation(1, LDValue.of("value")), LDValue.ofNull()); try (MockWebServer server = makeStartedServer(resp1, resp2)) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { // Send and flush an event we don't care about, just so we'll receive "resp1" which sets the last server time ep.sendEvent(EventFactory.DEFAULT.newIdentifyEvent(new LDUser.Builder("otherUser").build())); ep.flush(); @@ -295,7 +296,7 @@ public void debugModeExpiresBasedOnServerTimeIfServerTimeIsLaterThanClientTime() simpleEvaluation(1, LDValue.of("value")), LDValue.ofNull()); try (MockWebServer server = makeStartedServer(resp1, resp2)) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { // Send and flush an event we don't care about, just to set the last server time ep.sendEvent(EventFactory.DEFAULT.newIdentifyEvent(new LDUser.Builder("otherUser").build())); @@ -328,7 +329,7 @@ public void twoFeatureEventsForSameUserGenerateOnlyOneIndexEvent() throws Except simpleEvaluation(1, value), LDValue.ofNull()); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(fe1); ep.sendEvent(fe2); } @@ -351,7 +352,7 @@ public void identifyEventMakesIndexEventUnnecessary() throws Exception { simpleEvaluation(1, LDValue.of("value")), null); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(ie); ep.sendEvent(fe); } @@ -383,7 +384,7 @@ public void nonTrackedEventsAreSummarized() throws Exception { simpleEvaluation(2, value2), default2); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(fe1a); ep.sendEvent(fe1b); ep.sendEvent(fe1c); @@ -414,7 +415,7 @@ public void customEventIsQueuedWithUser() throws Exception { Event.Custom ce = EventFactory.DEFAULT.newCustomEvent("eventkey", user, data, metric); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(ce); } @@ -433,7 +434,7 @@ public void customEventCanContainInlineUser() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { LDConfig config = baseConfig(server).inlineUsersInEvents(true).build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, config)) { + try (DefaultEventProcessor ep = createBasicProcessor(config)) { ep.sendEvent(ce); } @@ -449,7 +450,7 @@ public void userIsFilteredInCustomEvent() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { LDConfig config = baseConfig(server).inlineUsersInEvents(true).allAttributesPrivate(true).build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, config)) { + try (DefaultEventProcessor ep = createBasicProcessor(config)) { ep.sendEvent(ce); } @@ -462,7 +463,7 @@ public void closingEventProcessorForcesSynchronousFlush() throws Exception { Event e = EventFactory.DEFAULT.newIdentifyEvent(user); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(e); } @@ -473,7 +474,7 @@ public void closingEventProcessorForcesSynchronousFlush() throws Exception { @Test public void nothingIsSentIfThereAreNoEvents() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build()); + DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build()); ep.close(); assertEquals(0, server.getRequestCount()); @@ -481,55 +482,63 @@ public void nothingIsSentIfThereAreNoEvents() throws Exception { } @Test - public void initialDiagnosticEventSentToDiagnosticEndpoint() throws Exception { + public void diagnosticEventsSentToDiagnosticEndpoint() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseDiagConfig(server).build()); - ep.close(); - - RecordedRequest req = server.takeRequest(100, TimeUnit.MILLISECONDS); - - assertNotNull(req); - assertThat(req.getPath(), equalTo("//diagnostic")); + DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(new DiagnosticId(SDK_KEY)); + try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseDiagConfig(server).build(), diagnosticAccumulator)) { + RecordedRequest initReq = server.takeRequest(); + ep.postDiagnostic(); + RecordedRequest periodicReq = server.takeRequest(); + + assertThat(initReq.getPath(), equalTo("//diagnostic")); + assertThat(periodicReq.getPath(), equalTo("//diagnostic")); + } } } @Test public void initialDiagnosticEventHasInitBody() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseDiagConfig(server).build()); - ep.close(); - - RecordedRequest req = server.takeRequest(100, TimeUnit.MILLISECONDS); - assertNotNull(req); + DiagnosticId diagnosticId = new DiagnosticId(SDK_KEY); + DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(diagnosticId); + try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseDiagConfig(server).build(), diagnosticAccumulator)) { + RecordedRequest req = server.takeRequest(); + + assertNotNull(req); - DiagnosticEvent.Init initEvent = gson.fromJson(req.getBody().readUtf8(), DiagnosticEvent.Init.class); + DiagnosticEvent.Init initEvent = gson.fromJson(req.getBody().readUtf8(), DiagnosticEvent.Init.class); - assertNotNull(initEvent); - assertThat(initEvent.kind, equalTo("diagnostic-init")); - assertNotNull(initEvent.configuration); - assertNotNull(initEvent.sdk); - assertNotNull(initEvent.platform); - assertNotNull(initEvent.id); + assertNotNull(initEvent); + assertThat(initEvent.kind, equalTo("diagnostic-init")); + assertThat(initEvent.id, samePropertyValuesAs(diagnosticId)); + assertNotNull(initEvent.configuration); + assertNotNull(initEvent.sdk); + assertNotNull(initEvent.platform); + } } } @Test public void periodicDiagnosticEventHasStatisticsBody() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse(), eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseDiagConfig(server).build())) { + DiagnosticId diagnosticId = new DiagnosticId(SDK_KEY); + DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(diagnosticId); + long dataSinceDate = diagnosticAccumulator.dataSinceDate; + try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseDiagConfig(server).build(), diagnosticAccumulator)) { + // Ignore the initial diagnostic event + server.takeRequest(); ep.postDiagnostic(); - } - - // Ignore the initial diagnostic event - server.takeRequest(100, TimeUnit.MILLISECONDS); - RecordedRequest periodReq = server.takeRequest(100, TimeUnit.MILLISECONDS); - assertNotNull(periodReq); + RecordedRequest periodicReq = server.takeRequest(); - DiagnosticEvent.Statistics statsEvent = gson.fromJson(periodReq.getBody().readUtf8(), DiagnosticEvent.Statistics.class); + assertNotNull(periodicReq); + DiagnosticEvent.Statistics statsEvent = gson.fromJson(periodicReq.getBody().readUtf8(), DiagnosticEvent.Statistics.class); - assertNotNull(statsEvent); - assertThat(statsEvent.kind, equalTo("diagnostic")); - assertNotNull(statsEvent.id); + assertNotNull(statsEvent); + assertThat(statsEvent.kind, equalTo("diagnostic")); + assertThat(statsEvent.id, samePropertyValuesAs(diagnosticId)); + assertThat(statsEvent.dataSinceDate, equalTo(dataSinceDate)); + assertThat(statsEvent.creationDate, equalTo(diagnosticAccumulator.dataSinceDate)); + } } } @@ -538,7 +547,7 @@ public void sdkKeyIsSent() throws Exception { Event e = EventFactory.DEFAULT.newIdentifyEvent(user); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(e); } @@ -550,12 +559,15 @@ public void sdkKeyIsSent() throws Exception { @Test public void sdkKeyIsSentOnDiagnosticEvents() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseDiagConfig(server).build()); - ep.close(); - - RecordedRequest req = server.takeRequest(100, TimeUnit.MILLISECONDS); - assertNotNull(req); - assertThat(req.getHeader("Authorization"), equalTo(SDK_KEY)); + DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(new DiagnosticId(SDK_KEY)); + try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseDiagConfig(server).build(), diagnosticAccumulator)) { + RecordedRequest initReq = server.takeRequest(); + ep.postDiagnostic(); + RecordedRequest periodicReq = server.takeRequest(); + + assertThat(initReq.getHeader("Authorization"), equalTo(SDK_KEY)); + assertThat(periodicReq.getHeader("Authorization"), equalTo(SDK_KEY)); + } } } @@ -564,7 +576,7 @@ public void eventSchemaIsSent() throws Exception { Event e = EventFactory.DEFAULT.newIdentifyEvent(user); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(e); } @@ -576,12 +588,15 @@ public void eventSchemaIsSent() throws Exception { @Test public void eventSchemaNotSetOnDiagnosticEvents() throws Exception { try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseDiagConfig(server).build()); - ep.close(); + DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(new DiagnosticId(SDK_KEY)); + try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseDiagConfig(server).build(), diagnosticAccumulator)) { + RecordedRequest initReq = server.takeRequest(); + ep.postDiagnostic(); + RecordedRequest periodicReq = server.takeRequest(); - RecordedRequest req = server.takeRequest(100, TimeUnit.MILLISECONDS); - assertNotNull(req); - assertNull(req.getHeader("X-LaunchDarkly-Event-Schema")); + assertNull(initReq.getHeader("X-LaunchDarkly-Event-Schema")); + assertNull(periodicReq.getHeader("X-LaunchDarkly-Event-Schema")); + } } } @@ -592,7 +607,7 @@ public void wrapperHeaderSentWhenSet() throws Exception { .wrapperName("Scala") .wrapperVersion("0.1.0") .build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, config)) { + try (DefaultEventProcessor ep = createBasicProcessor(config)) { Event e = EventFactory.DEFAULT.newIdentifyEvent(user); ep.sendEvent(e); } @@ -609,7 +624,7 @@ public void wrapperHeaderSentWithoutVersion() throws Exception { .wrapperName("Scala") .build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, config)) { + try (DefaultEventProcessor ep = createBasicProcessor(config)) { Event e = EventFactory.DEFAULT.newIdentifyEvent(user); ep.sendEvent(e); } @@ -662,7 +677,7 @@ public void httpClientDoesNotAllowSelfSignedCertByDefault() throws Exception { .eventsURI(serverWithCert.uri()) .build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor("sdk-key", config)) { + try (DefaultEventProcessor ep = new DefaultEventProcessor("sdk-key", config, null)) { Event e = EventFactory.DEFAULT.newIdentifyEvent(user); ep.sendEvent(e); @@ -683,7 +698,7 @@ public void httpClientCanUseCustomTlsConfig() throws Exception { .diagnosticOptOut(true) .build(); - try (DefaultEventProcessor ep = new DefaultEventProcessor("sdk-key", config)) { + try (DefaultEventProcessor ep = new DefaultEventProcessor("sdk-key", config, null)) { Event e = EventFactory.DEFAULT.newIdentifyEvent(user); ep.sendEvent(e); @@ -699,7 +714,7 @@ private void testUnrecoverableHttpError(int status) throws Exception { Event e = EventFactory.DEFAULT.newIdentifyEvent(user); try (MockWebServer server = makeStartedServer(eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(e); } @@ -717,7 +732,7 @@ private void testRecoverableHttpError(int status) throws Exception { // send two errors in a row, because the flush will be retried one time try (MockWebServer server = makeStartedServer(errorResponse, errorResponse, eventsSuccessResponse())) { - try (DefaultEventProcessor ep = new DefaultEventProcessor(SDK_KEY, baseConfig(server).build())) { + try (DefaultEventProcessor ep = createBasicProcessor(baseConfig(server).build())) { ep.sendEvent(e); } @@ -730,6 +745,10 @@ private void testRecoverableHttpError(int status) throws Exception { } } + private DefaultEventProcessor createBasicProcessor(LDConfig config) { + return new DefaultEventProcessor(SDK_KEY, config, null); + } + private LDConfig.Builder baseConfig(MockWebServer server) { return new LDConfig.Builder().eventsURI(server.url("/").uri()).diagnosticOptOut(true); } diff --git a/src/test/java/com/launchdarkly/client/DiagnosticAccumulatorTest.java b/src/test/java/com/launchdarkly/client/DiagnosticAccumulatorTest.java index 2e261f920..83468a1af 100644 --- a/src/test/java/com/launchdarkly/client/DiagnosticAccumulatorTest.java +++ b/src/test/java/com/launchdarkly/client/DiagnosticAccumulatorTest.java @@ -8,45 +8,25 @@ public class DiagnosticAccumulatorTest { - @Test - public void startSetsDiagnosticId() { - DiagnosticId diagnosticId = new DiagnosticId("SDK_KEY"); - long currentTime = System.currentTimeMillis(); - DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(); - diagnosticAccumulator.start(diagnosticId, currentTime); - assertSame(diagnosticId, diagnosticAccumulator.diagnosticId); - } - - @Test - public void startSetsDataSinceDate() { - DiagnosticId diagnosticId = new DiagnosticId("SDK_KEY"); - long currentTime = System.currentTimeMillis(); - DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(); - diagnosticAccumulator.start(diagnosticId, currentTime); - assertEquals(currentTime, diagnosticAccumulator.dataSinceDate); - } - @Test public void createsDiagnosticStatisticsEvent() { DiagnosticId diagnosticId = new DiagnosticId("SDK_KEY"); - long currentTime = System.currentTimeMillis(); - DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(); - diagnosticAccumulator.start(diagnosticId, currentTime); + DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(diagnosticId); + long startDate = diagnosticAccumulator.dataSinceDate; DiagnosticEvent.Statistics diagnosticStatisticsEvent = diagnosticAccumulator.createEventAndReset(10, 15, 20); assertSame(diagnosticId, diagnosticStatisticsEvent.id); assertEquals(10, diagnosticStatisticsEvent.droppedEvents); assertEquals(15, diagnosticStatisticsEvent.deduplicatedUsers); assertEquals(20, diagnosticStatisticsEvent.eventsInQueue); - assertEquals(currentTime, diagnosticStatisticsEvent.dataSinceDate); + assertEquals(startDate, diagnosticStatisticsEvent.dataSinceDate); } @Test public void resetsDataSinceDate() throws InterruptedException { - long currentTime = System.currentTimeMillis(); - DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(); - diagnosticAccumulator.start(null, currentTime); + DiagnosticAccumulator diagnosticAccumulator = new DiagnosticAccumulator(new DiagnosticId("SDK_KEY")); + long startDate = diagnosticAccumulator.dataSinceDate; Thread.sleep(2); diagnosticAccumulator.createEventAndReset(0, 0, 0); - assertNotEquals(currentTime, diagnosticAccumulator.dataSinceDate); + assertNotEquals(startDate, diagnosticAccumulator.dataSinceDate); } } diff --git a/src/test/java/com/launchdarkly/client/DiagnosticEventTest.java b/src/test/java/com/launchdarkly/client/DiagnosticEventTest.java index 70eb2e2cc..eabb778d5 100644 --- a/src/test/java/com/launchdarkly/client/DiagnosticEventTest.java +++ b/src/test/java/com/launchdarkly/client/DiagnosticEventTest.java @@ -46,7 +46,6 @@ public void testDefaultDiagnosticConfiguration() { expected.addProperty("customEventsURI", false); expected.addProperty("customStreamURI", false); expected.addProperty("diagnosticRecordingIntervalMillis", 900_000); - expected.addProperty("eventReportingDisabled", false); expected.addProperty("eventsCapacity", 10_000); expected.addProperty("eventsFlushIntervalMillis",5_000); expected.addProperty("featureStore", "InMemoryFeatureStoreFactory"); @@ -106,7 +105,6 @@ public void testCustomDiagnosticConfiguration() { expected.addProperty("customEventsURI", true); expected.addProperty("customStreamURI", true); expected.addProperty("diagnosticRecordingIntervalMillis", 1_800_000); - expected.addProperty("eventReportingDisabled", true); expected.addProperty("eventsCapacity", 20_000); expected.addProperty("eventsFlushIntervalMillis",10_000); expected.addProperty("featureStore", "RedisFeatureStoreBuilder"); diff --git a/src/test/java/com/launchdarkly/client/LDClientTest.java b/src/test/java/com/launchdarkly/client/LDClientTest.java index b585536c9..be53b301a 100644 --- a/src/test/java/com/launchdarkly/client/LDClientTest.java +++ b/src/test/java/com/launchdarkly/client/LDClientTest.java @@ -7,6 +7,8 @@ import com.google.common.collect.Iterables; import com.launchdarkly.client.value.LDValue; +import junit.framework.AssertionFailedError; + import org.easymock.Capture; import org.easymock.EasyMock; import org.easymock.EasyMockSupport; @@ -21,6 +23,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static com.launchdarkly.client.TestUtil.failedUpdateProcessor; import static com.launchdarkly.client.TestUtil.flagWithValue; import static com.launchdarkly.client.TestUtil.initedFeatureStore; import static com.launchdarkly.client.TestUtil.specificFeatureStore; @@ -28,21 +31,26 @@ import static com.launchdarkly.client.VersionedDataKind.FEATURES; import static com.launchdarkly.client.VersionedDataKind.SEGMENTS; import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.isA; +import static org.easymock.EasyMock.isNull; import static org.easymock.EasyMock.replay; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import junit.framework.AssertionFailedError; - /** * See also LDClientEvaluationTest, etc. This file contains mostly tests for the startup logic. */ @SuppressWarnings("javadoc") public class LDClientTest extends EasyMockSupport { + private final static String SDK_KEY = "SDK_KEY"; + private UpdateProcessor updateProcessor; private EventProcessor eventProcessor; private Future initFuture; @@ -76,7 +84,7 @@ public void constructorWithConfigThrowsExceptionForNullSdkKey() throws Exception @Test public void constructorThrowsExceptionForNullConfig() throws Exception { - try (LDClient client = new LDClient("SDK_KEY", null)) { + try (LDClient client = new LDClient(SDK_KEY, null)) { fail("expected exception"); } catch (NullPointerException e) { assertEquals("config must not be null", e.getMessage()); @@ -91,7 +99,7 @@ public void clientHasDefaultEventProcessorIfSendEventsIsTrue() throws Exception .startWaitMillis(0) .sendEvents(true) .build(); - try (LDClient client = new LDClient("SDK_KEY", config)) { + try (LDClient client = new LDClient(SDK_KEY, config)) { assertEquals(DefaultEventProcessor.class, client.eventProcessor.getClass()); } } @@ -104,7 +112,7 @@ public void clientHasNullEventProcessorIfSendEventsIsFalse() throws IOException .startWaitMillis(0) .sendEvents(false) .build(); - try (LDClient client = new LDClient("SDK_KEY", config)) { + try (LDClient client = new LDClient(SDK_KEY, config)) { assertEquals(EventProcessor.NullEventProcessor.class, client.eventProcessor.getClass()); } } @@ -116,7 +124,7 @@ public void streamingClientHasStreamProcessor() throws Exception { .streamURI(URI.create("http://fake")) .startWaitMillis(0) .build(); - try (LDClient client = new LDClient("SDK_KEY", config)) { + try (LDClient client = new LDClient(SDK_KEY, config)) { assertEquals(StreamProcessor.class, client.updateProcessor.getClass()); } } @@ -128,11 +136,85 @@ public void pollingClientHasPollingProcessor() throws IOException { .baseURI(URI.create("http://fake")) .startWaitMillis(0) .build(); - try (LDClient client = new LDClient("SDK_KEY", config)) { + try (LDClient client = new LDClient(SDK_KEY, config)) { assertEquals(PollingProcessor.class, client.updateProcessor.getClass()); } } + @Test + public void sameDiagnosticAccumulatorPassedToFactoriesWhenSupported() throws IOException { + EventProcessorFactoryWithDiagnostics mockEventProcessorFactory = createStrictMock(EventProcessorFactoryWithDiagnostics.class); + UpdateProcessorFactoryWithDiagnostics mockUpdateProcessorFactory = createStrictMock(UpdateProcessorFactoryWithDiagnostics.class); + + LDConfig config = new LDConfig.Builder() + .stream(false) + .baseURI(URI.create("http://fake")) + .startWaitMillis(0) + .eventProcessorFactory(mockEventProcessorFactory) + .updateProcessorFactory(mockUpdateProcessorFactory) + .build(); + + Capture capturedEventAccumulator = Capture.newInstance(); + Capture capturedUpdateAccumulator = Capture.newInstance(); + expect(mockEventProcessorFactory.createEventProcessor(eq(SDK_KEY), isA(LDConfig.class), capture(capturedEventAccumulator))).andReturn(niceMock(EventProcessor.class)); + expect(mockUpdateProcessorFactory.createUpdateProcessor(eq(SDK_KEY), isA(LDConfig.class), isA(FeatureStore.class), capture(capturedUpdateAccumulator))).andReturn(failedUpdateProcessor()); + + replayAll(); + + try (LDClient client = new LDClient(SDK_KEY, config)) { + verifyAll(); + assertNotNull(capturedEventAccumulator.getValue()); + assertEquals(capturedEventAccumulator.getValue(), capturedUpdateAccumulator.getValue()); + } + } + + @Test + public void nullDiagnosticAccumulatorPassedToFactoriesWhenOptedOut() throws IOException { + EventProcessorFactoryWithDiagnostics mockEventProcessorFactory = createStrictMock(EventProcessorFactoryWithDiagnostics.class); + UpdateProcessorFactoryWithDiagnostics mockUpdateProcessorFactory = createStrictMock(UpdateProcessorFactoryWithDiagnostics.class); + + LDConfig config = new LDConfig.Builder() + .stream(false) + .baseURI(URI.create("http://fake")) + .startWaitMillis(0) + .eventProcessorFactory(mockEventProcessorFactory) + .updateProcessorFactory(mockUpdateProcessorFactory) + .diagnosticOptOut(true) + .build(); + + expect(mockEventProcessorFactory.createEventProcessor(eq(SDK_KEY), isA(LDConfig.class), isNull(DiagnosticAccumulator.class))).andReturn(niceMock(EventProcessor.class)); + expect(mockUpdateProcessorFactory.createUpdateProcessor(eq(SDK_KEY), isA(LDConfig.class), isA(FeatureStore.class), isNull(DiagnosticAccumulator.class))).andReturn(failedUpdateProcessor()); + + replayAll(); + + try (LDClient client = new LDClient(SDK_KEY, config)) { + verifyAll(); + } + } + + @Test + public void nullDiagnosticAccumulatorPassedToUpdateFactoryWhenEventProcessorDoesNotSupportDiagnostics() throws IOException { + EventProcessorFactory mockEventProcessorFactory = createStrictMock(EventProcessorFactory.class); + UpdateProcessorFactoryWithDiagnostics mockUpdateProcessorFactory = createStrictMock(UpdateProcessorFactoryWithDiagnostics.class); + + LDConfig config = new LDConfig.Builder() + .stream(false) + .baseURI(URI.create("http://fake")) + .startWaitMillis(0) + .eventProcessorFactory(mockEventProcessorFactory) + .updateProcessorFactory(mockUpdateProcessorFactory) + .build(); + + expect(mockEventProcessorFactory.createEventProcessor(eq(SDK_KEY), isA(LDConfig.class))).andReturn(niceMock(EventProcessor.class)); + expect(mockUpdateProcessorFactory.createUpdateProcessor(eq(SDK_KEY), isA(LDConfig.class), isA(FeatureStore.class), isNull(DiagnosticAccumulator.class))).andReturn(failedUpdateProcessor()); + + replayAll(); + + try (LDClient client = new LDClient(SDK_KEY, config)) { + verifyAll(); + } + } + @Test public void noWaitForUpdateProcessorIfWaitMillisIsZero() throws Exception { LDConfig.Builder config = new LDConfig.Builder() @@ -297,7 +379,7 @@ public void dataSetIsPassedToFeatureStoreInCorrectOrder() throws Exception { .updateProcessorFactory(updateProcessorWithData(DEPENDENCY_ORDERING_TEST_DATA)) .featureStoreFactory(specificFeatureStore(store)) .sendEvents(false); - client = new LDClient("SDK_KEY", config.build()); + client = new LDClient(SDK_KEY, config.build()); Map, Map> dataMap = captureData.getValue(); assertEquals(2, dataMap.size()); @@ -342,7 +424,7 @@ private void expectEventsSent(int count) { private LDClientInterface createMockClient(LDConfig.Builder config) { config.updateProcessorFactory(TestUtil.specificUpdateProcessor(updateProcessor)); config.eventProcessorFactory(TestUtil.specificEventProcessor(eventProcessor)); - return new LDClient("SDK_KEY", config.build()); + return new LDClient(SDK_KEY, config.build()); } private static Map, Map> DEPENDENCY_ORDERING_TEST_DATA = diff --git a/src/test/java/com/launchdarkly/client/StreamProcessorTest.java b/src/test/java/com/launchdarkly/client/StreamProcessorTest.java index 586c80bba..42bcd535c 100644 --- a/src/test/java/com/launchdarkly/client/StreamProcessorTest.java +++ b/src/test/java/com/launchdarkly/client/StreamProcessorTest.java @@ -351,7 +351,7 @@ public void httpClientDoesNotAllowSelfSignedCertByDefault() throws Exception { .build(); try (StreamProcessor sp = new StreamProcessor("sdk-key", config, - mockRequestor, featureStore, null)) { + mockRequestor, featureStore, null, null)) { sp.connectionErrorHandler = errorSink; Future ready = sp.start(); ready.get(); @@ -375,7 +375,7 @@ public void httpClientCanUseCustomTlsConfig() throws Exception { .build(); try (StreamProcessor sp = new StreamProcessor("sdk-key", config, - mockRequestor, featureStore, null)) { + mockRequestor, featureStore, null, null)) { sp.connectionErrorHandler = errorSink; Future ready = sp.start(); ready.get(); @@ -398,7 +398,7 @@ public void httpClientCanUseProxyConfig() throws Exception { .build(); try (StreamProcessor sp = new StreamProcessor("sdk-key", config, - mockRequestor, featureStore, null)) { + mockRequestor, featureStore, null, null)) { sp.connectionErrorHandler = errorSink; Future ready = sp.start(); ready.get(); @@ -457,7 +457,7 @@ private void testRecoverableHttpError(int status) throws Exception { } private StreamProcessor createStreamProcessor(String sdkKey, LDConfig config) { - return new StreamProcessor(sdkKey, config, mockRequestor, featureStore, new StubEventSourceCreator()); + return new StreamProcessor(sdkKey, config, mockRequestor, featureStore, new StubEventSourceCreator(), null); } private String featureJson(String key, int version) {