From acab1645cfb7af4b370c24b427a9152b38c88ad7 Mon Sep 17 00:00:00 2001 From: Greg Leonard <45019882+greg-el@users.noreply.github.com> Date: Thu, 7 Sep 2023 17:18:24 +0100 Subject: [PATCH] Update tests --- .../snowplow/tracker/LinkDecoratorTest.kt | 155 +++++++++++++++--- .../core/tracker/CrossDeviceParameter.kt | 8 + .../core/tracker/CrossDeviceParameters.kt | 9 - .../core/tracker/TrackerControllerImpl.kt | 77 +++++---- .../snowplow/controller/TrackerController.kt | 15 +- 5 files changed, 195 insertions(+), 69 deletions(-) create mode 100644 snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/CrossDeviceParameter.kt delete mode 100644 snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/CrossDeviceParameters.kt diff --git a/snowplow-tracker/src/androidTest/java/com/snowplowanalytics/snowplow/tracker/LinkDecoratorTest.kt b/snowplow-tracker/src/androidTest/java/com/snowplowanalytics/snowplow/tracker/LinkDecoratorTest.kt index 6cc08bfc2..b3bb30b84 100644 --- a/snowplow-tracker/src/androidTest/java/com/snowplowanalytics/snowplow/tracker/LinkDecoratorTest.kt +++ b/snowplow-tracker/src/androidTest/java/com/snowplowanalytics/snowplow/tracker/LinkDecoratorTest.kt @@ -1,37 +1,129 @@ -package com.snowplowanalytics.snowplow.tracker; - -import android.net.Uri +package com.snowplowanalytics.snowplow.tracker -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; +import android.net.Uri +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.snowplowanalytics.core.tracker.CrossDeviceParameter import com.snowplowanalytics.snowplow.Snowplow -import com.snowplowanalytics.snowplow.configuration.EmitterConfiguration import com.snowplowanalytics.snowplow.configuration.NetworkConfiguration import com.snowplowanalytics.snowplow.configuration.SessionConfiguration +import com.snowplowanalytics.snowplow.configuration.SubjectConfiguration import com.snowplowanalytics.snowplow.configuration.TrackerConfiguration +import com.snowplowanalytics.snowplow.controller.SessionController import com.snowplowanalytics.snowplow.controller.TrackerController - -import com.snowplowanalytics.snowplow.network.HttpMethod; +import com.snowplowanalytics.snowplow.event.ScreenView +import com.snowplowanalytics.snowplow.network.HttpMethod import com.snowplowanalytics.snowplow.util.TimeMeasure - - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith import java.util.concurrent.TimeUnit @RunWith(AndroidJUnit4::class) class LinkDecoratorTest { + private lateinit var tracker: TrackerController + private lateinit var session: SessionController + private lateinit var userId: String + private val testLink = Uri.parse("http://example.com") + private fun matchesRegex(pattern: Regex, result: Uri) { + Assert.assertTrue( + "$result\ndoes not match expected: $pattern", + pattern.matches(result.toString()) + ) + } + + + @Before + fun before() { + tracker = getTracker() + session = tracker.session!! + userId = session.userId + } + @Test - fun testLink() { - val tracker = getTracker(); - val test = tracker.decorateLink(Uri.parse("whatever")) - Assert.assertEquals(test, Uri.parse("blah")) - Assert.assertEquals(1, 1) + fun testWithoutSession() { + val tracker = getTrackerNoSession() + val result = tracker.decorateLink(testLink) + Assert.assertEquals(null, result) } + @Test + fun testDecorateUriWithNoParams() { + tracker.track(ScreenView("test")) + + val pattern = + Regex("""http://example\.com\?_sp=$userId\.\d{13}\.${session.sessionId}\.decoratorTest\.mob\.subjectUserId""") + val result = tracker.decorateLink(testLink) + + matchesRegex(pattern, result!!) + } + + @Test + fun testDecorateUriWithOtherParam() { + tracker.track(ScreenView("test")) + + val pattern = + Regex("""http://example\.com\?a=b&_sp=$userId\.\d{13}\.${session.sessionId}\.decoratorTest\.mob\.subjectUserId""") + val result = + tracker.decorateLink(testLink.buildUpon().appendQueryParameter("a", "b").build()) + + matchesRegex(pattern, result!!) + } + + @Test + fun testDecorateUriWithParameters() { + tracker.track(ScreenView("test")) + + val sessionId = session.sessionId + val expectedParams = hashMapOf( + listOf(CrossDeviceParameter.SESSION_ID) to ".$sessionId", + + listOf( + CrossDeviceParameter.SESSION_ID, + CrossDeviceParameter.SOURCE_ID + ) to ".$sessionId.decoratorTest", + + listOf( + CrossDeviceParameter.SESSION_ID, + CrossDeviceParameter.SOURCE_ID, + CrossDeviceParameter.SOURCE_PLATFORM + ) to ".$sessionId.decoratorTest.mob", + + listOf( + CrossDeviceParameter.SESSION_ID, + CrossDeviceParameter.SOURCE_ID, + CrossDeviceParameter.SOURCE_PLATFORM, + CrossDeviceParameter.USER_ID + ) to ".$sessionId.decoratorTest.mob.subjectUserId", + + listOf( + CrossDeviceParameter.SESSION_ID, + CrossDeviceParameter.SOURCE_PLATFORM, + ) to ".$sessionId..mob", + + listOf( + CrossDeviceParameter.SOURCE_ID, + CrossDeviceParameter.USER_ID + ) to "..decoratorTest..subjectUserId", + + listOf( + CrossDeviceParameter.USER_ID + ) to "....subjectUserId", + + emptyList() to "", + ) + + for ((param, spVal) in expectedParams.entries) { + val pattern = + Regex("""http://example\.com\?_sp=$userId\.\d{13}$spVal""") + val result = tracker.decorateLink(testLink, param) + + matchesRegex(pattern, result!!) + } + } fun getTracker(): TrackerController { val context = InstrumentationRegistry.getInstrumentation().targetContext @@ -41,17 +133,36 @@ class LinkDecoratorTest { val trackerConfiguration = TrackerConfiguration("decoratorTest") .sessionContext(true) + val subjectConfig = SubjectConfiguration().userId("subjectUserId") + val sessionConfiguration = SessionConfiguration( - TimeMeasure(1, TimeUnit.MILLISECONDS), - TimeMeasure(1, TimeUnit.MILLISECONDS), + TimeMeasure(6, TimeUnit.SECONDS), + TimeMeasure(30, TimeUnit.SECONDS), + ) + + return Snowplow.createTracker( + context, + "namespace" + Math.random(), + networkConfiguration, + trackerConfiguration, + sessionConfiguration, + subjectConfig ) + } + + fun getTrackerNoSession(): TrackerController { + val context = InstrumentationRegistry.getInstrumentation().targetContext + // Setup tracker + val networkConfiguration = NetworkConfiguration("fake-url", HttpMethod.POST) + + val trackerConfiguration = TrackerConfiguration("decoratorTest") + .sessionContext(false) return Snowplow.createTracker( context, - "namespace", + "namespace" + Math.random(), networkConfiguration, trackerConfiguration, - sessionConfiguration ) } } diff --git a/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/CrossDeviceParameter.kt b/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/CrossDeviceParameter.kt new file mode 100644 index 000000000..36f5773aa --- /dev/null +++ b/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/CrossDeviceParameter.kt @@ -0,0 +1,8 @@ +package com.snowplowanalytics.core.tracker + +enum class CrossDeviceParameter { + SESSION_ID, + SOURCE_ID, + SOURCE_PLATFORM, + USER_ID, +} diff --git a/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/CrossDeviceParameters.kt b/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/CrossDeviceParameters.kt deleted file mode 100644 index cb62f56de..000000000 --- a/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/CrossDeviceParameters.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.snowplowanalytics.core.tracker; - -enum class CrossDeviceParameters { - session_id, - source_id, - source_platform, - user_id, - reason, -} diff --git a/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/TrackerControllerImpl.kt b/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/TrackerControllerImpl.kt index 89103d3a1..ac8bd879a 100644 --- a/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/TrackerControllerImpl.kt +++ b/snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/TrackerControllerImpl.kt @@ -43,7 +43,7 @@ class TrackerControllerImpl // Constructors get() = serviceProvider.getOrMakeGlobalContextsController() val sessionController: SessionControllerImpl get() = serviceProvider.getOrMakeSessionController() - + override val ecommerce: EcommerceControllerImpl get() = serviceProvider.ecommerceController override val session: SessionController? @@ -71,29 +71,38 @@ class TrackerControllerImpl // Constructors return tracker.track(event) } - override fun decorateLink(uri: Uri, parameters: List): Uri { - var spParam: String? = uri.getQueryParameter("_sp") - val values = hashMapOf( - CrossDeviceParameters.session_id to (this.session?.sessionId ?: ""), - CrossDeviceParameters.source_id to this.appId, - CrossDeviceParameters.source_platform to this.devicePlatform.value, - CrossDeviceParameters.user_id to (this.session?.userId ?: "") - ) + override fun decorateLink(uri: Uri, parameters: List): Uri? { + if (this.session?.userId == null) { + return null + } + val spParam: String? = uri.getQueryParameter("_sp") + + val values = hashMapOf( + CrossDeviceParameter.SESSION_ID to (this.session?.sessionId ?: ""), + CrossDeviceParameter.SOURCE_ID to this.appId, + CrossDeviceParameter.SOURCE_PLATFORM to this.devicePlatform.value, + CrossDeviceParameter.USER_ID to (this.subject.userId ?: "") + ) // No `_sp` param, we need to add it in - val spVals = arrayListOf() if (spParam.isNullOrBlank()) { - for (value in CrossDeviceParameters.values()) { - if (value in parameters) { - values[value]?.let { spVals.add(it) } - } else { - spVals.add("") - } + val spVals = listOf( + this.session?.userId, + System.currentTimeMillis() + ) + CrossDeviceParameter.values().map { + if (it in parameters) values[it] else "" + } + + var queryString = spVals.joinToString(".") + while (queryString.last() == '.') { + queryString = queryString.removeSuffix(".") } - return Uri.parse("$uri&_sp=${spVals.joinToString(".")}") + return uri.buildUpon() + .appendQueryParameter("_sp", queryString) + .build() } return Uri.parse("garbage") @@ -107,119 +116,119 @@ class TrackerControllerImpl // Constructors // Getters and Setters override val namespace: String get() = tracker.namespace - + override var appId: String get() = tracker.appId set(appId) { dirtyConfig.appId = appId tracker.appId = appId } - + override var devicePlatform: DevicePlatform get() = tracker.platform set(devicePlatform) { dirtyConfig.devicePlatform = devicePlatform tracker.platform = devicePlatform } - + override var base64encoding: Boolean get() = tracker.base64Encoded set(base64encoding) { dirtyConfig.base64encoding = base64encoding tracker.base64Encoded = base64encoding } - + override var logLevel: LogLevel get() = tracker.logLevel set(logLevel) { dirtyConfig.logLevel = logLevel tracker.logLevel = logLevel } - + override var loggerDelegate: LoggerDelegate? get() = Logger.delegate set(loggerDelegate) { dirtyConfig.loggerDelegate = loggerDelegate Logger.delegate = loggerDelegate } - + override var applicationContext: Boolean get() = tracker.applicationContext set(applicationContext) { dirtyConfig.applicationContext = applicationContext tracker.applicationContext = applicationContext } - + override var platformContext: Boolean get() = tracker.platformContextEnabled set(platformContext) { dirtyConfig.platformContext = platformContext tracker.platformContextEnabled = platformContext } - + override var geoLocationContext: Boolean get() = tracker.geoLocationContext set(geoLocationContext) { dirtyConfig.geoLocationContext = geoLocationContext tracker.geoLocationContext = geoLocationContext } - + override var sessionContext: Boolean get() = tracker.sessionContext set(sessionContext) { dirtyConfig.sessionContext = sessionContext tracker.sessionContext = sessionContext } - + override var deepLinkContext: Boolean get() = tracker.deepLinkContext set(deepLinkContext) { dirtyConfig.deepLinkContext = deepLinkContext tracker.deepLinkContext = deepLinkContext } - + override var screenContext: Boolean get() = tracker.screenContext set(screenContext) { dirtyConfig.screenContext = screenContext tracker.screenContext = screenContext } - + override var screenViewAutotracking: Boolean get() = tracker.screenViewAutotracking set(screenViewAutotracking) { dirtyConfig.screenViewAutotracking = screenViewAutotracking tracker.screenViewAutotracking = screenViewAutotracking } - + override var lifecycleAutotracking: Boolean get() = tracker.lifecycleAutotracking set(lifecycleAutotracking) { dirtyConfig.lifecycleAutotracking = lifecycleAutotracking tracker.lifecycleAutotracking = lifecycleAutotracking } - + override var installAutotracking: Boolean get() = tracker.installAutotracking set(installAutotracking) { dirtyConfig.installAutotracking = installAutotracking tracker.installAutotracking = installAutotracking } - + override var exceptionAutotracking: Boolean get() = tracker.exceptionAutotracking set(exceptionAutotracking) { dirtyConfig.exceptionAutotracking = exceptionAutotracking tracker.exceptionAutotracking = exceptionAutotracking } - + override var diagnosticAutotracking: Boolean get() = tracker.diagnosticAutotracking set(diagnosticAutotracking) { dirtyConfig.diagnosticAutotracking = diagnosticAutotracking tracker.diagnosticAutotracking = diagnosticAutotracking } - + override var userAnonymisation: Boolean get() = tracker.userAnonymisation set(userAnonymisation) { diff --git a/snowplow-tracker/src/main/java/com/snowplowanalytics/snowplow/controller/TrackerController.kt b/snowplow-tracker/src/main/java/com/snowplowanalytics/snowplow/controller/TrackerController.kt index e3d83ead7..bdef71abd 100644 --- a/snowplow-tracker/src/main/java/com/snowplowanalytics/snowplow/controller/TrackerController.kt +++ b/snowplow-tracker/src/main/java/com/snowplowanalytics/snowplow/controller/TrackerController.kt @@ -13,7 +13,7 @@ package com.snowplowanalytics.snowplow.controller import android.net.Uri -import com.snowplowanalytics.core.tracker.CrossDeviceParameters +import com.snowplowanalytics.core.tracker.CrossDeviceParameter import com.snowplowanalytics.core.tracker.TrackerConfigurationInterface import com.snowplowanalytics.snowplow.ecommerce.EcommerceController import com.snowplowanalytics.snowplow.event.Event @@ -121,7 +121,14 @@ interface TrackerController : TrackerConfigurationInterface { /** * Adds user and session information to a URI - * e.g. appSchema://path/to/page -> appSchema://path/to/page?_sp=userId.timestamp.sessionId.appId.platform + * e.g. appSchema://path/to/page -> appSchema://path/to/page?_sp=userId.timestamp.sessionId.appId.platform.domainUserId + * + * @return Optional Uri: + * - null if `sessionContext(false)` is passed to the tracker builder + * - otherwise, decorated Uri */ - fun decorateLink(uri: Uri, parameters: List = CrossDeviceParameters.values().toList()): Uri -} + fun decorateLink( + uri: Uri, + parameters: List = CrossDeviceParameter.values().toList() + ): Uri? +} \ No newline at end of file