From 02be21e08fdf5d77f89ec5d0111d44159cb25bc0 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Fri, 13 Dec 2024 11:12:49 -0300 Subject: [PATCH] WIP tests --- .../assets/split_changes_imp_toggle.json | 124 ++++++++++++ .../java/helper/IntegrationHelper.java | 4 + .../toggle/ImpressionsToggleTest.java | 190 ++++++++++++++++++ 3 files changed, 318 insertions(+) create mode 100644 src/androidTest/assets/split_changes_imp_toggle.json create mode 100644 src/androidTest/java/tests/integration/toggle/ImpressionsToggleTest.java diff --git a/src/androidTest/assets/split_changes_imp_toggle.json b/src/androidTest/assets/split_changes_imp_toggle.json new file mode 100644 index 000000000..d1f7196ce --- /dev/null +++ b/src/androidTest/assets/split_changes_imp_toggle.json @@ -0,0 +1,124 @@ +{ + "splits": [ + { + "trafficTypeName": "user", + "name": "tracked", + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "seed": -1992295819, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1506703262916, + "algo": 2, + "trackImpressions": true, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 + } + ], + "label": "in segment new_segment" + } + ] + }, + { + "trafficTypeName": "user", + "name": "not_tracked", + "trafficAllocation": 100, + "trafficAllocationSeed": -285565213, + "seed": -1992295819, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1506703262916, + "algo": 2, + "trackImpressions": false, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 + } + ], + "label": "in segment new_segment" + } + ] + } + ], + "since": 1506703262916, + "till": 1506703262916 +} diff --git a/src/androidTest/java/helper/IntegrationHelper.java b/src/androidTest/java/helper/IntegrationHelper.java index 528f730fa..6144c6a7b 100644 --- a/src/androidTest/java/helper/IntegrationHelper.java +++ b/src/androidTest/java/helper/IntegrationHelper.java @@ -442,5 +442,9 @@ public interface StreamingResponseClosure { public static class ServicePath { public static final String MEMBERSHIPS = "memberships"; public static final String SPLIT_CHANGES = "splitChanges"; + public static final String EVENTS = "events"; + public static final String UNIQUE_KEYS = "keys/cs"; + public static final String COUNT = "testImpressions/count"; + public static final String IMPRESSIONS = "testImpressions/bulk"; } } diff --git a/src/androidTest/java/tests/integration/toggle/ImpressionsToggleTest.java b/src/androidTest/java/tests/integration/toggle/ImpressionsToggleTest.java new file mode 100644 index 000000000..9c2a7bb69 --- /dev/null +++ b/src/androidTest/java/tests/integration/toggle/ImpressionsToggleTest.java @@ -0,0 +1,190 @@ +package tests.integration.toggle; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static helper.IntegrationHelper.buildFactory; +import static helper.IntegrationHelper.emptyAllSegments; +import static helper.IntegrationHelper.getSinceFromUri; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import helper.DatabaseHelper; +import helper.FileHelper; +import helper.IntegrationHelper; +import io.split.android.client.ServiceEndpoints; +import io.split.android.client.SplitClient; +import io.split.android.client.SplitClientConfig; +import io.split.android.client.SplitFactory; +import io.split.android.client.SplitManager; +import io.split.android.client.api.SplitView; +import io.split.android.client.dtos.SplitChange; +import io.split.android.client.events.SplitEvent; +import io.split.android.client.service.impressions.ImpressionsMode; +import io.split.android.client.utils.Json; +import io.split.android.client.utils.logger.SplitLogLevel; +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import tests.integration.shared.TestingHelper; + +public class ImpressionsToggleTest { + + private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); + private CountDownLatch mRequestCountdownLatch; + private MockWebServer mWebServer; + + private final AtomicReference mCountBody = new AtomicReference<>(null); + private final AtomicReference mImpressionsBody = new AtomicReference<>(null); + private final AtomicReference mUniqueKeysBody = new AtomicReference<>(null); + + @Before + public void setUp() { + setupServer(); + mRequestCountdownLatch = new CountDownLatch(1); + mCountBody.set(null); + mImpressionsBody.set(null); + mUniqueKeysBody.set(null); + } + + @Test + public void managerContainsProperty() throws InterruptedException { + SplitFactory splitFactory = getReadyFactory(ImpressionsMode.OPTIMIZED); + + SplitManager manager = splitFactory.manager(); + List splits = manager.splits(); + + assertTrue(manager.split("tracked").trackImpressions); + assertFalse(manager.split("not_tracked").trackImpressions); + assertEquals(2, splits.size()); + } + + @Test + public void test() throws InterruptedException { + // 1. Initialize SDK in impressions NONE mode + SplitFactory splitFactory = getReadyFactory(ImpressionsMode.NONE); + + // 2. Fetch splitChanges with both flags with trackImpressions true & false + SplitClient client = splitFactory.client(); + client.getTreatment("tracked"); + client.getTreatment("not_tracked"); + + // 3. Verify all counts & mtks are tracked + client.flush(); + + Thread.sleep(2000); + assertNotNull(mCountBody.get()); + assertNotNull(mUniqueKeysBody.get()); + assertNull(mImpressionsBody.get()); + } + + @Test + public void test2() { + // 1. Initialize SDK in impressions DEBUG mode + // 2. Fetch splitChanges with both flags with trackImpressions true & false + // 3. Verify counts & MTKs are tracked only for trackImpressions false + // 4. Verify impressions are tracked for trackImpressions true + } + + @Test + public void test3() { + // 1. Initialize SDK in impressions OPTIMIZED mode + // 2. Fetch splitChanges with both flags with trackImpressions true & false + // 3. Verify counts & MTKs are tracked only for trackImpressions false + // 4. Verify impressions are tracked for trackImpressions true + } + + private SplitFactory getReadyFactory(ImpressionsMode impressionsMode) throws InterruptedException { + SplitFactory splitFactory = getSplitFactory(impressionsMode); + CountDownLatch latch = new CountDownLatch(1); + splitFactory.client().on(SplitEvent.SDK_READY, new TestingHelper.TestEventTask(latch)); + mRequestCountdownLatch.countDown(); + + boolean await = latch.await(5, TimeUnit.SECONDS); + + if (!await) { + fail("Client was not ready"); + } + + return splitFactory; + } + + private SplitFactory getSplitFactory(ImpressionsMode impressionsMode) { + final String url = mWebServer.url("/").url().toString(); + ServiceEndpoints endpoints = ServiceEndpoints.builder() + .apiEndpoint(url) + .eventsEndpoint(url) + .telemetryServiceEndpoint(url) + .build(); + SplitClientConfig config = new SplitClientConfig.Builder() + .serviceEndpoints(endpoints) + .impressionsMode(impressionsMode) + .streamingEnabled(false) + .featuresRefreshRate(9999) + .segmentsRefreshRate(9999) + .impressionsRefreshRate(9999) + .logLevel(SplitLogLevel.VERBOSE) + .streamingEnabled(false) + .build(); + + return buildFactory(IntegrationHelper.dummyApiKey(), IntegrationHelper.dummyUserKey(), config, + mContext, null, DatabaseHelper.getTestDatabase(mContext)); + } + + private void setupServer() { + mWebServer = new MockWebServer(); + + final Dispatcher dispatcher = new Dispatcher() { + + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + mRequestCountdownLatch.await(); + if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) { + return new MockResponse().setResponseCode(200).setBody(emptyAllSegments()); + } else if (request.getPath().contains("/" + IntegrationHelper.ServicePath.SPLIT_CHANGES)) { + String sinceFromUri = getSinceFromUri(request.getRequestUrl().uri()); + if (sinceFromUri.equals("-1")) { + return new MockResponse().setResponseCode(200).setBody(loadSplitChanges()); + } else { + return new MockResponse().setResponseCode(200) + .setBody(IntegrationHelper.emptySplitChanges(1506703262916L, 1506703262916L)); + } + } else if (request.getPath().contains("/" + IntegrationHelper.ServicePath.COUNT)) { + mCountBody.set(request.getBody().readUtf8()); + return new MockResponse().setResponseCode(200); + } else if (request.getPath().contains("/" + IntegrationHelper.ServicePath.IMPRESSIONS)) { + mImpressionsBody.set(request.getBody().readUtf8()); + return new MockResponse().setResponseCode(200); + } else if (request.getPath().contains("/" + IntegrationHelper.ServicePath.UNIQUE_KEYS)) { + mUniqueKeysBody.set(request.getBody().readUtf8()); + return new MockResponse().setResponseCode(200); + } else { + return new MockResponse().setResponseCode(404); + } + } + }; + mWebServer.setDispatcher(dispatcher); + } + + private String loadSplitChanges() { + FileHelper fileHelper = new FileHelper(); + String change = fileHelper.loadFileContent(mContext, "split_changes_imp_toggle.json"); + SplitChange parsedChange = Json.fromJson(change, SplitChange.class); + parsedChange.since = parsedChange.till; + return Json.toJson(parsedChange); + } +}