From 545286d2ede941ad9fc14e4b54dad3ff2b88e867 Mon Sep 17 00:00:00 2001 From: Roman Onufryk Date: Wed, 1 Mar 2017 10:21:42 -0800 Subject: [PATCH 1/6] Throw ConfigParseException consistently in all JSON deserializers on parsing empty or null datafile (#77) --- .../java/com/optimizely/ab/Optimizely.java | 7 +++++ .../ab/config/parser/GsonConfigParser.java | 6 +++++ .../optimizely/ab/OptimizelyBuilderTest.java | 13 ++++++++++ .../parser/DefaultConfigParserTest.java | 2 +- .../config/parser/GsonConfigParserTest.java | 26 ++++++++++++++++++- .../parser/JacksonConfigParserTest.java | 26 ++++++++++++++++++- .../config/parser/JsonConfigParserTest.java | 26 ++++++++++++++++++- .../parser/JsonSimpleConfigParserTest.java | 24 ++++++++++++++++- 8 files changed, 125 insertions(+), 5 deletions(-) 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 334564508..989cb3fe5 100644 --- a/core-api/src/main/java/com/optimizely/ab/Optimizely.java +++ b/core-api/src/main/java/com/optimizely/ab/Optimizely.java @@ -498,6 +498,13 @@ private void track(@Nonnull String eventName, * @return a {@link ProjectConfig} instance given a json string */ private static ProjectConfig getProjectConfig(String datafile) throws ConfigParseException { + if (datafile == null) { + throw new ConfigParseException("Unable to parse null datafile."); + } + if (datafile.length() == 0) { + throw new ConfigParseException("Unable to parse empty datafile."); + } + return DefaultConfigParser.getInstance().parseProjectConfig(datafile); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java index 6a7f42a05..9bfa58981 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java @@ -33,6 +33,12 @@ final class GsonConfigParser implements ConfigParser { @Override public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParseException { + if (json == null) { + throw new ConfigParseException("Unable to parse null json."); + } + if (json.length() == 0) { + throw new ConfigParseException("Unable to parse empty json."); + } Gson gson = new GsonBuilder() .registerTypeAdapter(ProjectConfig.class, new ProjectConfigGsonDeserializer()) .registerTypeAdapter(Audience.class, new AudienceGsonDeserializer()) diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java index 69ed103e6..13557bc76 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java @@ -164,6 +164,19 @@ public void withCustomClientVersion() throws Exception { assertThat(((EventBuilderV2)optimizelyClient.eventBuilder).clientVersion, is("0.0.0")); } + @SuppressFBWarnings(value="NP_NONNULL_PARAM_VIOLATION", justification="Testing nullness contract violation") + @Test + public void builderThrowsConfigParseExceptionForNullDatafile() throws Exception { + thrown.expect(ConfigParseException.class); + Optimizely.builder(null, mockEventHandler).build(); + } + + @Test + public void builderThrowsConfigParseExceptionForEmptyDatafile() throws Exception { + thrown.expect(ConfigParseException.class); + Optimizely.builder("", mockEventHandler).build(); + } + @Test public void builderThrowsConfigParseExceptionForInvalidDatafile() throws Exception { thrown.expect(ConfigParseException.class); diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java index 0fa1db64e..f2e1a2df1 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java @@ -27,4 +27,4 @@ public class DefaultConfigParserTest { public void createThrowException() throws Exception { // FIXME - mdodsworth: hmmm, this isn't going to be the easiest thing to test } -} \ No newline at end of file +} diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java index feff38be2..f66c3e401 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java @@ -18,6 +18,7 @@ import com.optimizely.ab.config.ProjectConfig; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -86,4 +87,27 @@ public void validJsonRequiredFieldMissingExceptionWrapping() throws Exception { GsonConfigParser parser = new GsonConfigParser(); parser.parseProjectConfig("{\"valid\": \"json\"}"); } -} \ No newline at end of file + + /** + * Verify that empty string JSON results in a {@link ConfigParseException} being thrown. + */ + @Test + public void emptyJsonExceptionWrapping() throws Exception { + thrown.expect(ConfigParseException.class); + + GsonConfigParser parser = new GsonConfigParser(); + parser.parseProjectConfig(""); + } + + /** + * Verify that null JSON results in a {@link ConfigParseException} being thrown. + */ + @Test + @SuppressFBWarnings(value="NP_NONNULL_PARAM_VIOLATION", justification="Testing nullness contract violation") + public void nullJsonExceptionWrapping() throws Exception { + thrown.expect(ConfigParseException.class); + + GsonConfigParser parser = new GsonConfigParser(); + parser.parseProjectConfig(null); + } +} diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java index 18e558082..3adac6c70 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java @@ -18,6 +18,7 @@ import com.optimizely.ab.config.ProjectConfig; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -86,4 +87,27 @@ public void validJsonRequiredFieldMissingExceptionWrapping() throws Exception { JacksonConfigParser parser = new JacksonConfigParser(); parser.parseProjectConfig("{\"valid\": \"json\"}"); } -} \ No newline at end of file + + /** + * Verify that empty string JSON results in a {@link ConfigParseException} being thrown. + */ + @Test + public void emptyJsonExceptionWrapping() throws Exception { + thrown.expect(ConfigParseException.class); + + JacksonConfigParser parser = new JacksonConfigParser(); + parser.parseProjectConfig(""); + } + + /** + * Verify that null JSON results in a {@link ConfigParseException} being thrown. + */ + @Test + @SuppressFBWarnings(value="NP_NONNULL_PARAM_VIOLATION", justification="Testing nullness contract violation") + public void nullJsonExceptionWrapping() throws Exception { + thrown.expect(ConfigParseException.class); + + JacksonConfigParser parser = new JacksonConfigParser(); + parser.parseProjectConfig(null); + } +} diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java index 0e49c8204..064871471 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java @@ -18,6 +18,7 @@ import com.optimizely.ab.config.ProjectConfig; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -86,4 +87,27 @@ public void validJsonRequiredFieldMissingExceptionWrapping() throws Exception { JsonConfigParser parser = new JsonConfigParser(); parser.parseProjectConfig("{\"valid\": \"json\"}"); } -} \ No newline at end of file + + /** + * Verify that empty string JSON results in a {@link ConfigParseException} being thrown. + */ + @Test + public void emptyJsonExceptionWrapping() throws Exception { + thrown.expect(ConfigParseException.class); + + JsonConfigParser parser = new JsonConfigParser(); + parser.parseProjectConfig(""); + } + + /** + * Verify that null JSON results in a {@link ConfigParseException} being thrown. + */ + @Test + @SuppressFBWarnings(value="NP_NONNULL_PARAM_VIOLATION", justification="Testing nullness contract violation") + public void nullJsonExceptionWrapping() throws Exception { + thrown.expect(ConfigParseException.class); + + JsonConfigParser parser = new JsonConfigParser(); + parser.parseProjectConfig(null); + } +} diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java index 57a07f78d..48f679e7a 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java @@ -18,6 +18,7 @@ import com.optimizely.ab.config.ProjectConfig; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -86,4 +87,25 @@ public void validJsonRequiredFieldMissingExceptionWrapping() throws Exception { JsonSimpleConfigParser parser = new JsonSimpleConfigParser(); parser.parseProjectConfig("{\"valid\": \"json\"}"); } -} \ No newline at end of file + /** + * Verify that empty string JSON results in a {@link ConfigParseException} being thrown. + */ + @Test + public void emptyJsonExceptionWrapping() throws Exception { + thrown.expect(ConfigParseException.class); + + JsonSimpleConfigParser parser = new JsonSimpleConfigParser(); + parser.parseProjectConfig(""); + } + /** + * Verify that null JSON results in a {@link ConfigParseException} being thrown. + */ + @Test + @SuppressFBWarnings(value="NP_NONNULL_PARAM_VIOLATION", justification="Testing nullness contract violation") + public void nullJsonExceptionWrapping() throws Exception { + thrown.expect(ConfigParseException.class); + + JsonSimpleConfigParser parser = new JsonSimpleConfigParser(); + parser.parseProjectConfig(null); + } +} From 7a5fd993b636bc1f766b8fa24075e5cccedb2b1b Mon Sep 17 00:00:00 2001 From: Elliot Kim Date: Thu, 2 Mar 2017 23:36:03 -0800 Subject: [PATCH 2/6] `filterAttributes` returns an empty map if `attributes` parameter is null (#79) --- .../java/com/optimizely/ab/Optimizely.java | 12 +- .../com/optimizely/ab/OptimizelyTestV2.java | 216 ++++++++++++++++++ .../com/optimizely/ab/OptimizelyTestV3.java | 216 ++++++++++++++++++ 3 files changed, 441 insertions(+), 3 deletions(-) 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 989cb3fe5..274ec2525 100644 --- a/core-api/src/main/java/com/optimizely/ab/Optimizely.java +++ b/core-api/src/main/java/com/optimizely/ab/Optimizely.java @@ -631,10 +631,16 @@ private LiveVariable getLiveVariableOrThrow(ProjectConfig projectConfig, String * * @param projectConfig the current project config * @param attributes the attributes map to validate and potentially filter - * @return the filtered attributes map (containing only attributes that are present in the project config) - * + * @return the filtered attributes map (containing only attributes that are present in the project config) or an + * empty map if a null attributes object is passed in */ - private Map filterAttributes(ProjectConfig projectConfig, Map attributes) { + private Map filterAttributes(@Nonnull ProjectConfig projectConfig, + @Nonnull Map attributes) { + if (attributes == null) { + logger.warn("Attributes is null when non-null was expected. Defaulting to an empty attributes map."); + return Collections.emptyMap(); + } + List unknownAttributes = null; Map attributeKeyMapping = projectConfig.getAttributeKeyMapping(); diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java index 9d3c8d9ba..20d1255fa 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java @@ -433,6 +433,117 @@ public void activateWithUnknownAttribute() throws Exception { verify(mockEventHandler).dispatchEvent(logEventToDispatch); } + /** + * Verify that {@link Optimizely#activate(String, String, Map)} ignores null attributes. + */ + @Test + @SuppressFBWarnings( + value="NP_NONNULL_PARAM_VIOLATION", + justification="testing nullness contract violation") + public void activateWithNullAttributes() throws Exception { + String datafile = noAudienceProjectConfigJsonV2(); + ProjectConfig projectConfig = noAudienceProjectConfigV2(); + Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Variation bucketedVariation = activatedExperiment.getVariations().get(0); + + // setup a mock event builder to return expected impression params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), + eq("userId"), eq(Collections.emptyMap()), + isNull(String.class))) + .thenReturn(logEventToDispatch); + + when(mockBucketer.bucket(activatedExperiment, "userId")) + .thenReturn(bucketedVariation); + + // activate the experiment + Map attributes = null; + Variation actualVariation = optimizely.activate(activatedExperiment.getKey(), "userId", attributes); + + logbackVerifier.expectMessage(Level.WARN, "Attributes is null when non-null was expected. Defaulting to an empty attributes map."); + + // verify that the bucketing algorithm was called correctly + verify(mockBucketer).bucket(activatedExperiment, "userId"); + assertThat(actualVariation, is(bucketedVariation)); + + // setup the attribute map captor (so we can verify its content) + ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); + verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), + eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), + isNull(String.class)); + + Map actualValue = attributeCaptor.getValue(); + assertThat(actualValue, is(Collections.emptyMap())); + + // verify that dispatchEvent was called with the correct LogEvent object + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + + /** + * Verify that {@link Optimizely#activate(String, String, Map)} gracefully handles null attribute values. + */ + @Test + public void activateWithNullAttributeValues() throws Exception { + String datafile = validConfigJsonV2(); + ProjectConfig projectConfig = validProjectConfigV2(); + Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Variation bucketedVariation = activatedExperiment.getVariations().get(0); + Attribute attribute = projectConfig.getAttributes().get(0); + + // setup a mock event builder to return expected impression params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), + eq("userId"), anyMapOf(String.class, String.class), + isNull(String.class))) + .thenReturn(logEventToDispatch); + + when(mockBucketer.bucket(activatedExperiment, "userId")) + .thenReturn(bucketedVariation); + + // activate the experiment + Map attributes = new HashMap(); + attributes.put(attribute.getKey(), null); + Variation actualVariation = optimizely.activate(activatedExperiment.getKey(), "userId", attributes); + + // verify that the bucketing algorithm was called correctly + verify(mockBucketer).bucket(activatedExperiment, "userId"); + assertThat(actualVariation, is(bucketedVariation)); + + // setup the attribute map captor (so we can verify its content) + ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); + verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), + eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), + isNull(String.class)); + + Map actualValue = attributeCaptor.getValue(); + assertThat(actualValue, hasEntry(attribute.getKey(), null)); + + // verify that dispatchEvent was called with the correct LogEvent object + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + /** * Verify that {@link Optimizely#activate(String, String)} returns null when the experiment id corresponds to a * non-running experiment. @@ -883,6 +994,111 @@ public void trackEventWithAttributes() throws Exception { verify(mockEventHandler).dispatchEvent(logEventToDispatch); } + /** + * Verify that {@link Optimizely#track(String, String)} ignores null attributes. + */ + @Test + @SuppressFBWarnings( + value="NP_NONNULL_PARAM_VIOLATION", + justification="testing nullness contract violation") + public void trackEventWithNullAttributes() throws Exception { + String datafile = noAudienceProjectConfigJsonV2(); + ProjectConfig projectConfig = noAudienceProjectConfigV2(); + EventType eventType = projectConfig.getEventTypes().get(0); + + // setup a mock event builder to return expected conversion params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + eq(Collections.emptyMap()), isNull(Long.class), + isNull(String.class))) + .thenReturn(logEventToDispatch); + + logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); + logbackVerifier.expectMessage(Level.DEBUG, "Dispatching conversion event to URL test_url with params " + + testParams + " and payload \"\""); + + // call track + Map attributes = null; + optimizely.track(eventType.getKey(), "userId", attributes); + + logbackVerifier.expectMessage(Level.WARN, "Attributes is null when non-null was expected. Defaulting to an empty attributes map."); + + // setup the attribute map captor (so we can verify its content) + ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); + + // verify that the event builder was called with the expected attributes + verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + attributeCaptor.capture(), isNull(Long.class), + isNull(String.class)); + + Map actualValue = attributeCaptor.getValue(); + assertThat(actualValue, is(Collections.emptyMap())); + + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + + /** + * Verify that {@link Optimizely#track(String, String)} gracefully handles null attribute values. + */ + @Test + public void trackEventWithNullAttributeValues() throws Exception { + String datafile = validConfigJsonV2(); + ProjectConfig projectConfig = validProjectConfigV2(); + EventType eventType = projectConfig.getEventTypes().get(0); + + // setup a mock event builder to return expected conversion params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + anyMapOf(String.class, String.class), isNull(Long.class), + isNull(String.class))) + .thenReturn(logEventToDispatch); + + logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); + logbackVerifier.expectMessage(Level.DEBUG, "Dispatching conversion event to URL test_url with params " + + testParams + " and payload \"\""); + + // call track + Map attributes = new HashMap(); + attributes.put("test", null); + optimizely.track(eventType.getKey(), "userId", attributes); + + // setup the attribute map captor (so we can verify its content) + ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); + + // verify that the event builder was called with the expected attributes + verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + attributeCaptor.capture(), isNull(Long.class), + isNull(String.class)); + + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + /** * Verify that {@link Optimizely#track(String, String)} handles the case where an unknown attribute * (i.e., not in the config) is passed through. diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java index de8212376..50c7272b8 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java @@ -437,6 +437,117 @@ public void activateWithUnknownAttribute() throws Exception { verify(mockEventHandler).dispatchEvent(logEventToDispatch); } + /** + * Verify that {@link Optimizely#activate(String, String, Map)} ignores null attributes. + */ + @Test + @SuppressFBWarnings( + value="NP_NONNULL_PARAM_VIOLATION", + justification="testing nullness contract violation") + public void activateWithNullAttributes() throws Exception { + String datafile = noAudienceProjectConfigJsonV3(); + ProjectConfig projectConfig = noAudienceProjectConfigV3(); + Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Variation bucketedVariation = activatedExperiment.getVariations().get(0); + + // setup a mock event builder to return expected impression params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), + eq("userId"), eq(Collections.emptyMap()), + isNull(String.class))) + .thenReturn(logEventToDispatch); + + when(mockBucketer.bucket(activatedExperiment, "userId")) + .thenReturn(bucketedVariation); + + // activate the experiment + Map attributes = null; + Variation actualVariation = optimizely.activate(activatedExperiment.getKey(), "userId", attributes); + + logbackVerifier.expectMessage(Level.WARN, "Attributes is null when non-null was expected. Defaulting to an empty attributes map."); + + // verify that the bucketing algorithm was called correctly + verify(mockBucketer).bucket(activatedExperiment, "userId"); + assertThat(actualVariation, is(bucketedVariation)); + + // setup the attribute map captor (so we can verify its content) + ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); + verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), + eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), + isNull(String.class)); + + Map actualValue = attributeCaptor.getValue(); + assertThat(actualValue, is(Collections.emptyMap())); + + // verify that dispatchEvent was called with the correct LogEvent object + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + + /** + * Verify that {@link Optimizely#activate(String, String, Map)} gracefully handles null attribute values. + */ + @Test + public void activateWithNullAttributeValues() throws Exception { + String datafile = validConfigJsonV3(); + ProjectConfig projectConfig = validProjectConfigV3(); + Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Variation bucketedVariation = activatedExperiment.getVariations().get(0); + Attribute attribute = projectConfig.getAttributes().get(0); + + // setup a mock event builder to return expected impression params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), + eq("userId"), anyMapOf(String.class, String.class), + isNull(String.class))) + .thenReturn(logEventToDispatch); + + when(mockBucketer.bucket(activatedExperiment, "userId")) + .thenReturn(bucketedVariation); + + // activate the experiment + Map attributes = new HashMap(); + attributes.put(attribute.getKey(), null); + Variation actualVariation = optimizely.activate(activatedExperiment.getKey(), "userId", attributes); + + // verify that the bucketing algorithm was called correctly + verify(mockBucketer).bucket(activatedExperiment, "userId"); + assertThat(actualVariation, is(bucketedVariation)); + + // setup the attribute map captor (so we can verify its content) + ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); + verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), + eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), + isNull(String.class)); + + Map actualValue = attributeCaptor.getValue(); + assertThat(actualValue, hasEntry(attribute.getKey(), null)); + + // verify that dispatchEvent was called with the correct LogEvent object + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + /** * Verify that {@link Optimizely#activate(String, String)} returns null when the experiment id corresponds to a * non-running experiment. @@ -821,6 +932,111 @@ public void trackEventWithAttributes() throws Exception { verify(mockEventHandler).dispatchEvent(logEventToDispatch); } + /** + * Verify that {@link Optimizely#track(String, String)} ignores null attributes. + */ + @Test + @SuppressFBWarnings( + value="NP_NONNULL_PARAM_VIOLATION", + justification="testing nullness contract violation") + public void trackEventWithNullAttributes() throws Exception { + String datafile = noAudienceProjectConfigJsonV3(); + ProjectConfig projectConfig = noAudienceProjectConfigV3(); + EventType eventType = projectConfig.getEventTypes().get(0); + + // setup a mock event builder to return expected conversion params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + eq(Collections.emptyMap()), isNull(Long.class), + isNull(String.class))) + .thenReturn(logEventToDispatch); + + logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); + logbackVerifier.expectMessage(Level.DEBUG, "Dispatching conversion event to URL test_url with params " + + testParams + " and payload \"\""); + + // call track + Map attributes = null; + optimizely.track(eventType.getKey(), "userId", attributes); + + logbackVerifier.expectMessage(Level.WARN, "Attributes is null when non-null was expected. Defaulting to an empty attributes map."); + + // setup the attribute map captor (so we can verify its content) + ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); + + // verify that the event builder was called with the expected attributes + verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + attributeCaptor.capture(), isNull(Long.class), + isNull(String.class)); + + Map actualValue = attributeCaptor.getValue(); + assertThat(actualValue, is(Collections.emptyMap())); + + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + + /** + * Verify that {@link Optimizely#track(String, String)} gracefully handles null attribute values. + */ + @Test + public void trackEventWithNullAttributeValues() throws Exception { + String datafile = validConfigJsonV3(); + ProjectConfig projectConfig = validProjectConfigV3(); + EventType eventType = projectConfig.getEventTypes().get(0); + + // setup a mock event builder to return expected conversion params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + anyMapOf(String.class, String.class), isNull(Long.class), + isNull(String.class))) + .thenReturn(logEventToDispatch); + + logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); + logbackVerifier.expectMessage(Level.DEBUG, "Dispatching conversion event to URL test_url with params " + + testParams + " and payload \"\""); + + // call track + Map attributes = new HashMap(); + attributes.put("test", null); + optimizely.track(eventType.getKey(), "userId", attributes); + + // setup the attribute map captor (so we can verify its content) + ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); + + // verify that the event builder was called with the expected attributes + verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + attributeCaptor.capture(), isNull(Long.class), + isNull(String.class)); + + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + /** * Verify that {@link Optimizely#track(String, String)} handles the case where an unknown attribute * (i.e., not in the config) is passed through. From dbb352fca436401f37b0ea93942fa126741fc8d5 Mon Sep 17 00:00:00 2001 From: Joshua Wang Date: Thu, 9 Mar 2017 13:18:15 -0800 Subject: [PATCH 3/6] Improve Optimizely Tests (#81) * Use `@BeforeClass` to do static set up. The datafiles and configs need to only be set up once per test. Takes about 1 second of each test case run time. * make genericUserId static --- .../com/optimizely/ab/OptimizelyTestV2.java | 385 ++++++------- .../com/optimizely/ab/OptimizelyTestV3.java | 532 +++++++++--------- 2 files changed, 423 insertions(+), 494 deletions(-) diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java index 20d1255fa..e77d186a7 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java @@ -36,6 +36,7 @@ import com.optimizely.ab.internal.LogbackVerifier; import com.optimizely.ab.internal.ProjectValidationUtils; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -94,6 +95,19 @@ public class OptimizelyTestV2 { @Mock Bucketer mockBucketer; @Mock ErrorHandler mockErrorHandler; + private static final String genericUserId = "genericUserId"; + private static String validDatafile; + private static String noAudienceDatafile; + private static ProjectConfig validProjectConfig; + private static ProjectConfig noAudienceProjectConfig; + + @BeforeClass + public static void setUp() throws Exception { + validDatafile = validConfigJsonV2(); + noAudienceDatafile = noAudienceProjectConfigJsonV2(); + validProjectConfig = validProjectConfigV2(); + noAudienceProjectConfig = noAudienceProjectConfigV2(); + } //======== activate tests ========// /** @@ -102,16 +116,14 @@ public class OptimizelyTestV2 { */ @Test public void activateEndToEnd() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -121,7 +133,7 @@ public void activateEndToEnd() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, "userId", + when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, "userId", testUserAttributes, null)) .thenReturn(logEventToDispatch); @@ -149,14 +161,12 @@ public void activateEndToEnd() throws Exception { */ @Test public void initializationOccursForBucketerWhenBuildingOptly() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely.builder(datafile, mockEventHandler) + Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -169,13 +179,11 @@ public void initializationOccursForBucketerWhenBuildingOptly() throws Exception */ @Test public void activateForNullVariation() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -208,14 +216,12 @@ public void activateForNullVariation() throws Exception { */ @Test public void activateWhenExperimentIsNotInProject() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); Experiment unknownExperiment = createUnknownExperiment(); Variation bucketedVariation = unknownExperiment.getVariations().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -231,16 +237,14 @@ public void activateWhenExperimentIsNotInProject() throws Exception { */ @Test public void activateWithExperimentKey() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -250,7 +254,7 @@ public void activateWithExperimentKey() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), + when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), eq("userId"), eq(testUserAttributes), isNull(String.class))) .thenReturn(logEventToDispatch); @@ -274,12 +278,10 @@ public void activateWithExperimentKey() throws Exception { */ @Test public void activateWithUnknownExperimentKeyAndNoOpErrorHandler() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); Experiment unknownExperiment = createUnknownExperiment(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.ERROR, "Experiment \"unknown_experiment\" is not in the datafile."); @@ -301,12 +303,11 @@ public void activateWithUnknownExperimentKeyAndNoOpErrorHandler() throws Excepti public void activateWithUnknownExperimentKeyAndRaiseExceptionErrorHandler() throws Exception { thrown.expect(UnknownExperimentException.class); - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); + ProjectConfig validProjectConfig = validProjectConfigV2(); Experiment unknownExperiment = createUnknownExperiment(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -320,26 +321,24 @@ public void activateWithUnknownExperimentKeyAndRaiseExceptionErrorHandler() thro @Test @SuppressWarnings("unchecked") public void activateWithAttributes() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); - Attribute attribute = projectConfig.getAttributes().get(0); + Attribute attribute = validProjectConfig.getAttributes().get(0); // setup a mock event builder to return expected impression params EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), + when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), eq("userId"), anyMapOf(String.class, String.class), isNull(String.class))) .thenReturn(logEventToDispatch); @@ -357,7 +356,7 @@ public void activateWithAttributes() throws Exception { // setup the attribute map captor (so we can verify its content) ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); - verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), + verify(mockEventBuilder).createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), isNull(String.class)); @@ -377,18 +376,16 @@ public void activateWithAttributes() throws Exception { @Test @SuppressWarnings("unchecked") public void activateWithUnknownAttribute() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); // setup a mock event builder to return mock params and endpoint EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -399,7 +396,7 @@ public void activateWithUnknownAttribute() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), + when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), eq("userId"), anyMapOf(String.class, String.class), isNull(String.class))) .thenReturn(logEventToDispatch); @@ -422,7 +419,7 @@ public void activateWithUnknownAttribute() throws Exception { ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); // verify that the event builder was called with the expected attributes - verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), + verify(mockEventBuilder).createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), isNull(String.class)); @@ -550,12 +547,10 @@ public void activateWithNullAttributeValues() throws Exception { */ @Test public void activateDraftExperiment() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment draftExperiment = projectConfig.getExperiments().get(1); + Experiment draftExperiment = validProjectConfig.getExperiments().get(1); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.INFO, "Experiment \"etag2\" is not running."); @@ -572,12 +567,10 @@ public void activateDraftExperiment() throws Exception { */ @Test public void activateUserInAudience() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experimentToCheck = projectConfig.getExperiments().get(0); + Experiment experimentToCheck = validProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -593,12 +586,10 @@ public void activateUserInAudience() throws Exception { */ @Test public void activateUserNotInAudience() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experimentToCheck = projectConfig.getExperiments().get(0); + Experiment experimentToCheck = validProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -619,11 +610,9 @@ public void activateUserNotInAudience() throws Exception { */ @Test public void activateUserWithNoAudiences() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - Experiment experimentToCheck = projectConfig.getExperiments().get(0); + Experiment experimentToCheck = noAudienceProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) .withErrorHandler(mockErrorHandler) .build(); @@ -636,11 +625,9 @@ public void activateUserWithNoAudiences() throws Exception { */ @Test public void activateUserNoAttributesWithAudiences() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = validProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .build(); logbackVerifier.expectMessage(Level.INFO, @@ -655,13 +642,11 @@ public void activateUserNoAttributesWithAudiences() throws Exception { */ @Test public void activateWithEmptyUserId() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); String experimentKey = experiment.getKey(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -676,9 +661,7 @@ public void activateWithEmptyUserId() throws Exception { */ @Test public void activateForGroupExperimentWithMatchingAttributes() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getGroups() + Experiment experiment = validProjectConfig.getGroups() .get(0) .getExperiments() .get(0); @@ -686,8 +669,8 @@ public void activateForGroupExperimentWithMatchingAttributes() throws Exception when(mockBucketer.bucket(experiment, "user")).thenReturn(variation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .build(); @@ -701,15 +684,13 @@ public void activateForGroupExperimentWithMatchingAttributes() throws Exception */ @Test public void activateForGroupExperimentWithNonMatchingAttributes() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getGroups() + Experiment experiment = validProjectConfig.getGroups() .get(0) .getExperiments() .get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); String experimentKey = experiment.getKey(); @@ -728,13 +709,11 @@ public void activateForGroupExperimentWithNonMatchingAttributes() throws Excepti */ @Test public void activateForcedVariationPrecedesAudienceEval() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = validProjectConfig.getExperiments().get(0); Variation expectedVariation = experiment.getVariations().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.INFO, "User \"testUser1\" is forced in variation \"vtag1\"."); @@ -748,12 +727,10 @@ public void activateForcedVariationPrecedesAudienceEval() throws Exception { */ @Test public void activateExperimentStatusPrecedesForcedVariation() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(1); + Experiment experiment = validProjectConfig.getExperiments().get(1); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.INFO, "Experiment \"etag2\" is not running."); @@ -768,14 +745,12 @@ public void activateExperimentStatusPrecedesForcedVariation() throws Exception { */ @Test public void activateDispatchEventThrowsException() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); doThrow(new Exception("Test Exception")).when(mockEventHandler).dispatchEvent(any(LogEvent.class)); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .build(); logbackVerifier.expectMessage(Level.ERROR, "Unexpected exception in event dispatcher"); @@ -788,13 +763,11 @@ public void activateDispatchEventThrowsException() throws Exception { */ @Test public void activateLaunchedExperimentDoesNotDispatchEvent() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - Experiment launchedExperiment = projectConfig.getExperiments().get(2); + Experiment launchedExperiment = noAudienceProjectConfig.getExperiments().get(2); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) .withBucketing(mockBucketer) - .withConfig(projectConfig) + .withConfig(noAudienceProjectConfig) .build(); Variation expectedVariation = launchedExperiment.getVariations().get(0); @@ -819,9 +792,7 @@ public void activateLaunchedExperimentDoesNotDispatchEvent() throws Exception { */ @Test public void activateWithSessionId() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); EventBuilder mockEventBuilder = mock(EventBuilder.class); @@ -829,20 +800,20 @@ public void activateWithSessionId() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(eq(projectConfig), any(Experiment.class), any(Variation.class), + when(mockEventBuilder.createImpressionEvent(eq(noAudienceProjectConfig), any(Experiment.class), any(Variation.class), eq("userId"), eq(Collections.emptyMap()), eq("test_session_id"))) .thenReturn(logEventToDispatch); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .withEventBuilder(mockEventBuilder) .build(); optimizely.activate(experiment.getKey(), "userId", "test_session_id"); // verify that the event builder was called with the expected attributes - verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), any(Experiment.class), + verify(mockEventBuilder).createImpressionEvent(eq(noAudienceProjectConfig), any(Experiment.class), any(Variation.class), eq("userId"), eq(Collections.emptyMap()), eq("test_session_id")); @@ -856,21 +827,19 @@ public void activateWithSessionId() throws Exception { */ @Test public void trackEventEndToEnd() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - List allExperiments = projectConfig.getExperiments(); - EventType eventType = projectConfig.getEventTypes().get(0); + List allExperiments = noAudienceProjectConfig.getExperiments(); + EventType eventType = noAudienceProjectConfig.getEventTypes().get(0); EventBuilder eventBuilderV2 = new EventBuilderV2(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(eventBuilderV2) - .withConfig(projectConfig) + .withConfig(noAudienceProjectConfig) .withErrorHandler(mockErrorHandler) .build(); - List experimentIds = projectConfig.getExperimentIdsForGoal(eventType.getKey()); + List experimentIds = noAudienceProjectConfig.getExperimentIdsForGoal(eventType.getKey()); // Bucket to the first variation for all experiments. However, only a subset of the experiments will actually // call the bucket function. @@ -886,7 +855,7 @@ public void trackEventEndToEnd() throws Exception { // verify that the bucketing algorithm was called only on experiments corresponding to the specified goal. for (Experiment experiment : allExperiments) { - if (ProjectValidationUtils.validatePreconditions(projectConfig, experiment, "userId", emptyAttributes) && + if (ProjectValidationUtils.validatePreconditions(noAudienceProjectConfig, experiment, "userId", emptyAttributes) && experimentIds.contains(experiment.getId())) { verify(mockBucketer).bucket(experiment, "userId"); } else { @@ -904,12 +873,10 @@ public void trackEventEndToEnd() throws Exception { */ @Test public void trackEventWithUnknownEventKeyAndNoOpErrorHandler() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); EventType unknownEventType = createUnknownEventType(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(new NoOpErrorHandler()) .build(); @@ -929,12 +896,10 @@ public void trackEventWithUnknownEventKeyAndNoOpErrorHandler() throws Exception public void trackEventWithUnknownEventKeyAndRaiseExceptionErrorHandler() throws Exception { thrown.expect(UnknownEventTypeException.class); - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); EventType unknownEventType = createUnknownEventType(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -948,25 +913,23 @@ public void trackEventWithUnknownEventKeyAndRaiseExceptionErrorHandler() throws @Test @SuppressWarnings("unchecked") public void trackEventWithAttributes() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Attribute attribute = projectConfig.getAttributes().get(0); - EventType eventType = projectConfig.getEventTypes().get(0); + Attribute attribute = validProjectConfig.getAttributes().get(0); + EventType eventType = validProjectConfig.getEventTypes().get(0); // setup a mock event builder to return expected conversion params EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), anyMapOf(String.class, String.class), isNull(Long.class), isNull(String.class))) @@ -983,7 +946,7 @@ public void trackEventWithAttributes() throws Exception { ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); // verify that the event builder was called with the expected attributes - verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), attributeCaptor.capture(), isNull(Long.class), isNull(String.class)); @@ -1108,24 +1071,22 @@ public void trackEventWithNullAttributeValues() throws Exception { @Test @SuppressWarnings("unchecked") public void trackEventWithUnknownAttribute() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - EventType eventType = projectConfig.getEventTypes().get(0); + EventType eventType = validProjectConfig.getEventTypes().get(0); // setup a mock event builder to return expected conversion params EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), anyMapOf(String.class, String.class), isNull(Long.class), isNull(String.class))) @@ -1143,7 +1104,7 @@ public void trackEventWithUnknownAttribute() throws Exception { ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); // verify that the event builder was called with the expected attributes - verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), attributeCaptor.capture(), isNull(Long.class), isNull(String.class)); @@ -1159,25 +1120,23 @@ public void trackEventWithUnknownAttribute() throws Exception { */ @Test public void trackEventWithRevenue() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - EventType eventType = projectConfig.getEventTypes().get(0); + EventType eventType = validProjectConfig.getEventTypes().get(0); long revenue = 1234L; // setup a mock event builder to return expected conversion params EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), eq(Collections.emptyMap()), eq(revenue), isNull(String.class))) @@ -1190,7 +1149,7 @@ public void trackEventWithRevenue() throws Exception { ArgumentCaptor revenueCaptor = ArgumentCaptor.forClass(Long.class); // verify that the event builder was called with the expected revenue - verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), eq(Collections.emptyMap()), revenueCaptor.capture(), isNull(String.class)); @@ -1207,9 +1166,8 @@ public void trackEventWithRevenue() throws Exception { */ @Test public void trackEventWithNoValidExperiments() throws Exception { - String datafile = validConfigJsonV2(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler).build(); + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler).build(); Map attributes = new HashMap(); attributes.put("browser_type", "firefox"); @@ -1228,14 +1186,12 @@ public void trackEventWithNoValidExperiments() throws Exception { */ @Test public void trackDispatchEventThrowsException() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - EventType eventType = projectConfig.getEventTypes().get(0); + EventType eventType = noAudienceProjectConfig.getEventTypes().get(0); doThrow(new Exception("Test Exception")).when(mockEventHandler).dispatchEvent(any(LogEvent.class)); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .build(); logbackVerifier.expectMessage(Level.ERROR, "Unexpected exception in event dispatcher"); @@ -1248,12 +1204,10 @@ public void trackDispatchEventThrowsException() throws Exception { */ @Test public void trackLaunchedExperimentDoesNotDispatchEvent() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - EventType eventType = projectConfig.getEventTypes().get(3); + EventType eventType = noAudienceProjectConfig.getEventTypes().get(3); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .build(); optimizely.track(eventType.getKey(), "userId"); @@ -1268,24 +1222,22 @@ public void trackLaunchedExperimentDoesNotDispatchEvent() throws Exception { */ @Test public void trackEventWithSessionId() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - EventType eventType = projectConfig.getEventTypes().get(0); + EventType eventType = noAudienceProjectConfig.getEventTypes().get(0); // setup a mock event builder to return expected conversion params EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(noAudienceProjectConfig) .withErrorHandler(mockErrorHandler) .build(); Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + when(mockEventBuilder.createConversionEvent(eq(noAudienceProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), eq(Collections.emptyMap()), isNull(Long.class), eq("test_session_id"))) @@ -1295,7 +1247,7 @@ public void trackEventWithSessionId() throws Exception { optimizely.track(eventType.getKey(), "userId", "test_session_id"); // verify that the event builder was called with the expected attributes - verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + verify(mockEventBuilder).createConversionEvent(eq(noAudienceProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), eq(Collections.emptyMap()), isNull(Long.class), eq("test_session_id")); @@ -1309,14 +1261,13 @@ public void trackEventWithSessionId() throws Exception { */ @Test public void getVariation() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + ProjectConfig validProjectConfig = validProjectConfigV2(); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -1343,14 +1294,12 @@ public void getVariation() throws Exception { */ @Test public void getVariationWithExperimentKey() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = noAudienceProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) .withBucketing(mockBucketer) - .withConfig(projectConfig) + .withConfig(noAudienceProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -1373,10 +1322,9 @@ public void getVariationWithExperimentKey() throws Exception { */ @Test public void getVariationWithUnknownExperimentKeyAndNoOpErrorHandler() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); Experiment unknownExperiment = createUnknownExperiment(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) .withErrorHandler(new NoOpErrorHandler()) .build(); @@ -1395,15 +1343,14 @@ public void getVariationWithUnknownExperimentKeyAndNoOpErrorHandler() throws Exc */ @Test public void getVariationWithAudiences() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(0); + ProjectConfig validProjectConfig = validProjectConfigV2(); + Experiment experiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = experiment.getVariations().get(0); when(mockBucketer.bucket(experiment, "userId")).thenReturn(bucketedVariation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .withErrorHandler(mockErrorHandler) .build(); @@ -1423,11 +1370,10 @@ public void getVariationWithAudiences() throws Exception { */ @Test public void getVariationWithAudiencesNoAttributes() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(0); + ProjectConfig validProjectConfig = validProjectConfigV2(); + Experiment experiment = validProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withErrorHandler(mockErrorHandler) .build(); @@ -1444,15 +1390,13 @@ public void getVariationWithAudiencesNoAttributes() throws Exception { */ @Test public void getVariationNoAudiences() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); Variation bucketedVariation = experiment.getVariations().get(0); when(mockBucketer.bucket(experiment, "userId")).thenReturn(bucketedVariation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .withBucketing(mockBucketer) .withErrorHandler(mockErrorHandler) .build(); @@ -1471,12 +1415,10 @@ public void getVariationNoAudiences() throws Exception { public void getVariationWithUnknownExperimentKeyAndRaiseExceptionErrorHandler() throws Exception { thrown.expect(UnknownExperimentException.class); - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); Experiment unknownExperiment = createUnknownExperiment(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -1490,12 +1432,10 @@ public void getVariationWithUnknownExperimentKeyAndRaiseExceptionErrorHandler() */ @Test public void getVariationWithEmptyUserId() throws Exception { - String datafile = noAudienceProjectConfigJsonV2(); - ProjectConfig projectConfig = noAudienceProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -1509,9 +1449,7 @@ public void getVariationWithEmptyUserId() throws Exception { */ @Test public void getVariationForGroupExperimentWithMatchingAttributes() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getGroups() + Experiment experiment = validProjectConfig.getGroups() .get(0) .getExperiments() .get(0); @@ -1519,8 +1457,8 @@ public void getVariationForGroupExperimentWithMatchingAttributes() throws Except when(mockBucketer.bucket(experiment, "user")).thenReturn(variation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .build(); @@ -1534,15 +1472,14 @@ public void getVariationForGroupExperimentWithMatchingAttributes() throws Except */ @Test public void getVariationForGroupExperimentWithNonMatchingAttributes() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getGroups() + ProjectConfig validProjectConfig = validProjectConfigV2(); + Experiment experiment = validProjectConfig.getGroups() .get(0) .getExperiments() .get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); assertNull(optimizely.getVariation(experiment.getKey(), "user", @@ -1555,13 +1492,12 @@ public void getVariationForGroupExperimentWithNonMatchingAttributes() throws Exc */ @Test public void getVariationForcedVariationPrecedesAudienceEval() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(0); + ProjectConfig validProjectConfig = validProjectConfigV2(); + Experiment experiment = validProjectConfig.getExperiments().get(0); Variation expectedVariation = experiment.getVariations().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.INFO, "User \"testUser1\" is forced in variation \"vtag1\"."); @@ -1575,12 +1511,11 @@ public void getVariationForcedVariationPrecedesAudienceEval() throws Exception { */ @Test public void getVariationExperimentStatusPrecedesForcedVariation() throws Exception { - String datafile = validConfigJsonV2(); - ProjectConfig projectConfig = validProjectConfigV2(); - Experiment experiment = projectConfig.getExperiments().get(1); + ProjectConfig validProjectConfig = validProjectConfigV2(); + Experiment experiment = validProjectConfig.getExperiments().get(1); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.INFO, "Experiment \"etag2\" is not running."); diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java index 50c7272b8..0d9ceed93 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java @@ -38,6 +38,7 @@ import com.optimizely.ab.internal.ProjectValidationUtils; import com.optimizely.ab.notification.NotificationListener; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -98,6 +99,20 @@ public class OptimizelyTestV3 { @Mock Bucketer mockBucketer; @Mock ErrorHandler mockErrorHandler; + private static final String genericUserId = "genericUserId"; + private static String validDatafile; + private static String noAudienceDatafile; + private static ProjectConfig validProjectConfig; + private static ProjectConfig noAudienceProjectConfig; + + @BeforeClass + public static void setUp() throws Exception { + validDatafile = validConfigJsonV3(); + noAudienceDatafile = noAudienceProjectConfigJsonV3(); + validProjectConfig = validProjectConfigV3(); + noAudienceProjectConfig = noAudienceProjectConfigV3(); + } + //======== activate tests ========// /** @@ -106,16 +121,14 @@ public class OptimizelyTestV3 { */ @Test public void activateEndToEnd() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -125,7 +138,7 @@ public void activateEndToEnd() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, "userId", + when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, "userId", testUserAttributes, null)) .thenReturn(logEventToDispatch); @@ -153,14 +166,14 @@ public void activateEndToEnd() throws Exception { */ @Test public void initializationOccursForBucketerWhenBuildingOptly() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely.builder(datafile, mockEventHandler) + Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -173,13 +186,13 @@ public void initializationOccursForBucketerWhenBuildingOptly() throws Exception */ @Test public void activateForNullVariation() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -212,14 +225,14 @@ public void activateForNullVariation() throws Exception { */ @Test public void activateWhenExperimentIsNotInProject() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); Experiment unknownExperiment = createUnknownExperiment(); Variation bucketedVariation = unknownExperiment.getVariations().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -235,16 +248,16 @@ public void activateWhenExperimentIsNotInProject() throws Exception { */ @Test public void activateWithExperimentKey() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -254,7 +267,7 @@ public void activateWithExperimentKey() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), + when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), eq("userId"), eq(testUserAttributes), isNull(String.class))) .thenReturn(logEventToDispatch); @@ -278,12 +291,12 @@ public void activateWithExperimentKey() throws Exception { */ @Test public void activateWithUnknownExperimentKeyAndNoOpErrorHandler() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); Experiment unknownExperiment = createUnknownExperiment(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.ERROR, "Experiment \"unknown_experiment\" is not in the datafile."); @@ -305,12 +318,12 @@ public void activateWithUnknownExperimentKeyAndNoOpErrorHandler() throws Excepti public void activateWithUnknownExperimentKeyAndRaiseExceptionErrorHandler() throws Exception { thrown.expect(UnknownExperimentException.class); - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); Experiment unknownExperiment = createUnknownExperiment(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -324,26 +337,26 @@ public void activateWithUnknownExperimentKeyAndRaiseExceptionErrorHandler() thro @Test @SuppressWarnings("unchecked") public void activateWithAttributes() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); - Attribute attribute = projectConfig.getAttributes().get(0); + Attribute attribute = validProjectConfig.getAttributes().get(0); // setup a mock event builder to return expected impression params EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), + when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), eq("userId"), anyMapOf(String.class, String.class), isNull(String.class))) .thenReturn(logEventToDispatch); @@ -361,7 +374,7 @@ public void activateWithAttributes() throws Exception { // setup the attribute map captor (so we can verify its content) ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); - verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), + verify(mockEventBuilder).createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), isNull(String.class)); @@ -381,18 +394,18 @@ public void activateWithAttributes() throws Exception { @Test @SuppressWarnings("unchecked") public void activateWithUnknownAttribute() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); // setup a mock event builder to return mock params and endpoint EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -403,7 +416,7 @@ public void activateWithUnknownAttribute() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), + when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), eq("userId"), anyMapOf(String.class, String.class), isNull(String.class))) .thenReturn(logEventToDispatch); @@ -426,7 +439,7 @@ public void activateWithUnknownAttribute() throws Exception { ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); // verify that the event builder was called with the expected attributes - verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), + verify(mockEventBuilder).createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), isNull(String.class)); @@ -554,12 +567,12 @@ public void activateWithNullAttributeValues() throws Exception { */ @Test public void activateDraftExperiment() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment draftExperiment = projectConfig.getExperiments().get(1); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment draftExperiment = validProjectConfig.getExperiments().get(1); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.INFO, "Experiment \"etag2\" is not running."); @@ -576,12 +589,12 @@ public void activateDraftExperiment() throws Exception { */ @Test public void activateUserInAudience() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experimentToCheck = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experimentToCheck = validProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -597,12 +610,12 @@ public void activateUserInAudience() throws Exception { */ @Test public void activateUserNotInAudience() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experimentToCheck = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experimentToCheck = validProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -623,11 +636,9 @@ public void activateUserNotInAudience() throws Exception { */ @Test public void activateUserWithNoAudiences() throws Exception { - String datafile = noAudienceProjectConfigJsonV3(); - ProjectConfig projectConfig = noAudienceProjectConfigV3(); - Experiment experimentToCheck = projectConfig.getExperiments().get(0); + Experiment experimentToCheck = noAudienceProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) .withErrorHandler(mockErrorHandler) .build(); @@ -640,11 +651,11 @@ public void activateUserWithNoAudiences() throws Exception { */ @Test public void activateUserNoAttributesWithAudiences() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .build(); logbackVerifier.expectMessage(Level.INFO, @@ -659,13 +670,11 @@ public void activateUserNoAttributesWithAudiences() throws Exception { */ @Test public void activateWithEmptyUserId() throws Exception { - String datafile = noAudienceProjectConfigJsonV3(); - ProjectConfig projectConfig = noAudienceProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); String experimentKey = experiment.getKey(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -680,9 +689,9 @@ public void activateWithEmptyUserId() throws Exception { */ @Test public void activateForGroupExperimentWithMatchingAttributes() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getGroups() + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getGroups() .get(0) .getExperiments() .get(0); @@ -690,8 +699,8 @@ public void activateForGroupExperimentWithMatchingAttributes() throws Exception when(mockBucketer.bucket(experiment, "user")).thenReturn(variation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .build(); @@ -705,15 +714,15 @@ public void activateForGroupExperimentWithMatchingAttributes() throws Exception */ @Test public void activateForGroupExperimentWithNonMatchingAttributes() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getGroups() + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getGroups() .get(0) .getExperiments() .get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); String experimentKey = experiment.getKey(); @@ -732,13 +741,13 @@ public void activateForGroupExperimentWithNonMatchingAttributes() throws Excepti */ @Test public void activateForcedVariationPrecedesAudienceEval() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getExperiments().get(0); Variation expectedVariation = experiment.getVariations().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.INFO, "User \"testUser1\" is forced in variation \"vtag1\"."); @@ -752,12 +761,12 @@ public void activateForcedVariationPrecedesAudienceEval() throws Exception { */ @Test public void activateExperimentStatusPrecedesForcedVariation() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(1); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getExperiments().get(1); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.INFO, "Experiment \"etag2\" is not running."); @@ -772,14 +781,12 @@ public void activateExperimentStatusPrecedesForcedVariation() throws Exception { */ @Test public void activateDispatchEventThrowsException() throws Exception { - String datafile = noAudienceProjectConfigJsonV3(); - ProjectConfig projectConfig = noAudienceProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); doThrow(new Exception("Test Exception")).when(mockEventHandler).dispatchEvent(any(LogEvent.class)); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .build(); logbackVerifier.expectMessage(Level.ERROR, "Unexpected exception in event dispatcher"); @@ -794,21 +801,19 @@ public void activateDispatchEventThrowsException() throws Exception { */ @Test public void trackEventEndToEnd() throws Exception { - String datafile = noAudienceProjectConfigJsonV3(); - ProjectConfig projectConfig = noAudienceProjectConfigV3(); - List allExperiments = projectConfig.getExperiments(); - EventType eventType = projectConfig.getEventTypes().get(0); + List allExperiments = noAudienceProjectConfig.getExperiments(); + EventType eventType = noAudienceProjectConfig.getEventTypes().get(0); EventBuilder eventBuilderV2 = new EventBuilderV2(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(eventBuilderV2) - .withConfig(projectConfig) + .withConfig(noAudienceProjectConfig) .withErrorHandler(mockErrorHandler) .build(); - List experimentIds = projectConfig.getExperimentIdsForGoal(eventType.getKey()); + List experimentIds = noAudienceProjectConfig.getExperimentIdsForGoal(eventType.getKey()); // Bucket to the first variation for all experiments. However, only a subset of the experiments will actually // call the bucket function. @@ -824,7 +829,7 @@ public void trackEventEndToEnd() throws Exception { // verify that the bucketing algorithm was called only on experiments corresponding to the specified goal. for (Experiment experiment : allExperiments) { - if (ProjectValidationUtils.validatePreconditions(projectConfig, experiment, "userId", emptyAttributes) && + if (ProjectValidationUtils.validatePreconditions(noAudienceProjectConfig, experiment, "userId", emptyAttributes) && experimentIds.contains(experiment.getId())) { verify(mockBucketer).bucket(experiment, "userId"); } else { @@ -842,12 +847,12 @@ public void trackEventEndToEnd() throws Exception { */ @Test public void trackEventWithUnknownEventKeyAndNoOpErrorHandler() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); EventType unknownEventType = createUnknownEventType(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(new NoOpErrorHandler()) .build(); @@ -867,12 +872,12 @@ public void trackEventWithUnknownEventKeyAndNoOpErrorHandler() throws Exception public void trackEventWithUnknownEventKeyAndRaiseExceptionErrorHandler() throws Exception { thrown.expect(UnknownEventTypeException.class); - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); EventType unknownEventType = createUnknownEventType(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -886,25 +891,25 @@ public void trackEventWithUnknownEventKeyAndRaiseExceptionErrorHandler() throws @Test @SuppressWarnings("unchecked") public void trackEventWithAttributes() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Attribute attribute = projectConfig.getAttributes().get(0); - EventType eventType = projectConfig.getEventTypes().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Attribute attribute = validProjectConfig.getAttributes().get(0); + EventType eventType = validProjectConfig.getEventTypes().get(0); // setup a mock event builder to return expected conversion params EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), anyMapOf(String.class, String.class), isNull(Long.class), isNull(String.class))) @@ -921,7 +926,7 @@ public void trackEventWithAttributes() throws Exception { ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); // verify that the event builder was called with the expected attributes - verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), attributeCaptor.capture(), isNull(Long.class), isNull(String.class)); @@ -1046,24 +1051,24 @@ public void trackEventWithNullAttributeValues() throws Exception { @Test @SuppressWarnings("unchecked") public void trackEventWithUnknownAttribute() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - EventType eventType = projectConfig.getEventTypes().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + EventType eventType = validProjectConfig.getEventTypes().get(0); // setup a mock event builder to return expected conversion params EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), anyMapOf(String.class, String.class), isNull(Long.class), isNull(String.class))) @@ -1081,7 +1086,7 @@ public void trackEventWithUnknownAttribute() throws Exception { ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); // verify that the event builder was called with the expected attributes - verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), attributeCaptor.capture(), isNull(Long.class), isNull(String.class)); @@ -1097,25 +1102,25 @@ public void trackEventWithUnknownAttribute() throws Exception { */ @Test public void trackEventWithRevenue() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - EventType eventType = projectConfig.getEventTypes().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + EventType eventType = validProjectConfig.getEventTypes().get(0); long revenue = 1234L; // setup a mock event builder to return expected conversion params EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), eq(Collections.emptyMap()), eq(revenue), isNull(String.class))) @@ -1128,7 +1133,7 @@ public void trackEventWithRevenue() throws Exception { ArgumentCaptor revenueCaptor = ArgumentCaptor.forClass(Long.class); // verify that the event builder was called with the expected revenue - verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), eq(Collections.emptyMap()), revenueCaptor.capture(), isNull(String.class)); @@ -1145,9 +1150,9 @@ public void trackEventWithRevenue() throws Exception { */ @Test public void trackEventWithNoValidExperiments() throws Exception { - String datafile = validConfigJsonV3(); + String validDatafile = validConfigJsonV3(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler).build(); + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler).build(); Map attributes = new HashMap(); attributes.put("browser_type", "firefox"); @@ -1166,14 +1171,12 @@ public void trackEventWithNoValidExperiments() throws Exception { */ @Test public void trackDispatchEventThrowsException() throws Exception { - String datafile = noAudienceProjectConfigJsonV3(); - ProjectConfig projectConfig = noAudienceProjectConfigV3(); - EventType eventType = projectConfig.getEventTypes().get(0); + EventType eventType = noAudienceProjectConfig.getEventTypes().get(0); doThrow(new Exception("Test Exception")).when(mockEventHandler).dispatchEvent(any(LogEvent.class)); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .build(); logbackVerifier.expectMessage(Level.ERROR, "Unexpected exception in event dispatcher"); @@ -1188,11 +1191,11 @@ public void trackDispatchEventThrowsException() throws Exception { */ @Test public void getVariableInvalidVariableKeyNoOpErrorHandler() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.ERROR, "Live variable \"invalid_key\" is not in the datafile."); @@ -1208,11 +1211,11 @@ public void getVariableInvalidVariableKeyNoOpErrorHandler() throws Exception { public void getVariableInvalidVariableKeyRaiseExceptionErrorHandler() throws Exception { thrown.expect(UnknownLiveVariableException.class); - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -1225,17 +1228,17 @@ public void getVariableInvalidVariableKeyRaiseExceptionErrorHandler() throws Exc */ @Test public void getVariableStringActivateExperimentTrue() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); when(mockBucketer.bucket(activatedExperiment, "userId")) .thenReturn(bucketedVariation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -1253,17 +1256,17 @@ public void getVariableStringActivateExperimentTrue() throws Exception { */ @Test public void getVariableStringActivateExperimentFalse() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); when(mockBucketer.bucket(activatedExperiment, "userId")) .thenReturn(bucketedVariation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -1280,11 +1283,11 @@ public void getVariableStringActivateExperimentFalse() throws Exception { */ @Test public void getVariableStringReturnsDefaultValueNoExperimentsUsingLiveVariable() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.WARN, "No experiment is using variable \"unused_string_variable\"."); @@ -1297,15 +1300,15 @@ public void getVariableStringReturnsDefaultValueNoExperimentsUsingLiveVariable() */ @Test public void getVariableStringReturnsDefaultValueUserNotInVariation() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); // user isn't bucketed into a variation in any experiment when(mockBucketer.bucket(any(Experiment.class), any(String.class))) .thenReturn(null); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .build(); @@ -1320,17 +1323,17 @@ public void getVariableStringReturnsDefaultValueUserNotInVariation() throws Exce */ @Test public void getVariableBoolean() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); when(mockBucketer.bucket(activatedExperiment, "userId")) .thenReturn(bucketedVariation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .build(); @@ -1344,17 +1347,17 @@ public void getVariableBoolean() throws Exception { */ @Test public void getVariableDouble() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); when(mockBucketer.bucket(activatedExperiment, "userId")) .thenReturn(bucketedVariation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .build(); @@ -1370,17 +1373,17 @@ public void getVariableDouble() throws Exception { */ @Test public void getVariableInteger() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); when(mockBucketer.bucket(activatedExperiment, "userId")) .thenReturn(bucketedVariation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .build(); @@ -1398,14 +1401,14 @@ public void getVariableInteger() throws Exception { */ @Test public void getVariation() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -1432,14 +1435,12 @@ public void getVariation() throws Exception { */ @Test public void getVariationWithExperimentKey() throws Exception { - String datafile = noAudienceProjectConfigJsonV3(); - ProjectConfig projectConfig = noAudienceProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + Experiment activatedExperiment = noAudienceProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) .withBucketing(mockBucketer) - .withConfig(projectConfig) + .withConfig(noAudienceProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -1462,10 +1463,9 @@ public void getVariationWithExperimentKey() throws Exception { */ @Test public void getVariationWithUnknownExperimentKeyAndNoOpErrorHandler() throws Exception { - String datafile = noAudienceProjectConfigJsonV3(); Experiment unknownExperiment = createUnknownExperiment(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) .withErrorHandler(new NoOpErrorHandler()) .build(); @@ -1484,15 +1484,15 @@ public void getVariationWithUnknownExperimentKeyAndNoOpErrorHandler() throws Exc */ @Test public void getVariationWithAudiences() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = experiment.getVariations().get(0); when(mockBucketer.bucket(experiment, "userId")).thenReturn(bucketedVariation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .withErrorHandler(mockErrorHandler) .build(); @@ -1512,11 +1512,11 @@ public void getVariationWithAudiences() throws Exception { */ @Test public void getVariationWithAudiencesNoAttributes() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withErrorHandler(mockErrorHandler) .build(); @@ -1533,15 +1533,13 @@ public void getVariationWithAudiencesNoAttributes() throws Exception { */ @Test public void getVariationNoAudiences() throws Exception { - String datafile = noAudienceProjectConfigJsonV3(); - ProjectConfig projectConfig = noAudienceProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); Variation bucketedVariation = experiment.getVariations().get(0); when(mockBucketer.bucket(experiment, "userId")).thenReturn(bucketedVariation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .withBucketing(mockBucketer) .withErrorHandler(mockErrorHandler) .build(); @@ -1560,12 +1558,10 @@ public void getVariationNoAudiences() throws Exception { public void getVariationWithUnknownExperimentKeyAndRaiseExceptionErrorHandler() throws Exception { thrown.expect(UnknownExperimentException.class); - String datafile = noAudienceProjectConfigJsonV3(); - ProjectConfig projectConfig = noAudienceProjectConfigV3(); Experiment unknownExperiment = createUnknownExperiment(); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -1579,12 +1575,10 @@ public void getVariationWithUnknownExperimentKeyAndRaiseExceptionErrorHandler() */ @Test public void getVariationWithEmptyUserId() throws Exception { - String datafile = noAudienceProjectConfigJsonV3(); - ProjectConfig projectConfig = noAudienceProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(0); + Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) + .withConfig(noAudienceProjectConfig) .withErrorHandler(new RaiseExceptionErrorHandler()) .build(); @@ -1598,9 +1592,9 @@ public void getVariationWithEmptyUserId() throws Exception { */ @Test public void getVariationForGroupExperimentWithMatchingAttributes() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getGroups() + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getGroups() .get(0) .getExperiments() .get(0); @@ -1608,8 +1602,8 @@ public void getVariationForGroupExperimentWithMatchingAttributes() throws Except when(mockBucketer.bucket(experiment, "user")).thenReturn(variation); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .withBucketing(mockBucketer) .build(); @@ -1623,15 +1617,15 @@ public void getVariationForGroupExperimentWithMatchingAttributes() throws Except */ @Test public void getVariationForGroupExperimentWithNonMatchingAttributes() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getGroups() + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getGroups() .get(0) .getExperiments() .get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); assertNull(optimizely.getVariation(experiment.getKey(), "user", @@ -1644,13 +1638,13 @@ public void getVariationForGroupExperimentWithNonMatchingAttributes() throws Exc */ @Test public void getVariationForcedVariationPrecedesAudienceEval() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getExperiments().get(0); Variation expectedVariation = experiment.getVariations().get(0); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.INFO, "User \"testUser1\" is forced in variation \"vtag1\"."); @@ -1664,12 +1658,12 @@ public void getVariationForcedVariationPrecedesAudienceEval() throws Exception { */ @Test public void getVariationExperimentStatusPrecedesForcedVariation() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment experiment = projectConfig.getExperiments().get(1); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment experiment = validProjectConfig.getExperiments().get(1); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) - .withConfig(projectConfig) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) + .withConfig(validProjectConfig) .build(); logbackVerifier.expectMessage(Level.INFO, "Experiment \"etag2\" is not running."); @@ -1686,16 +1680,16 @@ public void getVariationExperimentStatusPrecedesForcedVariation() throws Excepti */ @Test public void addNotificationListener() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -1705,14 +1699,14 @@ public void addNotificationListener() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(projectConfig, activatedExperiment, + when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, userId, attributes, null)) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, userId)) .thenReturn(bucketedVariation); - when(mockEventBuilder.createImpressionEvent(projectConfig, activatedExperiment, + when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, userId, attributes, null)) .thenReturn(logEventToDispatch); @@ -1732,10 +1726,10 @@ public void addNotificationListener() throws Exception { .onExperimentActivated(activatedExperiment, userId, attributes, actualVariation); // Check if listener is notified after an event is tracked - EventType eventType = projectConfig.getEventTypes().get(0); + EventType eventType = validProjectConfig.getEventTypes().get(0); String eventKey = eventType.getKey(); - when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq(userId), + when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq(userId), eq(eventType.getId()), eq(eventKey), anyMapOf(String.class, String.class), isNull(Long.class), isNull(String.class))) @@ -1753,16 +1747,16 @@ public void addNotificationListener() throws Exception { */ @Test public void removeNotificationListener() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -1772,14 +1766,14 @@ public void removeNotificationListener() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(projectConfig, activatedExperiment, + when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, userId, attributes, null)) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, userId)) .thenReturn(bucketedVariation); - when(mockEventBuilder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, userId, + when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, userId, attributes, null)) .thenReturn(logEventToDispatch); @@ -1800,10 +1794,10 @@ public void removeNotificationListener() throws Exception { .onExperimentActivated(activatedExperiment, userId, attributes, actualVariation); // Check if listener is notified after an event is tracked - EventType eventType = projectConfig.getEventTypes().get(0); + EventType eventType = validProjectConfig.getEventTypes().get(0); String eventKey = eventType.getKey(); - when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq(userId), + when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq(userId), eq(eventType.getId()), eq(eventKey), anyMapOf(String.class, String.class))) .thenReturn(logEventToDispatch); @@ -1820,16 +1814,16 @@ public void removeNotificationListener() throws Exception { */ @Test public void clearNotificationListeners() throws Exception { - String datafile = validConfigJsonV3(); - ProjectConfig projectConfig = validProjectConfigV3(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); + String validDatafile = validConfigJsonV3(); + ProjectConfig validProjectConfig = validProjectConfigV3(); + Experiment activatedExperiment = validProjectConfig.getExperiments().get(0); Variation bucketedVariation = activatedExperiment.getVariations().get(0); EventBuilder mockEventBuilder = mock(EventBuilder.class); - Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler) .withBucketing(mockBucketer) .withEventBuilder(mockEventBuilder) - .withConfig(projectConfig) + .withConfig(validProjectConfig) .withErrorHandler(mockErrorHandler) .build(); @@ -1839,14 +1833,14 @@ public void clearNotificationListeners() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createImpressionEvent(projectConfig, activatedExperiment, + when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, userId, attributes, null)) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, userId)) .thenReturn(bucketedVariation); - when(mockEventBuilder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, userId, + when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, userId, attributes, null)) .thenReturn(logEventToDispatch); @@ -1866,10 +1860,10 @@ public void clearNotificationListeners() throws Exception { .onExperimentActivated(activatedExperiment, userId, attributes, actualVariation); // Check if listener is notified after a event is tracked - EventType eventType = projectConfig.getEventTypes().get(0); + EventType eventType = validProjectConfig.getEventTypes().get(0); String eventKey = eventType.getKey(); - when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq(userId), + when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq(userId), eq(eventType.getId()), eq(eventKey), anyMapOf(String.class, String.class))) .thenReturn(logEventToDispatch); From eeb820c07ef3c9c6e7fa3cf955c48cc3b8501eb1 Mon Sep 17 00:00:00 2001 From: Joshua Wang Date: Fri, 10 Mar 2017 13:28:28 -0800 Subject: [PATCH 4/6] speed up local build time by 97% (#82) * skip the exhaustive `generateBucketValueDistribution` when we're not testing on Travis builds * Check for ENV variable set to true by default by travis. * use exhaustive test for gradles --- .travis.yml | 1 + .../java/com/optimizely/ab/bucketing/BucketerTest.java | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5be8e640d..adb85e7dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ jdk: install: true script: - "./gradlew clean" + - "./gradlew exhaustiveTest" - "if [[ -n $TRAVIS_TAG ]]; then ./gradlew ship; else diff --git a/core-api/src/test/java/com/optimizely/ab/bucketing/BucketerTest.java b/core-api/src/test/java/com/optimizely/ab/bucketing/BucketerTest.java index 1e571945e..f22d6a7cf 100644 --- a/core-api/src/test/java/com/optimizely/ab/bucketing/BucketerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/bucketing/BucketerTest.java @@ -16,6 +16,7 @@ */ package com.optimizely.ab.bucketing; +import ch.qos.logback.classic.Level; import com.optimizely.ab.bucketing.internal.MurmurHash3; import com.optimizely.ab.categories.ExhaustiveTest; import com.optimizely.ab.config.Experiment; @@ -23,7 +24,8 @@ import com.optimizely.ab.config.TrafficAllocation; import com.optimizely.ab.config.Variation; import com.optimizely.ab.internal.LogbackVerifier; - +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -36,9 +38,6 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import ch.qos.logback.classic.Level; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - import static com.optimizely.ab.config.ProjectConfigTestUtils.validProjectConfigV2; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -79,6 +78,7 @@ public void generateBucketValueForNegativeHashCodes() throws Exception { @Test @Category(ExhaustiveTest.class) public void generateBucketValueDistribution() throws Exception { + Assume.assumeTrue(Boolean.valueOf(System.getenv("CI"))); long lowerHalfCount = 0; long totalCount = 0; int outOfRangeCount = 0; From 5405a2b68bf75ec45d01d7765304fdfe6f830db2 Mon Sep 17 00:00:00 2001 From: Michael Ng Date: Sat, 11 Mar 2017 11:19:42 -0800 Subject: [PATCH 5/6] Introduce event tags to track API (#83) --- .../com/optimizely/ab/BenchmarkUtils.java | 2 +- .../optimizely/ab/OptimizelyBenchmark.java | 2 +- .../ab/OptimizelyBuilderBenchmark.java | 2 +- .../java/com/optimizely/ab/Optimizely.java | 96 ++----- .../ab/OptimizelyRuntimeException.java | 2 +- .../ab/UnknownEventTypeException.java | 2 +- .../ab/UnknownExperimentException.java | 2 +- .../ab/UnknownLiveVariableException.java | 2 +- .../ab/annotations/VisibleForTesting.java | 2 +- .../com/optimizely/ab/bucketing/Bucketer.java | 2 +- .../optimizely/ab/bucketing/UserProfile.java | 2 +- .../ab/bucketing/internal/MurmurHash3.java | 2 +- .../com/optimizely/ab/config/Attribute.java | 2 +- .../com/optimizely/ab/config/EventType.java | 2 +- .../com/optimizely/ab/config/Experiment.java | 2 +- .../java/com/optimizely/ab/config/Group.java | 2 +- .../com/optimizely/ab/config/IdKeyMapped.java | 2 +- .../com/optimizely/ab/config/IdMapped.java | 2 +- .../optimizely/ab/config/LiveVariable.java | 2 +- .../ab/config/LiveVariableUsageInstance.java | 2 +- .../optimizely/ab/config/ProjectConfig.java | 2 +- .../ab/config/ProjectConfigUtils.java | 2 +- .../ab/config/TrafficAllocation.java | 2 +- .../com/optimizely/ab/config/Variation.java | 2 +- .../ab/config/audience/AndCondition.java | 2 +- .../ab/config/audience/Audience.java | 2 +- .../ab/config/audience/Condition.java | 2 +- .../ab/config/audience/NotCondition.java | 2 +- .../ab/config/audience/OrCondition.java | 2 +- .../ab/config/audience/UserAttribute.java | 2 +- .../parser/AudienceGsonDeserializer.java | 2 +- .../parser/AudienceJacksonDeserializer.java | 2 +- .../config/parser/ConfigParseException.java | 2 +- .../ab/config/parser/ConfigParser.java | 2 +- .../ab/config/parser/DefaultConfigParser.java | 2 +- .../parser/ExperimentGsonDeserializer.java | 2 +- .../config/parser/GroupGsonDeserializer.java | 2 +- .../parser/GroupJacksonDeserializer.java | 2 +- .../ab/config/parser/GsonConfigParser.java | 2 +- .../ab/config/parser/GsonHelpers.java | 2 +- .../ab/config/parser/JacksonConfigParser.java | 2 +- .../ab/config/parser/JsonConfigParser.java | 2 +- .../config/parser/JsonSimpleConfigParser.java | 2 +- .../parser/MissingJsonParserException.java | 2 +- .../parser/ProjectConfigGsonDeserializer.java | 2 +- .../ProjectConfigJacksonDeserializer.java | 2 +- .../com/optimizely/ab/error/ErrorHandler.java | 2 +- .../optimizely/ab/error/NoOpErrorHandler.java | 2 +- .../ab/error/RaiseExceptionErrorHandler.java | 2 +- .../com/optimizely/ab/event/EventHandler.java | 2 +- .../com/optimizely/ab/event/LogEvent.java | 2 +- .../optimizely/ab/event/NoopEventHandler.java | 2 +- .../ab/event/internal/BuildVersionInfo.java | 2 +- .../ab/event/internal/EventBuilder.java | 30 +-- .../ab/event/internal/EventBuilderV1.java | 10 +- .../ab/event/internal/EventBuilderV2.java | 35 ++- .../ab/event/internal/payload/Conversion.java | 2 +- .../ab/event/internal/payload/Decision.java | 2 +- .../ab/event/internal/payload/Event.java | 2 +- .../event/internal/payload/EventMetric.java | 2 +- .../ab/event/internal/payload/Feature.java | 9 +- .../ab/event/internal/payload/Impression.java | 2 +- .../ab/event/internal/payload/LayerState.java | 2 +- .../serializer/DefaultJsonSerializer.java | 2 +- .../internal/serializer/GsonSerializer.java | 2 +- .../serializer/JacksonSerializer.java | 2 +- .../internal/serializer/JsonSerializer.java | 2 +- .../serializer/JsonSimpleSerializer.java | 2 +- .../serializer/SerializationException.java | 2 +- .../event/internal/serializer/Serializer.java | 2 +- .../optimizely/ab/internal/EventTagUtils.java | 50 ++++ .../ab/internal/ProjectValidationUtils.java | 2 +- .../ab/internal/ReservedEventKey.java | 32 +++ .../notification/NotificationBroadcaster.java | 2 +- .../ab/notification/NotificationListener.java | 2 +- .../optimizely/ab/OptimizelyBuilderTest.java | 2 +- .../com/optimizely/ab/OptimizelyTestV1.java | 47 ++-- .../com/optimizely/ab/OptimizelyTestV2.java | 246 ++++++++++-------- .../com/optimizely/ab/OptimizelyTestV3.java | 188 +++++++++---- .../optimizely/ab/bucketing/BucketerTest.java | 2 +- .../bucketing/internal/MurmurHash3Test.java | 2 +- .../ab/categories/ExhaustiveTest.java | 2 +- .../ab/config/ProjectConfigTest.java | 2 +- .../ab/config/ProjectConfigTestUtils.java | 2 +- .../optimizely/ab/config/VariationTest.java | 2 +- .../AudienceConditionEvaluationTest.java | 2 +- .../parser/DefaultConfigParserTest.java | 2 +- .../config/parser/GsonConfigParserTest.java | 2 +- .../parser/JacksonConfigParserTest.java | 2 +- .../config/parser/JsonConfigParserTest.java | 2 +- .../parser/JsonSimpleConfigParserTest.java | 2 +- .../ab/event/NoopEventHandlerTest.java | 2 +- .../ab/event/internal/EventBuilderV1Test.java | 6 +- .../ab/event/internal/EventBuilderV2Test.java | 75 ++---- .../serializer/GsonSerializerTest.java | 2 +- .../serializer/JacksonSerializerTest.java | 2 +- .../serializer/JsonSerializerTest.java | 2 +- .../serializer/JsonSimpleSerializerTest.java | 2 +- .../serializer/SerializerTestUtils.java | 2 +- .../ProjectValidationUtilsTestV1.java | 2 +- .../ProjectValidationUtilsTestV2.java | 2 +- .../NotificationBroadcasterTest.java | 2 +- .../com/optimizely/ab/HttpClientUtils.java | 2 +- .../com/optimizely/ab/NamedThreadFactory.java | 2 +- .../ab/event/AsyncEventHandler.java | 2 +- gradle/HEADER | 2 +- gradle/license.gradle | 3 +- 107 files changed, 564 insertions(+), 451 deletions(-) create mode 100644 core-api/src/main/java/com/optimizely/ab/internal/EventTagUtils.java create mode 100644 core-api/src/main/java/com/optimizely/ab/internal/ReservedEventKey.java diff --git a/core-api/src/jmh/java/com/optimizely/ab/BenchmarkUtils.java b/core-api/src/jmh/java/com/optimizely/ab/BenchmarkUtils.java index a4b1b1ad3..8028a6830 100644 --- a/core-api/src/jmh/java/com/optimizely/ab/BenchmarkUtils.java +++ b/core-api/src/jmh/java/com/optimizely/ab/BenchmarkUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/jmh/java/com/optimizely/ab/OptimizelyBenchmark.java b/core-api/src/jmh/java/com/optimizely/ab/OptimizelyBenchmark.java index 631cf62f4..1e902f473 100644 --- a/core-api/src/jmh/java/com/optimizely/ab/OptimizelyBenchmark.java +++ b/core-api/src/jmh/java/com/optimizely/ab/OptimizelyBenchmark.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/jmh/java/com/optimizely/ab/OptimizelyBuilderBenchmark.java b/core-api/src/jmh/java/com/optimizely/ab/OptimizelyBuilderBenchmark.java index 5d9302dbc..596597f1b 100644 --- a/core-api/src/jmh/java/com/optimizely/ab/OptimizelyBuilderBenchmark.java +++ b/core-api/src/jmh/java/com/optimizely/ab/OptimizelyBuilderBenchmark.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 274ec2525..a3d30261b 100644 --- a/core-api/src/main/java/com/optimizely/ab/Optimizely.java +++ b/core-api/src/main/java/com/optimizely/ab/Optimizely.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,9 @@ import com.optimizely.ab.event.internal.EventBuilderV1; import com.optimizely.ab.event.internal.EventBuilderV2; import com.optimizely.ab.event.internal.payload.Event.ClientEngine; +import com.optimizely.ab.internal.EventTagUtils; import com.optimizely.ab.internal.ProjectValidationUtils; +import com.optimizely.ab.internal.ReservedEventKey; import com.optimizely.ab.notification.NotificationListener; import com.optimizely.ab.notification.NotificationBroadcaster; @@ -117,22 +119,9 @@ private Optimizely(@Nonnull ProjectConfig projectConfig, return activate(experimentKey, userId, Collections.emptyMap()); } - public @Nullable Variation activate(@Nonnull String experimentKey, - @Nonnull String userId, - @CheckForNull String sessionId) throws UnknownExperimentException { - return activate(experimentKey, userId, Collections.emptyMap(), sessionId); - } - public @Nullable Variation activate(@Nonnull String experimentKey, @Nonnull String userId, @Nonnull Map attributes) throws UnknownExperimentException { - return activate(experimentKey, userId, attributes, null); - } - - public @Nullable Variation activate(@Nonnull String experimentKey, - @Nonnull String userId, - @Nonnull Map attributes, - @CheckForNull String sessionId) throws UnknownExperimentException { if (!validateUserId(userId)) { logger.info("Not activating user for experiment \"{}\".", experimentKey); @@ -148,7 +137,7 @@ private Optimizely(@Nonnull ProjectConfig projectConfig, return null; } - return activate(currentConfig, experiment, userId, attributes, sessionId); + return activate(currentConfig, experiment, userId, attributes); } public @Nullable Variation activate(@Nonnull Experiment experiment, @@ -156,34 +145,19 @@ private Optimizely(@Nonnull ProjectConfig projectConfig, return activate(experiment, userId, Collections.emptyMap()); } - public @Nullable Variation activate(@Nonnull Experiment experiment, - @Nonnull String userId, - @CheckForNull String sessionId) { - return activate(experiment, userId, Collections.emptyMap(), sessionId); - } - public @Nullable Variation activate(@Nonnull Experiment experiment, @Nonnull String userId, @Nonnull Map attributes) { - return activate(experiment, userId, attributes, null); - } - - public @Nullable Variation activate(@Nonnull Experiment experiment, - @Nonnull String userId, - @Nonnull Map attributes, - @CheckForNull String sessionId) { - ProjectConfig currentConfig = getProjectConfig(); - return activate(currentConfig, experiment, userId, attributes, sessionId); + return activate(currentConfig, experiment, userId, attributes); } private @Nullable Variation activate(@Nonnull ProjectConfig projectConfig, @Nonnull Experiment experiment, @Nonnull String userId, - @Nonnull Map attributes, - @CheckForNull String sessionId) { + @Nonnull Map attributes) { // determine whether all the given attributes are present in the project config. If not, filter out the unknown // attributes. attributes = filterAttributes(projectConfig, attributes); @@ -202,7 +176,7 @@ private Optimizely(@Nonnull ProjectConfig projectConfig, if (experiment.isRunning()) { LogEvent impressionEvent = eventBuilder.createImpressionEvent(projectConfig, experiment, variation, userId, - attributes, sessionId); + attributes); logger.info("Activating user \"{}\" in experiment \"{}\".", userId, experiment.getKey()); logger.debug( "Dispatching impression event to URL {} with params {} and payload \"{}\".", @@ -225,61 +199,39 @@ private Optimizely(@Nonnull ProjectConfig projectConfig, public void track(@Nonnull String eventName, @Nonnull String userId) throws UnknownEventTypeException { - track(eventName, userId, Collections.emptyMap(), null, null); - } - - public void track(@Nonnull String eventName, - @Nonnull String userId, - @CheckForNull String sessionId) throws UnknownEventTypeException { - track(eventName, userId, Collections.emptyMap(), null, sessionId); + track(eventName, userId, Collections.emptyMap(), Collections.emptyMap()); } public void track(@Nonnull String eventName, @Nonnull String userId, @Nonnull Map attributes) throws UnknownEventTypeException { - track(eventName, userId, attributes, null, null); - } - - public void track(@Nonnull String eventName, - @Nonnull String userId, - @Nonnull Map attributes, - @CheckForNull String sessionId) throws UnknownEventTypeException { - track(eventName, userId, attributes, null, sessionId); + track(eventName, userId, attributes, Collections.emptyMap()); } + /** + * @deprecated see {@link #track(String, String, Map)} and pass in the revenue value as an event tag instead. + */ public void track(@Nonnull String eventName, @Nonnull String userId, long eventValue) throws UnknownEventTypeException { - track(eventName, userId, Collections.emptyMap(), eventValue); - } - - public void track(@Nonnull String eventName, - @Nonnull String userId, - long eventValue, - @CheckForNull String sessionId) throws UnknownEventTypeException { - track(eventName, userId, Collections.emptyMap(), eventValue, sessionId); + track(eventName, userId, Collections.emptyMap(), Collections.singletonMap( + ReservedEventKey.REVENUE.toString(), eventValue)); } + /** + * @deprecated see {@link #track(String, String, Map, long)} and pass in the revenue value as an event tag instead. + */ public void track(@Nonnull String eventName, @Nonnull String userId, @Nonnull Map attributes, long eventValue) throws UnknownEventTypeException { - track(eventName, userId, attributes, (Long)eventValue, null); + track(eventName, userId, attributes, Collections.singletonMap(ReservedEventKey.REVENUE.toString(), eventValue)); } public void track(@Nonnull String eventName, - @Nonnull String userId, - @Nonnull Map attributes, - long eventValue, - @CheckForNull String sessionId) throws UnknownEventTypeException { - track(eventName, userId, attributes, (Long)eventValue, sessionId); - } - - private void track(@Nonnull String eventName, @Nonnull String userId, @Nonnull Map attributes, - @CheckForNull Long eventValue, - @CheckForNull String sessionId) throws UnknownEventTypeException { + @Nonnull Map eventTags) throws UnknownEventTypeException { ProjectConfig currentConfig = getProjectConfig(); @@ -294,10 +246,18 @@ private void track(@Nonnull String eventName, // attributes. attributes = filterAttributes(currentConfig, attributes); + Long eventValue = null; + if (eventTags == null) { + logger.warn("Event tags is null when non-null was expected. Defaulting to an empty event tags map."); + eventTags = Collections.emptyMap(); + } else { + eventValue = EventTagUtils.getRevenueValue(eventTags); + } + // create the conversion event request parameters, then dispatch LogEvent conversionEvent = eventBuilder.createConversionEvent(currentConfig, bucketer, userId, eventType.getId(), eventType.getKey(), attributes, - eventValue, sessionId); + eventTags); if (conversionEvent == null) { logger.info("There are no valid experiments for event \"{}\" to track.", eventName); diff --git a/core-api/src/main/java/com/optimizely/ab/OptimizelyRuntimeException.java b/core-api/src/main/java/com/optimizely/ab/OptimizelyRuntimeException.java index 1ab3780ef..f61ac0fae 100644 --- a/core-api/src/main/java/com/optimizely/ab/OptimizelyRuntimeException.java +++ b/core-api/src/main/java/com/optimizely/ab/OptimizelyRuntimeException.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/UnknownEventTypeException.java b/core-api/src/main/java/com/optimizely/ab/UnknownEventTypeException.java index 75b0c1f16..9375b35e2 100644 --- a/core-api/src/main/java/com/optimizely/ab/UnknownEventTypeException.java +++ b/core-api/src/main/java/com/optimizely/ab/UnknownEventTypeException.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/UnknownExperimentException.java b/core-api/src/main/java/com/optimizely/ab/UnknownExperimentException.java index 8b5399781..01a9aa774 100644 --- a/core-api/src/main/java/com/optimizely/ab/UnknownExperimentException.java +++ b/core-api/src/main/java/com/optimizely/ab/UnknownExperimentException.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/UnknownLiveVariableException.java b/core-api/src/main/java/com/optimizely/ab/UnknownLiveVariableException.java index ac14587c2..bd8e0989b 100644 --- a/core-api/src/main/java/com/optimizely/ab/UnknownLiveVariableException.java +++ b/core-api/src/main/java/com/optimizely/ab/UnknownLiveVariableException.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/annotations/VisibleForTesting.java b/core-api/src/main/java/com/optimizely/ab/annotations/VisibleForTesting.java index f190684df..4dc0bae67 100644 --- a/core-api/src/main/java/com/optimizely/ab/annotations/VisibleForTesting.java +++ b/core-api/src/main/java/com/optimizely/ab/annotations/VisibleForTesting.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/bucketing/Bucketer.java b/core-api/src/main/java/com/optimizely/ab/bucketing/Bucketer.java index b7199a113..c1f1c7cef 100644 --- a/core-api/src/main/java/com/optimizely/ab/bucketing/Bucketer.java +++ b/core-api/src/main/java/com/optimizely/ab/bucketing/Bucketer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/bucketing/UserProfile.java b/core-api/src/main/java/com/optimizely/ab/bucketing/UserProfile.java index c5405d9ae..8238bf1c2 100644 --- a/core-api/src/main/java/com/optimizely/ab/bucketing/UserProfile.java +++ b/core-api/src/main/java/com/optimizely/ab/bucketing/UserProfile.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/bucketing/internal/MurmurHash3.java b/core-api/src/main/java/com/optimizely/ab/bucketing/internal/MurmurHash3.java index e4f09747d..93e706f3f 100644 --- a/core-api/src/main/java/com/optimizely/ab/bucketing/internal/MurmurHash3.java +++ b/core-api/src/main/java/com/optimizely/ab/bucketing/internal/MurmurHash3.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/Attribute.java b/core-api/src/main/java/com/optimizely/ab/config/Attribute.java index 6d2702982..5bb6698bc 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Attribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Attribute.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/EventType.java b/core-api/src/main/java/com/optimizely/ab/config/EventType.java index b0c0bb455..961d15392 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/EventType.java +++ b/core-api/src/main/java/com/optimizely/ab/config/EventType.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java index b8bfc99e9..f82e78903 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Experiment.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Experiment.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/Group.java b/core-api/src/main/java/com/optimizely/ab/config/Group.java index 0dc01820d..cd41bc120 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Group.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Group.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/IdKeyMapped.java b/core-api/src/main/java/com/optimizely/ab/config/IdKeyMapped.java index c9331c83f..3e7578d77 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/IdKeyMapped.java +++ b/core-api/src/main/java/com/optimizely/ab/config/IdKeyMapped.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/IdMapped.java b/core-api/src/main/java/com/optimizely/ab/config/IdMapped.java index 88828bb6a..0a032c7c8 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/IdMapped.java +++ b/core-api/src/main/java/com/optimizely/ab/config/IdMapped.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java b/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java index 0937e1ecd..4f6049282 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java +++ b/core-api/src/main/java/com/optimizely/ab/config/LiveVariable.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/LiveVariableUsageInstance.java b/core-api/src/main/java/com/optimizely/ab/config/LiveVariableUsageInstance.java index be54a05e0..05378b808 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/LiveVariableUsageInstance.java +++ b/core-api/src/main/java/com/optimizely/ab/config/LiveVariableUsageInstance.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java b/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java index 254b70963..9f7b6b292 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/ProjectConfigUtils.java b/core-api/src/main/java/com/optimizely/ab/config/ProjectConfigUtils.java index 7dde59fca..84cf6cab1 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/ProjectConfigUtils.java +++ b/core-api/src/main/java/com/optimizely/ab/config/ProjectConfigUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/TrafficAllocation.java b/core-api/src/main/java/com/optimizely/ab/config/TrafficAllocation.java index 2a6b4c9e7..a66cfb81a 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/TrafficAllocation.java +++ b/core-api/src/main/java/com/optimizely/ab/config/TrafficAllocation.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/Variation.java b/core-api/src/main/java/com/optimizely/ab/config/Variation.java index 9a31a8fb8..0991a0a5e 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/Variation.java +++ b/core-api/src/main/java/com/optimizely/ab/config/Variation.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java index 49754b2d8..b734f8a9c 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/Audience.java b/core-api/src/main/java/com/optimizely/ab/config/audience/Audience.java index 8f4578b5e..dfb6c26a5 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/Audience.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/Audience.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java index c77080231..3a47453e4 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java index c342776d9..7e269c6f3 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java index 9710962d2..c7a85556e 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index 2568a8609..34e49b0bb 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/AudienceGsonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/AudienceGsonDeserializer.java index 0066d7496..8158aeb4b 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/AudienceGsonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/AudienceGsonDeserializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/AudienceJacksonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/AudienceJacksonDeserializer.java index 6fde7aafc..1d0efc3e7 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/AudienceJacksonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/AudienceJacksonDeserializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ConfigParseException.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ConfigParseException.java index 58ff40350..a85ca5d15 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ConfigParseException.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ConfigParseException.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ConfigParser.java index 466821273..eb24b68f3 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ConfigParser.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/DefaultConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/DefaultConfigParser.java index 9530f2c7b..2d19dfb28 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/DefaultConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/DefaultConfigParser.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ExperimentGsonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ExperimentGsonDeserializer.java index 695a63b5b..6eea6be21 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ExperimentGsonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ExperimentGsonDeserializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/GroupGsonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/GroupGsonDeserializer.java index c153c34c8..05959a464 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/GroupGsonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/GroupGsonDeserializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/GroupJacksonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/GroupJacksonDeserializer.java index b7957de8d..714326fcc 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/GroupJacksonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/GroupJacksonDeserializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java index 9bfa58981..b87c0a16a 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonConfigParser.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java index 0031a4098..7ebdb02d2 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/GsonHelpers.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/JacksonConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/JacksonConfigParser.java index 2e90c49ed..67ab86771 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/JacksonConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/JacksonConfigParser.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java index a614ae6ef..56dc808d2 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java index d0bbd435a..5ac5552ee 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/MissingJsonParserException.java b/core-api/src/main/java/com/optimizely/ab/config/parser/MissingJsonParserException.java index 41c173d6c..a07539713 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/MissingJsonParserException.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/MissingJsonParserException.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java index 6b43fc281..7403f0031 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigGsonDeserializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java index 7ae04077b..38c844457 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/ProjectConfigJacksonDeserializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/error/ErrorHandler.java b/core-api/src/main/java/com/optimizely/ab/error/ErrorHandler.java index a5e74fa37..f38ee8db9 100644 --- a/core-api/src/main/java/com/optimizely/ab/error/ErrorHandler.java +++ b/core-api/src/main/java/com/optimizely/ab/error/ErrorHandler.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/error/NoOpErrorHandler.java b/core-api/src/main/java/com/optimizely/ab/error/NoOpErrorHandler.java index fc3318941..ef763a21f 100644 --- a/core-api/src/main/java/com/optimizely/ab/error/NoOpErrorHandler.java +++ b/core-api/src/main/java/com/optimizely/ab/error/NoOpErrorHandler.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/error/RaiseExceptionErrorHandler.java b/core-api/src/main/java/com/optimizely/ab/error/RaiseExceptionErrorHandler.java index 87ace738d..9aa6f17c1 100644 --- a/core-api/src/main/java/com/optimizely/ab/error/RaiseExceptionErrorHandler.java +++ b/core-api/src/main/java/com/optimizely/ab/error/RaiseExceptionErrorHandler.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/EventHandler.java b/core-api/src/main/java/com/optimizely/ab/event/EventHandler.java index 97f7ebfcc..7df38fabb 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/EventHandler.java +++ b/core-api/src/main/java/com/optimizely/ab/event/EventHandler.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/LogEvent.java b/core-api/src/main/java/com/optimizely/ab/event/LogEvent.java index f8e72fc0a..a353d9877 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/LogEvent.java +++ b/core-api/src/main/java/com/optimizely/ab/event/LogEvent.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/NoopEventHandler.java b/core-api/src/main/java/com/optimizely/ab/event/NoopEventHandler.java index 723c898a8..e84ff4c58 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/NoopEventHandler.java +++ b/core-api/src/main/java/com/optimizely/ab/event/NoopEventHandler.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/BuildVersionInfo.java b/core-api/src/main/java/com/optimizely/ab/event/internal/BuildVersionInfo.java index 9d4778797..c86568fe8 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/BuildVersionInfo.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/BuildVersionInfo.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilder.java b/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilder.java index 33bf72272..b2c4a39ef 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilder.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilder.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,24 +25,16 @@ import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import java.util.Collections; import java.util.Map; public abstract class EventBuilder { - public LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig, - @Nonnull Experiment activatedExperiment, - @Nonnull Variation variation, - @Nonnull String userId, - @Nonnull Map attributes) { - return createImpressionEvent(projectConfig, activatedExperiment, variation, userId, attributes, null); - } - public abstract LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig, @Nonnull Experiment activatedExperiment, @Nonnull Variation variation, @Nonnull String userId, - @Nonnull Map attributes, - @CheckForNull String sessionId); + @Nonnull Map attributes); public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, @Nonnull Bucketer bucketer, @@ -50,18 +42,7 @@ public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, @Nonnull String eventId, @Nonnull String eventName, @Nonnull Map attributes) { - return createConversionEvent(projectConfig, bucketer, userId, eventId, eventName, attributes, null, null); - } - - public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, - @Nonnull Bucketer bucketer, - @Nonnull String userId, - @Nonnull String eventId, - @Nonnull String eventName, - @Nonnull Map attributes, - long eventValue) { - return createConversionEvent(projectConfig, bucketer, userId, eventId, eventName, attributes, (Long)eventValue, - null); + return createConversionEvent(projectConfig, bucketer, userId, eventId, eventName, attributes, Collections.emptyMap()); } public abstract LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, @@ -70,6 +51,5 @@ public abstract LogEvent createConversionEvent(@Nonnull ProjectConfig projectCon @Nonnull String eventId, @Nonnull String eventName, @Nonnull Map attributes, - @CheckForNull Long eventValue, - @CheckForNull String sessionId); + @Nonnull Map eventTags); } diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV1.java b/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV1.java index a1a4860b7..3aead7152 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV1.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV1.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package com.optimizely.ab.event.internal; import com.optimizely.ab.event.LogEvent; +import com.optimizely.ab.internal.EventTagUtils; import com.optimizely.ab.internal.ProjectValidationUtils; import com.optimizely.ab.bucketing.Bucketer; import com.optimizely.ab.config.Attribute; @@ -67,8 +68,7 @@ public LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig, @Nonnull Experiment activatedExperiment, @Nonnull Variation variation, @Nonnull String userId, - @Nonnull Map attributes, - @CheckForNull String sessionId) { + @Nonnull Map attributes) { Map requestParams = new HashMap(); addCommonRequestParams(requestParams, projectConfig, userId, attributes); @@ -85,8 +85,7 @@ public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, @Nonnull String eventId, @Nonnull String eventName, @Nonnull Map attributes, - @CheckForNull Long eventValue, - @CheckForNull String sessionId) { + @Nonnull Map eventTags) { Map requestParams = new HashMap(); List addedExperiments = @@ -96,6 +95,7 @@ public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, return null; } + Long eventValue = EventTagUtils.getRevenueValue(eventTags); addCommonRequestParams(requestParams, projectConfig, userId, attributes); addConversionGoal(requestParams, projectConfig, eventId, eventName, eventValue); diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV2.java b/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV2.java index 295dcf565..eb835d543 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV2.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/EventBuilderV2.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ import com.optimizely.ab.event.internal.payload.LayerState; import com.optimizely.ab.event.internal.serializer.DefaultJsonSerializer; import com.optimizely.ab.event.internal.serializer.Serializer; +import com.optimizely.ab.internal.EventTagUtils; import com.optimizely.ab.internal.ProjectValidationUtils; import org.slf4j.Logger; @@ -76,8 +77,7 @@ public LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig, @Nonnull Experiment activatedExperiment, @Nonnull Variation variation, @Nonnull String userId, - @Nonnull Map attributes, - @CheckForNull String sessionId) { + @Nonnull Map attributes) { Impression impressionPayload = new Impression(); impressionPayload.setVisitorId(userId); @@ -93,12 +93,11 @@ public LogEvent createImpressionEvent(@Nonnull ProjectConfig projectConfig, impressionPayload.setLayerId(activatedExperiment.getLayerId()); impressionPayload.setAccountId(projectConfig.getAccountId()); - impressionPayload.setUserFeatures(createFeatures(attributes, projectConfig)); + impressionPayload.setUserFeatures(createUserFeatures(attributes, projectConfig)); impressionPayload.setClientEngine(clientEngine); impressionPayload.setClientVersion(clientVersion); impressionPayload.setAnonymizeIP(projectConfig.getAnonymizeIP()); impressionPayload.setRevision(projectConfig.getRevision()); - impressionPayload.setSessionId(sessionId); String payload = this.serializer.serialize(impressionPayload); return new LogEvent(RequestMethod.POST, IMPRESSION_ENDPOINT, Collections.emptyMap(), payload); @@ -110,15 +109,14 @@ public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, @Nonnull String eventId, @Nonnull String eventName, @Nonnull Map attributes, - @CheckForNull Long eventValue, - @CheckForNull String sessionId) { + @Nonnull Map eventTags) { Conversion conversionPayload = new Conversion(); conversionPayload.setVisitorId(userId); conversionPayload.setTimestamp(System.currentTimeMillis()); conversionPayload.setProjectId(projectConfig.getProjectId()); conversionPayload.setAccountId(projectConfig.getAccountId()); - conversionPayload.setUserFeatures(createFeatures(attributes, projectConfig)); + conversionPayload.setUserFeatures(createUserFeatures(attributes, projectConfig)); List layerStates = createLayerStates(projectConfig, bucketer, userId, eventName, attributes); if (layerStates.isEmpty()) { @@ -129,6 +127,7 @@ public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, conversionPayload.setEventEntityId(eventId); conversionPayload.setEventName(eventName); + Long eventValue = EventTagUtils.getRevenueValue(eventTags); if (eventValue != null) { conversionPayload.setEventMetrics( Collections.singletonList(new EventMetric(EventMetric.REVENUE_METRIC_TYPE, eventValue))); @@ -142,8 +141,7 @@ public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, conversionPayload.setClientEngine(clientEngine); conversionPayload.setClientVersion(clientVersion); conversionPayload.setRevision(projectConfig.getRevision()); - conversionPayload.setSessionId(sessionId); - + conversionPayload.setEventFeatures(createEventFeatures(eventTags)); String payload = this.serializer.serialize(conversionPayload); return new LogEvent(RequestMethod.POST, CONVERSION_ENDPOINT, Collections.emptyMap(), payload); @@ -155,7 +153,7 @@ public LogEvent createConversionEvent(@Nonnull ProjectConfig projectConfig, * @param attributes the {@code {attributeKey -> value}} mapping * @param projectConfig the current project config */ - private List createFeatures(Map attributes, ProjectConfig projectConfig) { + private List createUserFeatures(Map attributes, ProjectConfig projectConfig) { Map attributeKeyMapping = projectConfig.getAttributeKeyMapping(); List features = new ArrayList(); @@ -175,6 +173,21 @@ private List createFeatures(Map attributes, ProjectConf return features; } + /** + * Helper method to generate {@link Feature} objects from the given {@code {eventTagKey-> value}} mapping. + * + * @param eventTags the {@code {eventTagKey -> value}} mapping + */ + private List createEventFeatures(Map eventTags) { + List features = new ArrayList(); + + for (Map.Entry eventTagEntry : eventTags.entrySet()) { + String eventTagKey = eventTagEntry.getKey(); + features.add(new Feature("", eventTagKey, Feature.EVENT_FEATURE_TYPE, eventTagEntry.getValue(), false)); + } + return features; + } + /** * Helper method to create {@link LayerState} objects for all experiments mapped to an event. *

diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Conversion.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Conversion.java index f2a02a7b1..9c54389bc 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Conversion.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Conversion.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Decision.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Decision.java index 93314a890..d641552a1 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Decision.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Decision.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Event.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Event.java index 9237d5b96..974a72a4c 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Event.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Event.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/EventMetric.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/EventMetric.java index 34ad16cf6..5303871c9 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/EventMetric.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/EventMetric.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java index 8f54be74e..22df21df9 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Feature.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,16 +19,17 @@ public class Feature { public static final String CUSTOM_ATTRIBUTE_FEATURE_TYPE = "custom"; + public static final String EVENT_FEATURE_TYPE = "custom"; private String id; private String name; private String type; - private String value; + private Object value; private boolean shouldIndex; public Feature() { } - public Feature(String id, String name, String type, String value, boolean shouldIndex) { + public Feature(String id, String name, String type, Object value, boolean shouldIndex) { this.id = id; this.name = name; this.type = type; @@ -60,7 +61,7 @@ public void setType(String type) { this.type = type; } - public String getValue() { + public Object getValue() { return value; } diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Impression.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Impression.java index 771c1213c..33878a09f 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Impression.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/Impression.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/LayerState.java b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/LayerState.java index 02915ffdf..589197be7 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/payload/LayerState.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/payload/LayerState.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/DefaultJsonSerializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/DefaultJsonSerializer.java index e027f6784..717ac9e0b 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/DefaultJsonSerializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/DefaultJsonSerializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/GsonSerializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/GsonSerializer.java index 5d06715fe..a5a7aafba 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/GsonSerializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/GsonSerializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JacksonSerializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JacksonSerializer.java index 107aa47ff..afc316d34 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JacksonSerializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JacksonSerializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSerializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSerializer.java index b8d329735..eb1fa71e9 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSerializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSerializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializer.java index 625754200..549fc230e 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/SerializationException.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/SerializationException.java index de1110ff1..798d1a552 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/SerializationException.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/SerializationException.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/Serializer.java b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/Serializer.java index 24d703c45..01bd4c123 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/Serializer.java +++ b/core-api/src/main/java/com/optimizely/ab/event/internal/serializer/Serializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/internal/EventTagUtils.java b/core-api/src/main/java/com/optimizely/ab/internal/EventTagUtils.java new file mode 100644 index 000000000..42dacfc5e --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/internal/EventTagUtils.java @@ -0,0 +1,50 @@ +/** + * + * Copyright 2016-2017, Optimizely and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.optimizely.ab.internal; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import java.util.Map; + +public final class EventTagUtils { + + private static final Logger logger = LoggerFactory.getLogger(EventTagUtils.class); + + /** + * Grab the revenue value from the event tags. "revenue" is a reserved keyword. + * @param eventTags + * @return Long + */ + public static Long getRevenueValue(@Nonnull Map eventTags) { + Long eventValue = null; + if (eventTags.containsKey(ReservedEventKey.REVENUE.toString())) { + Object rawValue = eventTags.get(ReservedEventKey.REVENUE.toString()); + if (Long.class.isInstance(rawValue)) { + eventValue = (Long)rawValue; + logger.info("Parsed revenue value \"{}\" from event tags.", eventValue); + } else if (Integer.class.isInstance(rawValue)) { + eventValue = ((Integer)rawValue).longValue(); + logger.info("Parsed revenue value \"{}\" from event tags.", eventValue); + } else { + logger.warn("Failed to parse revenue value \"{}\" from event tags.", rawValue); + } + } + return eventValue; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/internal/ProjectValidationUtils.java b/core-api/src/main/java/com/optimizely/ab/internal/ProjectValidationUtils.java index 2ea00ea79..da283155d 100644 --- a/core-api/src/main/java/com/optimizely/ab/internal/ProjectValidationUtils.java +++ b/core-api/src/main/java/com/optimizely/ab/internal/ProjectValidationUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/internal/ReservedEventKey.java b/core-api/src/main/java/com/optimizely/ab/internal/ReservedEventKey.java new file mode 100644 index 000000000..ecc30f5b6 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/internal/ReservedEventKey.java @@ -0,0 +1,32 @@ +/** + * + * Copyright 2016-2017, Optimizely and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.optimizely.ab.internal; + +public enum ReservedEventKey { + REVENUE("revenue"); + + private final String key; + + ReservedEventKey(String key) { + this.key = key; + } + + @Override + public String toString() { + return key; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/notification/NotificationBroadcaster.java b/core-api/src/main/java/com/optimizely/ab/notification/NotificationBroadcaster.java index d9df413c0..bb77468df 100644 --- a/core-api/src/main/java/com/optimizely/ab/notification/NotificationBroadcaster.java +++ b/core-api/src/main/java/com/optimizely/ab/notification/NotificationBroadcaster.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/notification/NotificationListener.java b/core-api/src/main/java/com/optimizely/ab/notification/NotificationListener.java index a7c12820a..2fe6e6bc1 100644 --- a/core-api/src/main/java/com/optimizely/ab/notification/NotificationListener.java +++ b/core-api/src/main/java/com/optimizely/ab/notification/NotificationListener.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java index 13557bc76..b9958a194 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyBuilderTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV1.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV1.java index 55af6086d..47fa1b966 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV1.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV1.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import com.optimizely.ab.event.internal.EventBuilderV1; import com.optimizely.ab.internal.LogbackVerifier; import com.optimizely.ab.internal.ProjectValidationUtils; +import com.optimizely.ab.internal.ReservedEventKey; import org.junit.Rule; import org.junit.Test; @@ -122,7 +123,7 @@ public void activateEndToEnd() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, "userId", - testUserAttributes, null)) + testUserAttributes)) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -251,7 +252,7 @@ public void activateWithExperimentKey() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), eq(testUserAttributes), isNull(String.class))) + eq("userId"), eq(testUserAttributes))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -340,8 +341,7 @@ public void activateWithAttributes() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), anyMapOf(String.class, String.class), - isNull(String.class))) + eq("userId"), anyMapOf(String.class, String.class))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -361,8 +361,7 @@ public void activateWithAttributes() throws Exception { eq(activatedExperiment), eq(bucketedVariation), eq("userId"), - attributeCaptor.capture(), - isNull(String.class)); + attributeCaptor.capture()); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, hasEntry(attribute.getKey(), "attributeValue")); @@ -403,8 +402,7 @@ public void activateWithUnknownAttribute() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), anyMapOf(String.class, String.class), - isNull(String.class))) + eq("userId"), anyMapOf(String.class, String.class))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -426,8 +424,7 @@ public void activateWithUnknownAttribute() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), - eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), - isNull(String.class)); + eq(bucketedVariation), eq("userId"), attributeCaptor.capture()); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, not(hasKey("unknownAttribute"))); @@ -794,8 +791,8 @@ public void trackEventWithAttributes() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - anyMapOf(String.class, String.class), isNull(Long.class), - isNull(String.class))) + anyMapOf(String.class, String.class), + anyMapOf(String.class, Object.class))) .thenReturn(logEventToDispatch); logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); @@ -811,8 +808,8 @@ public void trackEventWithAttributes() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - attributeCaptor.capture(), isNull(Long.class), - isNull(String.class)); + attributeCaptor.capture(), + eq(Collections.emptyMap())); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, hasEntry(attribute.getKey(), "attributeValue")); @@ -848,8 +845,8 @@ public void trackEventWithUnknownAttribute() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - anyMapOf(String.class, String.class), isNull(Long.class), - isNull(String.class))) + anyMapOf(String.class, String.class), + anyMapOf(String.class, Object.class))) .thenReturn(logEventToDispatch); logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); @@ -866,8 +863,8 @@ public void trackEventWithUnknownAttribute() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - attributeCaptor.capture(), isNull(Long.class), - isNull(String.class)); + attributeCaptor.capture(), + eq(Collections.emptyMap())); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, not(hasKey("unknownAttribute"))); @@ -900,23 +897,23 @@ public void trackEventWithRevenue() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - eq(Collections.emptyMap()), eq(revenue), - isNull(String.class))) + eq(Collections.emptyMap()), + eq(Collections.singletonMap(ReservedEventKey.REVENUE.toString(), revenue)))) .thenReturn(logEventToDispatch); // call track optimizely.track(eventType.getKey(), "userId", revenue); - // setup the revenue captor (so we can verify its content) - ArgumentCaptor revenueCaptor = ArgumentCaptor.forClass(Long.class); + // setup the event tag map captor (capture the revenue value, which is a reserved keyword) + ArgumentCaptor eventTagCaptor = ArgumentCaptor.forClass(Map.class); // verify that the event builder was called with the expected revenue verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), eq(Collections.emptyMap()), - revenueCaptor.capture(), isNull(String.class)); + eventTagCaptor.capture()); - Long actualValue = revenueCaptor.getValue(); + Long actualValue = (Long)eventTagCaptor.getValue().get(ReservedEventKey.REVENUE.toString()); assertThat(actualValue, is(revenue)); verify(mockEventHandler).dispatchEvent(logEventToDispatch); diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java index e77d186a7..fedbfb88e 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV2.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import com.optimizely.ab.event.internal.EventBuilderV2; import com.optimizely.ab.internal.LogbackVerifier; import com.optimizely.ab.internal.ProjectValidationUtils; +import com.optimizely.ab.internal.ReservedEventKey; import org.junit.BeforeClass; import org.junit.Rule; @@ -46,9 +47,9 @@ import org.mockito.junit.MockitoRule; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.HashMap; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -134,7 +135,7 @@ public void activateEndToEnd() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, "userId", - testUserAttributes, null)) + testUserAttributes)) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -255,7 +256,7 @@ public void activateWithExperimentKey() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), eq(testUserAttributes), isNull(String.class))) + eq("userId"), eq(testUserAttributes))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -339,8 +340,7 @@ public void activateWithAttributes() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), anyMapOf(String.class, String.class), - isNull(String.class))) + eq("userId"), anyMapOf(String.class, String.class))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -357,8 +357,7 @@ public void activateWithAttributes() throws Exception { // setup the attribute map captor (so we can verify its content) ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); verify(mockEventBuilder).createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), - eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), - isNull(String.class)); + eq(bucketedVariation), eq("userId"), attributeCaptor.capture()); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, hasEntry(attribute.getKey(), "attributeValue")); @@ -397,8 +396,7 @@ public void activateWithUnknownAttribute() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), anyMapOf(String.class, String.class), - isNull(String.class))) + eq("userId"), anyMapOf(String.class, String.class))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -420,8 +418,7 @@ public void activateWithUnknownAttribute() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), - eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), - isNull(String.class)); + eq(bucketedVariation), eq("userId"), attributeCaptor.capture()); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, not(hasKey("unknownAttribute"))); @@ -457,8 +454,7 @@ public void activateWithNullAttributes() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), eq(Collections.emptyMap()), - isNull(String.class))) + eq("userId"), eq(Collections.emptyMap()))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -477,8 +473,7 @@ public void activateWithNullAttributes() throws Exception { // setup the attribute map captor (so we can verify its content) ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), - eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), - isNull(String.class)); + eq(bucketedVariation), eq("userId"), attributeCaptor.capture()); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, is(Collections.emptyMap())); @@ -512,8 +507,7 @@ public void activateWithNullAttributeValues() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), anyMapOf(String.class, String.class), - isNull(String.class))) + eq("userId"), anyMapOf(String.class, String.class))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -531,8 +525,7 @@ public void activateWithNullAttributeValues() throws Exception { // setup the attribute map captor (so we can verify its content) ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), - eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), - isNull(String.class)); + eq(bucketedVariation), eq("userId"), attributeCaptor.capture()); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, hasEntry(attribute.getKey(), null)); @@ -786,39 +779,6 @@ public void activateLaunchedExperimentDoesNotDispatchEvent() throws Exception { verify(mockEventHandler, never()).dispatchEvent(any(LogEvent.class)); } - /** - * Verify that {@link Optimizely#activate(String, String, String)} passes the session ID to - * {@link EventBuilder#createImpressionEvent(ProjectConfig, Experiment, Variation, String, Map, String)} - */ - @Test - public void activateWithSessionId() throws Exception { - Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); - - EventBuilder mockEventBuilder = mock(EventBuilder.class); - - Map testParams = new HashMap(); - testParams.put("test", "params"); - LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - - when(mockEventBuilder.createImpressionEvent(eq(noAudienceProjectConfig), any(Experiment.class), any(Variation.class), - eq("userId"), eq(Collections.emptyMap()), - eq("test_session_id"))) - .thenReturn(logEventToDispatch); - - Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) - .withConfig(noAudienceProjectConfig) - .withEventBuilder(mockEventBuilder) - .build(); - - optimizely.activate(experiment.getKey(), "userId", "test_session_id"); - - // verify that the event builder was called with the expected attributes - verify(mockEventBuilder).createImpressionEvent(eq(noAudienceProjectConfig), any(Experiment.class), - any(Variation.class), eq("userId"), - eq(Collections.emptyMap()), - eq("test_session_id")); - } - //======== track tests ========// /** @@ -931,8 +891,7 @@ public void trackEventWithAttributes() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - anyMapOf(String.class, String.class), isNull(Long.class), - isNull(String.class))) + anyMapOf(String.class, String.class), eq(Collections.emptyMap()))) .thenReturn(logEventToDispatch); logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); @@ -948,8 +907,7 @@ public void trackEventWithAttributes() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - attributeCaptor.capture(), isNull(Long.class), - isNull(String.class)); + attributeCaptor.capture(), eq(Collections.emptyMap())); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, hasEntry(attribute.getKey(), "attributeValue")); @@ -984,8 +942,7 @@ public void trackEventWithNullAttributes() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - eq(Collections.emptyMap()), isNull(Long.class), - isNull(String.class))) + eq(Collections.emptyMap()), eq(Collections.emptyMap()))) .thenReturn(logEventToDispatch); logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); @@ -1004,8 +961,7 @@ public void trackEventWithNullAttributes() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - attributeCaptor.capture(), isNull(Long.class), - isNull(String.class)); + attributeCaptor.capture(), eq(Collections.emptyMap())); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, is(Collections.emptyMap())); @@ -1037,8 +993,7 @@ public void trackEventWithNullAttributeValues() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - anyMapOf(String.class, String.class), isNull(Long.class), - isNull(String.class))) + anyMapOf(String.class, String.class), eq(Collections.emptyMap()))) .thenReturn(logEventToDispatch); logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); @@ -1056,8 +1011,7 @@ public void trackEventWithNullAttributeValues() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - attributeCaptor.capture(), isNull(Long.class), - isNull(String.class)); + attributeCaptor.capture(), eq(Collections.emptyMap())); verify(mockEventHandler).dispatchEvent(logEventToDispatch); } @@ -1088,8 +1042,7 @@ public void trackEventWithUnknownAttribute() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - anyMapOf(String.class, String.class), isNull(Long.class), - isNull(String.class))) + anyMapOf(String.class, String.class), eq(Collections.emptyMap()))) .thenReturn(logEventToDispatch); logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); @@ -1106,8 +1059,7 @@ public void trackEventWithUnknownAttribute() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - attributeCaptor.capture(), isNull(Long.class), - isNull(String.class)); + attributeCaptor.capture(), eq(Collections.emptyMap())); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, not(hasKey("unknownAttribute"))); @@ -1116,7 +1068,7 @@ public void trackEventWithUnknownAttribute() throws Exception { } /** - * Verify that {@link Optimizely#track(String, String)} passes through revenue. + * Verify that {@link Optimizely#track(String, String, long)} passes through revenue. */ @Test public void trackEventWithRevenue() throws Exception { @@ -1135,31 +1087,136 @@ public void trackEventWithRevenue() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); + Map eventTags = new HashMap(); + eventTags.put(ReservedEventKey.REVENUE.toString(), revenue); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - eq(Collections.emptyMap()), eq(revenue), - isNull(String.class))) + eq(Collections.emptyMap()), eq(eventTags))) .thenReturn(logEventToDispatch); // call track optimizely.track(eventType.getKey(), "userId", revenue); - // setup the revenue captor (so we can verify its content) - ArgumentCaptor revenueCaptor = ArgumentCaptor.forClass(Long.class); + // setup the event tag map captor (so we can verify its content) + ArgumentCaptor eventTagCaptor = ArgumentCaptor.forClass(Map.class); // verify that the event builder was called with the expected revenue verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), eq(Collections.emptyMap()), - revenueCaptor.capture(), isNull(String.class)); + eventTagCaptor.capture()); - Long actualValue = revenueCaptor.getValue(); + Long actualValue = (Long)eventTagCaptor.getValue().get(ReservedEventKey.REVENUE.toString()); assertThat(actualValue, is(revenue)); verify(mockEventHandler).dispatchEvent(logEventToDispatch); } + /** + * Verify that {@link Optimizely#track(String, String, Map, Map)} passes event features to + * @{link EventBuilder#createConversionEvent(ProjectConfig, Bucketer, String, String, String, Map, Map)} + */ + @Test + public void trackEventWithEventTags() throws Exception { + String datafile = validConfigJsonV2(); + ProjectConfig projectConfig = validProjectConfigV2(); + EventType eventType = projectConfig.getEventTypes().get(0); + + // setup a mock event builder to return expected conversion params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + anyMapOf(String.class, String.class), anyMapOf(String.class, Object.class))) + .thenReturn(logEventToDispatch); + + logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); + logbackVerifier.expectMessage(Level.DEBUG, "Dispatching conversion event to URL test_url with params " + + testParams + " and payload \"\""); + + Map eventTags = new HashMap(); + eventTags.put("int_param", 123); + eventTags.put("string_param", "123"); + eventTags.put("boolean_param", false); + eventTags.put("float_param", 12.3f); + + // call track + optimizely.track(eventType.getKey(), "userId", Collections.emptyMap(), eventTags); + + // setup the event map captor (so we can verify its content) + ArgumentCaptor eventTagCaptor = ArgumentCaptor.forClass(Map.class); + + // verify that the event builder was called with the expected attributes + verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + eq(Collections.emptyMap()), eventTagCaptor.capture()); + + Map actualValue = eventTagCaptor.getValue(); + assertThat(actualValue, hasEntry("int_param", eventTags.get("int_param"))); + assertThat(actualValue, hasEntry("string_param", eventTags.get("string_param"))); + assertThat(actualValue, hasEntry("boolean_param", eventTags.get("boolean_param"))); + assertThat(actualValue, hasEntry("float_param", eventTags.get("float_param"))); + + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + + /** + * Verify that {@link Optimizely#track(String, String, Map, Map)} called with null event tags will default to + * an empty map when calling @{link EventBuilder#createConversionEvent(ProjectConfig, Bucketer, String, String, String, Map, Map)} + */ + @Test + @SuppressFBWarnings( + value="NP_NONNULL_PARAM_VIOLATION", + justification="testing nullness contract violation") + public void trackEventWithNullEventTags() throws Exception { + String datafile = validConfigJsonV2(); + ProjectConfig projectConfig = validProjectConfigV2(); + EventType eventType = projectConfig.getEventTypes().get(0); + + // setup a mock event builder to return expected conversion params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + eq(Collections.emptyMap()), eq(Collections.emptyMap()))) + .thenReturn(logEventToDispatch); + + logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); + logbackVerifier.expectMessage(Level.DEBUG, "Dispatching conversion event to URL test_url with params " + + testParams + " and payload \"\""); + + // call track + optimizely.track(eventType.getKey(), "userId", Collections.emptyMap(), null); + + // verify that the event builder was called with the expected attributes + verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + eq(Collections.emptyMap()), eq(Collections.emptyMap())); + + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + /** * Verify that {@link Optimizely#track(String, String, Map)} doesn't dispatch an event when no valid experiments * correspond to an event. @@ -1216,43 +1273,6 @@ public void trackLaunchedExperimentDoesNotDispatchEvent() throws Exception { verify(mockEventHandler, never()).dispatchEvent(any(LogEvent.class)); } - /** - * Verify that {@link Optimizely#track(String, String, String)} passes the session ID to - * {@link EventBuilder#createConversionEvent(ProjectConfig, Bucketer, String, String, String, Map, Long, String)} - */ - @Test - public void trackEventWithSessionId() throws Exception { - EventType eventType = noAudienceProjectConfig.getEventTypes().get(0); - - // setup a mock event builder to return expected conversion params - EventBuilder mockEventBuilder = mock(EventBuilder.class); - - Optimizely optimizely = Optimizely.builder(noAudienceDatafile, mockEventHandler) - .withBucketing(mockBucketer) - .withEventBuilder(mockEventBuilder) - .withConfig(noAudienceProjectConfig) - .withErrorHandler(mockErrorHandler) - .build(); - - Map testParams = new HashMap(); - testParams.put("test", "params"); - LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); - when(mockEventBuilder.createConversionEvent(eq(noAudienceProjectConfig), eq(mockBucketer), eq("userId"), - eq(eventType.getId()), eq(eventType.getKey()), - eq(Collections.emptyMap()), isNull(Long.class), - eq("test_session_id"))) - .thenReturn(logEventToDispatch); - - // call track - optimizely.track(eventType.getKey(), "userId", "test_session_id"); - - // verify that the event builder was called with the expected attributes - verify(mockEventBuilder).createConversionEvent(eq(noAudienceProjectConfig), eq(mockBucketer), eq("userId"), - eq(eventType.getId()), eq(eventType.getKey()), - eq(Collections.emptyMap()), isNull(Long.class), - eq("test_session_id")); - } - //======== getVariation tests ========// /** diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java index 0d9ceed93..f3f556856 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTestV3.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import com.optimizely.ab.event.internal.EventBuilderV2; import com.optimizely.ab.internal.LogbackVerifier; import com.optimizely.ab.internal.ProjectValidationUtils; +import com.optimizely.ab.internal.ReservedEventKey; import com.optimizely.ab.notification.NotificationListener; import org.junit.BeforeClass; @@ -139,7 +140,7 @@ public void activateEndToEnd() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, "userId", - testUserAttributes, null)) + testUserAttributes)) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -268,7 +269,7 @@ public void activateWithExperimentKey() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), eq(testUserAttributes), isNull(String.class))) + eq("userId"), eq(testUserAttributes))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -357,8 +358,7 @@ public void activateWithAttributes() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), anyMapOf(String.class, String.class), - isNull(String.class))) + eq("userId"), anyMapOf(String.class, String.class))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -375,8 +375,7 @@ public void activateWithAttributes() throws Exception { // setup the attribute map captor (so we can verify its content) ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); verify(mockEventBuilder).createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), - eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), - isNull(String.class)); + eq(bucketedVariation), eq("userId"), attributeCaptor.capture()); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, hasEntry(attribute.getKey(), "attributeValue")); @@ -417,8 +416,7 @@ public void activateWithUnknownAttribute() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), anyMapOf(String.class, String.class), - isNull(String.class))) + eq("userId"), anyMapOf(String.class, String.class))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -440,8 +438,7 @@ public void activateWithUnknownAttribute() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment), - eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), - isNull(String.class)); + eq(bucketedVariation), eq("userId"), attributeCaptor.capture()); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, not(hasKey("unknownAttribute"))); @@ -477,8 +474,7 @@ public void activateWithNullAttributes() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), eq(Collections.emptyMap()), - isNull(String.class))) + eq("userId"), eq(Collections.emptyMap()))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -497,8 +493,7 @@ public void activateWithNullAttributes() throws Exception { // setup the attribute map captor (so we can verify its content) ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), - eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), - isNull(String.class)); + eq(bucketedVariation), eq("userId"), attributeCaptor.capture()); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, is(Collections.emptyMap())); @@ -532,8 +527,7 @@ public void activateWithNullAttributeValues() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(eq(projectConfig), eq(activatedExperiment), eq(bucketedVariation), - eq("userId"), anyMapOf(String.class, String.class), - isNull(String.class))) + eq("userId"), anyMapOf(String.class, String.class))) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, "userId")) @@ -551,8 +545,7 @@ public void activateWithNullAttributeValues() throws Exception { // setup the attribute map captor (so we can verify its content) ArgumentCaptor attributeCaptor = ArgumentCaptor.forClass(Map.class); verify(mockEventBuilder).createImpressionEvent(eq(projectConfig), eq(activatedExperiment), - eq(bucketedVariation), eq("userId"), attributeCaptor.capture(), - isNull(String.class)); + eq(bucketedVariation), eq("userId"), attributeCaptor.capture()); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, hasEntry(attribute.getKey(), null)); @@ -911,8 +904,7 @@ public void trackEventWithAttributes() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - anyMapOf(String.class, String.class), isNull(Long.class), - isNull(String.class))) + anyMapOf(String.class, String.class), anyMapOf(String.class, Object.class))) .thenReturn(logEventToDispatch); logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); @@ -928,8 +920,7 @@ public void trackEventWithAttributes() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - attributeCaptor.capture(), isNull(Long.class), - isNull(String.class)); + attributeCaptor.capture(), anyMapOf(String.class, Object.class)); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, hasEntry(attribute.getKey(), "attributeValue")); @@ -964,8 +955,7 @@ public void trackEventWithNullAttributes() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - eq(Collections.emptyMap()), isNull(Long.class), - isNull(String.class))) + eq(Collections.emptyMap()), eq(Collections.emptyMap()))) .thenReturn(logEventToDispatch); logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); @@ -984,8 +974,7 @@ public void trackEventWithNullAttributes() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - attributeCaptor.capture(), isNull(Long.class), - isNull(String.class)); + attributeCaptor.capture(), eq(Collections.emptyMap())); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, is(Collections.emptyMap())); @@ -1017,8 +1006,7 @@ public void trackEventWithNullAttributeValues() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - anyMapOf(String.class, String.class), isNull(Long.class), - isNull(String.class))) + anyMapOf(String.class, String.class), anyMapOf(String.class, Object.class))) .thenReturn(logEventToDispatch); logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); @@ -1036,8 +1024,7 @@ public void trackEventWithNullAttributeValues() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - attributeCaptor.capture(), isNull(Long.class), - isNull(String.class)); + attributeCaptor.capture(), eq(Collections.emptyMap())); verify(mockEventHandler).dispatchEvent(logEventToDispatch); } @@ -1070,8 +1057,7 @@ public void trackEventWithUnknownAttribute() throws Exception { LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - anyMapOf(String.class, String.class), isNull(Long.class), - isNull(String.class))) + anyMapOf(String.class, String.class), anyMapOf(String.class, Object.class))) .thenReturn(logEventToDispatch); logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); @@ -1088,8 +1074,7 @@ public void trackEventWithUnknownAttribute() throws Exception { // verify that the event builder was called with the expected attributes verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - attributeCaptor.capture(), isNull(Long.class), - isNull(String.class)); + attributeCaptor.capture(), anyMapOf(String.class, Object.class)); Map actualValue = attributeCaptor.getValue(); assertThat(actualValue, not(hasKey("unknownAttribute"))); @@ -1097,6 +1082,111 @@ public void trackEventWithUnknownAttribute() throws Exception { verify(mockEventHandler).dispatchEvent(logEventToDispatch); } + /** + * Verify that {@link Optimizely#track(String, String, Map, Map)} passes event features to + * @{link EventBuilder#createConversionEvent(ProjectConfig, Bucketer, String, String, String, Map, Map)} + */ + @Test + public void trackEventWithEventTags() throws Exception { + String datafile = validConfigJsonV3(); + ProjectConfig projectConfig = validProjectConfigV3(); + EventType eventType = projectConfig.getEventTypes().get(0); + + // setup a mock event builder to return expected conversion params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + + Map eventTags = new HashMap(); + eventTags.put("int_param", 123); + eventTags.put("string_param", "123"); + eventTags.put("boolean_param", false); + eventTags.put("float_param", 12.3f); + + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + anyMapOf(String.class, String.class), eq(eventTags))) + .thenReturn(logEventToDispatch); + + logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); + logbackVerifier.expectMessage(Level.DEBUG, "Dispatching conversion event to URL test_url with params " + + testParams + " and payload \"\""); + + // call track + optimizely.track(eventType.getKey(), "userId", Collections.emptyMap(), eventTags); + + // setup the event map captor (so we can verify its content) + ArgumentCaptor eventTagCaptor = ArgumentCaptor.forClass(Map.class); + + // verify that the event builder was called with the expected attributes + verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + eq(Collections.emptyMap()), eventTagCaptor.capture()); + + Map actualValue = eventTagCaptor.getValue(); + assertThat(actualValue, hasEntry("int_param", eventTags.get("int_param"))); + assertThat(actualValue, hasEntry("string_param", eventTags.get("string_param"))); + assertThat(actualValue, hasEntry("boolean_param", eventTags.get("boolean_param"))); + assertThat(actualValue, hasEntry("float_param", eventTags.get("float_param"))); + + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + + /** + * Verify that {@link Optimizely#track(String, String, Map, Map)} called with null event tags will default to + * an empty map when calling @{link EventBuilder#createConversionEvent(ProjectConfig, Bucketer, String, String, String, Map, Map)} + */ + @Test + @SuppressFBWarnings( + value="NP_NONNULL_PARAM_VIOLATION", + justification="testing nullness contract violation") + public void trackEventWithNullEventTags() throws Exception { + String datafile = validConfigJsonV3(); + ProjectConfig projectConfig = validProjectConfigV3(); + EventType eventType = projectConfig.getEventTypes().get(0); + + // setup a mock event builder to return expected conversion params + EventBuilder mockEventBuilder = mock(EventBuilder.class); + + Optimizely optimizely = Optimizely.builder(datafile, mockEventHandler) + .withBucketing(mockBucketer) + .withEventBuilder(mockEventBuilder) + .withConfig(projectConfig) + .withErrorHandler(mockErrorHandler) + .build(); + + Map testParams = new HashMap(); + testParams.put("test", "params"); + LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); + when(mockEventBuilder.createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + eq(Collections.emptyMap()), eq(Collections.emptyMap()))) + .thenReturn(logEventToDispatch); + + logbackVerifier.expectMessage(Level.INFO, "Tracking event \"clicked_cart\" for user \"userId\"."); + logbackVerifier.expectMessage(Level.DEBUG, "Dispatching conversion event to URL test_url with params " + + testParams + " and payload \"\""); + + // call track + optimizely.track(eventType.getKey(), "userId", Collections.emptyMap(), null); + + // verify that the event builder was called with the expected attributes + verify(mockEventBuilder).createConversionEvent(eq(projectConfig), eq(mockBucketer), eq("userId"), + eq(eventType.getId()), eq(eventType.getKey()), + eq(Collections.emptyMap()), eq(Collections.emptyMap())); + + verify(mockEventHandler).dispatchEvent(logEventToDispatch); + } + /** * Verify that {@link Optimizely#track(String, String)} passes through revenue. */ @@ -1119,26 +1209,27 @@ public void trackEventWithRevenue() throws Exception { Map testParams = new HashMap(); testParams.put("test", "params"); + Map eventTags= new HashMap(); + eventTags.put(ReservedEventKey.REVENUE.toString(), revenue); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), - eq(Collections.emptyMap()), eq(revenue), - isNull(String.class))) + eq(Collections.emptyMap()), eq(eventTags))) .thenReturn(logEventToDispatch); // call track optimizely.track(eventType.getKey(), "userId", revenue); - // setup the revenue captor (so we can verify its content) - ArgumentCaptor revenueCaptor = ArgumentCaptor.forClass(Long.class); + // setup the event tag map captor (so we can verify its content) + ArgumentCaptor eventTagCaptor = ArgumentCaptor.forClass(Map.class); // verify that the event builder was called with the expected revenue verify(mockEventBuilder).createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq("userId"), eq(eventType.getId()), eq(eventType.getKey()), eq(Collections.emptyMap()), - revenueCaptor.capture(), isNull(String.class)); + eventTagCaptor.capture()); - Long actualValue = revenueCaptor.getValue(); + Long actualValue = (Long)eventTagCaptor.getValue().get(ReservedEventKey.REVENUE.toString()); assertThat(actualValue, is(revenue)); verify(mockEventHandler).dispatchEvent(logEventToDispatch); @@ -1700,14 +1791,14 @@ public void addNotificationListener() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, - bucketedVariation, userId, attributes, null)) + bucketedVariation, userId, attributes)) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, userId)) .thenReturn(bucketedVariation); when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, - bucketedVariation, userId, attributes, null)) + bucketedVariation, userId, attributes)) .thenReturn(logEventToDispatch); // Add listener @@ -1731,8 +1822,7 @@ public void addNotificationListener() throws Exception { when(mockEventBuilder.createConversionEvent(eq(validProjectConfig), eq(mockBucketer), eq(userId), eq(eventType.getId()), eq(eventKey), - anyMapOf(String.class, String.class), isNull(Long.class), - isNull(String.class))) + anyMapOf(String.class, String.class), anyMapOf(String.class, Object.class))) .thenReturn(logEventToDispatch); optimizely.track(eventKey, userId, attributes); @@ -1767,14 +1857,14 @@ public void removeNotificationListener() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, - bucketedVariation, userId, attributes, null)) + bucketedVariation, userId, attributes)) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, userId)) .thenReturn(bucketedVariation); when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, userId, - attributes, null)) + attributes)) .thenReturn(logEventToDispatch); // Add and remove listener @@ -1834,14 +1924,14 @@ public void clearNotificationListeners() throws Exception { testParams.put("test", "params"); LogEvent logEventToDispatch = new LogEvent(RequestMethod.GET, "test_url", testParams, ""); when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, - bucketedVariation, userId, attributes, null)) + bucketedVariation, userId, attributes)) .thenReturn(logEventToDispatch); when(mockBucketer.bucket(activatedExperiment, userId)) .thenReturn(bucketedVariation); when(mockEventBuilder.createImpressionEvent(validProjectConfig, activatedExperiment, bucketedVariation, userId, - attributes, null)) + attributes)) .thenReturn(logEventToDispatch); NotificationListener listener = mock(NotificationListener.class); diff --git a/core-api/src/test/java/com/optimizely/ab/bucketing/BucketerTest.java b/core-api/src/test/java/com/optimizely/ab/bucketing/BucketerTest.java index f22d6a7cf..f98763505 100644 --- a/core-api/src/test/java/com/optimizely/ab/bucketing/BucketerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/bucketing/BucketerTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/bucketing/internal/MurmurHash3Test.java b/core-api/src/test/java/com/optimizely/ab/bucketing/internal/MurmurHash3Test.java index 65f7b9ae9..bf86a31f9 100644 --- a/core-api/src/test/java/com/optimizely/ab/bucketing/internal/MurmurHash3Test.java +++ b/core-api/src/test/java/com/optimizely/ab/bucketing/internal/MurmurHash3Test.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/categories/ExhaustiveTest.java b/core-api/src/test/java/com/optimizely/ab/categories/ExhaustiveTest.java index 9e9494fae..484112a79 100644 --- a/core-api/src/test/java/com/optimizely/ab/categories/ExhaustiveTest.java +++ b/core-api/src/test/java/com/optimizely/ab/categories/ExhaustiveTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTest.java b/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTest.java index a91e09452..69ca282a9 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTestUtils.java b/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTestUtils.java index 483100f47..261da80f9 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTestUtils.java +++ b/core-api/src/test/java/com/optimizely/ab/config/ProjectConfigTestUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/config/VariationTest.java b/core-api/src/test/java/com/optimizely/ab/config/VariationTest.java index 1e145e07d..5a76f51ca 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/VariationTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/VariationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java b/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java index 6bbb91d36..101193558 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java index f2e1a2df1..5911932f8 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java index f66c3e401..9a182c371 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java index 3adac6c70..f2c8ce8d0 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java index 064871471..cb9088ea0 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java index 48f679e7a..b8bc9c373 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/event/NoopEventHandlerTest.java b/core-api/src/test/java/com/optimizely/ab/event/NoopEventHandlerTest.java index 972120565..9b1f29e9c 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/NoopEventHandlerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/event/NoopEventHandlerTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV1Test.java b/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV1Test.java index 64f86a401..fc3c3cc09 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV1Test.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV1Test.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import com.optimizely.ab.config.Variation; import com.optimizely.ab.internal.ProjectValidationUtils; +import com.optimizely.ab.internal.ReservedEventKey; import org.junit.Test; import java.util.Arrays; @@ -162,9 +163,10 @@ public void createConversionParamsWithRevenue() throws Exception { } Map attributeMap = Collections.singletonMap(attribute.getKey(), "value"); + Map eventTagsMap = Collections.singletonMap(ReservedEventKey.REVENUE.toString(), revenue); LogEvent conversionEvent = builder.createConversionEvent(projectConfig, mockBucketAlgorithm, "userId", eventType.getId(), eventType.getKey(), attributeMap, - revenue); + eventTagsMap); Map requestParams = conversionEvent.getRequestParams(); // we're not going to verify everything, just revenue and the associated goals diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java b/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java index 3b76e8184..ffcc8ddc0 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/EventBuilderV2Test.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,16 +37,19 @@ import com.optimizely.ab.internal.LogbackVerifier; import com.optimizely.ab.internal.ProjectValidationUtils; +import com.optimizely.ab.internal.ReservedEventKey; import org.junit.Rule; import org.junit.Test; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.closeTo; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; @@ -176,27 +179,6 @@ public void createImpressionEventAndroidTVClientEngineClientVersion() throws Exc assertThat(impression.getClientVersion(), is(clientVersion)); } - /** - * Verify that passing a non-null session ID to - * {@link EventBuilder#createImpressionEvent(ProjectConfig, Experiment, Variation, String, Map, String)} properly - * constructs an impression payload with the session ID specified. - */ - @Test - public void createImpressionEventWithSessionId() throws Exception { - ProjectConfig projectConfig = ProjectConfigTestUtils.validProjectConfigV2(); - Experiment activatedExperiment = projectConfig.getExperiments().get(0); - Variation bucketedVariation = activatedExperiment.getVariations().get(0); - Attribute attribute = projectConfig.getAttributes().get(0); - String userId = "userId"; - Map attributeMap = Collections.singletonMap(attribute.getKey(), "value"); - String sessionId = "sessionid"; - - LogEvent impressionEvent = builder.createImpressionEvent(projectConfig, activatedExperiment, bucketedVariation, - userId, attributeMap, sessionId); - Impression impression = gson.fromJson(impressionEvent.getBody(), Impression.class); - assertThat(impression.getSessionId(), is(sessionId)); - } - /** * Verify {@link Conversion} event creation */ @@ -221,8 +203,11 @@ public void createConversionEvent() throws Exception { } Map attributeMap = Collections.singletonMap(attribute.getKey(), "value"); + Map eventTagMap = new HashMap(); + eventTagMap.put("boolean_param", false); + eventTagMap.put("string_param", "123"); LogEvent conversionEvent = builder.createConversionEvent(projectConfig, mockBucketAlgorithm, userId, - eventType.getId(), eventType.getKey(), attributeMap); + eventType.getId(), eventType.getKey(), attributeMap, eventTagMap); List expectedLayerStates = new ArrayList(); @@ -252,12 +237,21 @@ public void createConversionEvent() throws Exception { Feature feature = new Feature(attribute.getId(), attribute.getKey(), Feature.CUSTOM_ATTRIBUTE_FEATURE_TYPE, "value", true); List expectedUserFeatures = Collections.singletonList(feature); + + // Event Features + List expectedEventFeatures = new ArrayList(); + expectedEventFeatures.add(new Feature("", "boolean_param", Feature.EVENT_FEATURE_TYPE, + false, false)); + expectedEventFeatures.add(new Feature("", "string_param", Feature.EVENT_FEATURE_TYPE, + "123", false)); + assertThat(conversion.getUserFeatures(), is(expectedUserFeatures)); assertThat(conversion.getLayerStates(), is(expectedLayerStates)); assertThat(conversion.getEventEntityId(), is(eventType.getId())); assertThat(conversion.getEventName(), is(eventType.getKey())); assertThat(conversion.getEventMetrics(), is(Collections.emptyList())); - assertThat(conversion.getEventFeatures(), is(Collections.emptyList())); + assertTrue(conversion.getEventFeatures().containsAll(expectedEventFeatures)); + assertTrue(expectedEventFeatures.containsAll(conversion.getEventFeatures())); assertFalse(conversion.getIsGlobalHoldback()); assertThat(conversion.getAnonymizeIP(), is(projectConfig.getAnonymizeIP())); assertThat(conversion.getClientEngine(), is(ClientEngine.JAVA_SDK.getClientEngineValue())); @@ -285,9 +279,11 @@ public void createConversionParamsWithRevenue() throws Exception { } Map attributeMap = Collections.singletonMap(attribute.getKey(), "value"); + Map eventTagMap = new HashMap(); + eventTagMap.put(ReservedEventKey.REVENUE.toString(), revenue); LogEvent conversionEvent = builder.createConversionEvent(projectConfig, mockBucketAlgorithm, "userId", eventType.getId(), eventType.getKey(), attributeMap, - revenue); + eventTagMap); Conversion conversion = gson.fromJson(conversionEvent.getBody(), Conversion.class); @@ -472,33 +468,4 @@ public void createConversionEventForEventUsingLaunchedExperiment() throws Except // event will be null assertNull(conversionEvent); } - - /** - * Verify that passing a non-null session ID to - * {@link EventBuilder#createConversionEvent(ProjectConfig, Bucketer, String, String, String, Map, Long, String)} - * properly constructs an impression payload with the session ID specified. - */ - @Test - public void createConversionEventWithSessionId() throws Exception { - EventBuilderV2 builder = new EventBuilderV2(ClientEngine.ANDROID_SDK, "0.0.0"); - ProjectConfig projectConfig = ProjectConfigTestUtils.validProjectConfigV2(); - Attribute attribute = projectConfig.getAttributes().get(0); - EventType eventType = projectConfig.getEventTypes().get(0); - String userId = "userId"; - String sessionId = "sessionid"; - - Bucketer mockBucketAlgorithm = mock(Bucketer.class); - for (Experiment experiment : projectConfig.getExperiments()) { - when(mockBucketAlgorithm.bucket(experiment, userId)) - .thenReturn(experiment.getVariations().get(0)); - } - - Map attributeMap = Collections.singletonMap(attribute.getKey(), "value"); - LogEvent conversionEvent = builder.createConversionEvent(projectConfig, mockBucketAlgorithm, userId, - eventType.getId(), eventType.getKey(), attributeMap, - null, sessionId); - - Conversion conversion = gson.fromJson(conversionEvent.getBody(), Conversion.class); - assertThat(conversion.getSessionId(), is(sessionId)); - } } diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/GsonSerializerTest.java b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/GsonSerializerTest.java index e7977092a..a69340482 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/GsonSerializerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/GsonSerializerTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JacksonSerializerTest.java b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JacksonSerializerTest.java index e29c5ebaa..03792b9e2 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JacksonSerializerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JacksonSerializerTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSerializerTest.java b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSerializerTest.java index 64c7988e9..f76d02f77 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSerializerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSerializerTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializerTest.java b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializerTest.java index c2e0775f5..da286b2ba 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializerTest.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/JsonSimpleSerializerTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/SerializerTestUtils.java b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/SerializerTestUtils.java index 456ec149b..e39464fc5 100644 --- a/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/SerializerTestUtils.java +++ b/core-api/src/test/java/com/optimizely/ab/event/internal/serializer/SerializerTestUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/internal/ProjectValidationUtilsTestV1.java b/core-api/src/test/java/com/optimizely/ab/internal/ProjectValidationUtilsTestV1.java index 771635034..5bfce4fe2 100644 --- a/core-api/src/test/java/com/optimizely/ab/internal/ProjectValidationUtilsTestV1.java +++ b/core-api/src/test/java/com/optimizely/ab/internal/ProjectValidationUtilsTestV1.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/internal/ProjectValidationUtilsTestV2.java b/core-api/src/test/java/com/optimizely/ab/internal/ProjectValidationUtilsTestV2.java index f7828eb77..c4b954742 100644 --- a/core-api/src/test/java/com/optimizely/ab/internal/ProjectValidationUtilsTestV2.java +++ b/core-api/src/test/java/com/optimizely/ab/internal/ProjectValidationUtilsTestV2.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/test/java/com/optimizely/ab/notification/NotificationBroadcasterTest.java b/core-api/src/test/java/com/optimizely/ab/notification/NotificationBroadcasterTest.java index 9bfa36d05..d754c4233 100644 --- a/core-api/src/test/java/com/optimizely/ab/notification/NotificationBroadcasterTest.java +++ b/core-api/src/test/java/com/optimizely/ab/notification/NotificationBroadcasterTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/HttpClientUtils.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/HttpClientUtils.java index d26249b55..bb7ca3e76 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/HttpClientUtils.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/HttpClientUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/NamedThreadFactory.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/NamedThreadFactory.java index 098d4f605..8e1443c8a 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/NamedThreadFactory.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/NamedThreadFactory.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java index 881dabe72..6cce2fc00 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016, Optimizely and contributors + * Copyright 2016-2017, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gradle/HEADER b/gradle/HEADER index 95949fc29..1e8f53dac 100644 --- a/gradle/HEADER +++ b/gradle/HEADER @@ -1,5 +1,5 @@ - Copyright ${year}, Optimizely and contributors + Copyright ${start_year}-${end_year}, Optimizely and contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/gradle/license.gradle b/gradle/license.gradle index 7ae4e4bf3..5e46c7a89 100644 --- a/gradle/license.gradle +++ b/gradle/license.gradle @@ -3,6 +3,7 @@ subprojects { license { header rootProject.file('gradle/HEADER') excludes(['**/*.properties', '**/*.txt', '**/*.conf', '**/*.xml', '**/*.json', '**/LogbackVerifier.java']) - ext.year = '2016' // year that the project was created + ext.start_year = '2016' // year that the project was created + ext.end_year = Calendar.getInstance().get(Calendar.YEAR) } } From a7a1652a80fe79389d9d726a5d9304d6cab06f8d Mon Sep 17 00:00:00 2001 From: Michael Ng Date: Fri, 17 Mar 2017 13:55:15 -0700 Subject: [PATCH 6/6] Prepare 1.6.0 release (#84) --- CHANGELOG.md | 9 +++++++++ gradle.properties | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f1d990de..b577c49a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 1.6.0 + +March 17, 2017 + +- Add event tags to `track` API and include in the event payload +- Deprecates the `eventValue` parameter from the `track` method. Should use event tags to pass in event value instead +- Gracefully handle a null attributes parameter +- Gracefully handle a null/empty datafile when using the Gson parser + ## 1.5.0 February 16, 2017 diff --git a/gradle.properties b/gradle.properties index 59036d0e7..7fb8b8b76 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # Maven version -version = 1.5.0-SNAPSHOT +version = 1.6.0-SNAPSHOT # Artifact paths mavenS3Bucket = optimizely-maven