Valere
5 months ago
8 changed files with 245 additions and 9 deletions
@ -0,0 +1,59 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2024 New Vector Ltd |
||||||
|
* |
||||||
|
* 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 io.element.android.appnav.loggedin |
||||||
|
|
||||||
|
import im.vector.app.features.analytics.plan.CryptoSessionStateChange |
||||||
|
import im.vector.app.features.analytics.plan.UserProperties |
||||||
|
import io.element.android.libraries.matrix.api.encryption.RecoveryState |
||||||
|
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus |
||||||
|
|
||||||
|
fun SessionVerifiedStatus.toAnalyticsUserPropertyValue(): UserProperties.VerificationState? { |
||||||
|
return when (this) { |
||||||
|
// we don't need to report transient states |
||||||
|
SessionVerifiedStatus.Unknown -> null |
||||||
|
SessionVerifiedStatus.NotVerified -> UserProperties.VerificationState.NotVerified |
||||||
|
SessionVerifiedStatus.Verified -> UserProperties.VerificationState.Verified |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun RecoveryState.toAnalyticsUserPropertyValue(): UserProperties.RecoveryState? { |
||||||
|
return when (this) { |
||||||
|
RecoveryState.ENABLED -> UserProperties.RecoveryState.Enabled |
||||||
|
RecoveryState.DISABLED -> UserProperties.RecoveryState.Disabled |
||||||
|
RecoveryState.INCOMPLETE -> UserProperties.RecoveryState.Incomplete |
||||||
|
// we don't need to report transient states |
||||||
|
else -> null |
||||||
|
} |
||||||
|
} |
||||||
|
fun SessionVerifiedStatus.toAnalyticsStateChangeValue(): CryptoSessionStateChange.VerificationState? { |
||||||
|
return when (this) { |
||||||
|
// we don't need to report transient states |
||||||
|
SessionVerifiedStatus.Unknown -> null |
||||||
|
SessionVerifiedStatus.NotVerified -> CryptoSessionStateChange.VerificationState.NotVerified |
||||||
|
SessionVerifiedStatus.Verified -> CryptoSessionStateChange.VerificationState.Verified |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun RecoveryState.toAnalyticsStateChangeValue(): CryptoSessionStateChange.RecoveryState? { |
||||||
|
return when (this) { |
||||||
|
RecoveryState.ENABLED -> CryptoSessionStateChange.RecoveryState.Enabled |
||||||
|
RecoveryState.DISABLED -> CryptoSessionStateChange.RecoveryState.Disabled |
||||||
|
RecoveryState.INCOMPLETE -> CryptoSessionStateChange.RecoveryState.Incomplete |
||||||
|
// we don't need to report transient states |
||||||
|
else -> null |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,116 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2024 New Vector Ltd |
||||||
|
* |
||||||
|
* 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 io.element.android.services.analyticsproviders.posthog |
||||||
|
|
||||||
|
import com.google.common.truth.Truth.assertThat |
||||||
|
import com.posthog.PostHogInterface |
||||||
|
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent |
||||||
|
import im.vector.app.features.analytics.plan.UserProperties |
||||||
|
import io.element.android.tests.testutils.WarmUpRule |
||||||
|
import io.mockk.every |
||||||
|
import io.mockk.just |
||||||
|
import io.mockk.mockk |
||||||
|
import io.mockk.runs |
||||||
|
import io.mockk.slot |
||||||
|
import io.mockk.verify |
||||||
|
import kotlinx.coroutines.test.runTest |
||||||
|
import org.junit.Rule |
||||||
|
import org.junit.Test |
||||||
|
|
||||||
|
class PosthogAnalyticsProviderTest { |
||||||
|
@get:Rule |
||||||
|
val warmUpRule = WarmUpRule() |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `Posthog - Test user properties`() = runTest { |
||||||
|
val mockPosthog = mockk<PostHogInterface>().also { |
||||||
|
every { it.optIn() } just runs |
||||||
|
every { it.capture(any(), any(), any(), any(), any(), any()) } just runs |
||||||
|
} |
||||||
|
val mockPosthogFactory = mockk<PostHogFactory>() |
||||||
|
every { mockPosthogFactory.createPosthog() } returns mockPosthog |
||||||
|
|
||||||
|
val analyticsProvider = PosthogAnalyticsProvider(mockPosthogFactory) |
||||||
|
analyticsProvider.init() |
||||||
|
|
||||||
|
val testUserProperties = UserProperties( |
||||||
|
verificationState = UserProperties.VerificationState.Verified, |
||||||
|
recoveryState = UserProperties.RecoveryState.Incomplete, |
||||||
|
) |
||||||
|
analyticsProvider.updateUserProperties(testUserProperties) |
||||||
|
|
||||||
|
// report mock event |
||||||
|
val mockEvent = mockk<VectorAnalyticsEvent>().also { |
||||||
|
every { |
||||||
|
it.getProperties() |
||||||
|
} returns emptyMap() |
||||||
|
every { it.getName() } returns "MockEventName" |
||||||
|
} |
||||||
|
analyticsProvider.capture(mockEvent) |
||||||
|
val userPropertiesSlot = slot<Map<String, Any>>() |
||||||
|
|
||||||
|
verify { mockPosthog.capture(event = "MockEventName", any(), any(), userProperties = capture(userPropertiesSlot)) } |
||||||
|
|
||||||
|
assertThat(userPropertiesSlot.captured).isNotNull() |
||||||
|
assertThat(userPropertiesSlot.captured["verificationState"] as String).isEqualTo(testUserProperties.verificationState?.name) |
||||||
|
assertThat(userPropertiesSlot.captured["recoveryState"] as String).isEqualTo(testUserProperties.recoveryState?.name) |
||||||
|
|
||||||
|
// Should only be reported once when the next event is sent |
||||||
|
// Try another capture to check |
||||||
|
analyticsProvider.capture(mockEvent) |
||||||
|
verify { mockPosthog.capture(any(), any(), any(), userProperties = null) } |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `Posthog - Test accumulate user properties until next capture call`() = runTest { |
||||||
|
val mockPosthog = mockk<PostHogInterface>().also { |
||||||
|
every { it.optIn() } just runs |
||||||
|
every { it.capture(any(), any(), any(), any(), any(), any()) } just runs |
||||||
|
} |
||||||
|
val mockPosthogFactory = mockk<PostHogFactory>() |
||||||
|
every { mockPosthogFactory.createPosthog() } returns mockPosthog |
||||||
|
|
||||||
|
val analyticsProvider = PosthogAnalyticsProvider(mockPosthogFactory) |
||||||
|
analyticsProvider.init() |
||||||
|
|
||||||
|
val testUserProperties = UserProperties( |
||||||
|
verificationState = UserProperties.VerificationState.NotVerified, |
||||||
|
) |
||||||
|
analyticsProvider.updateUserProperties(testUserProperties) |
||||||
|
|
||||||
|
// Update again |
||||||
|
val testUserPropertiesUpdate = UserProperties( |
||||||
|
verificationState = UserProperties.VerificationState.Verified, |
||||||
|
) |
||||||
|
analyticsProvider.updateUserProperties(testUserPropertiesUpdate) |
||||||
|
|
||||||
|
// report mock event |
||||||
|
val mockEvent = mockk<VectorAnalyticsEvent>().also { |
||||||
|
every { |
||||||
|
it.getProperties() |
||||||
|
} returns emptyMap() |
||||||
|
every { it.getName() } returns "MockEventName" |
||||||
|
} |
||||||
|
analyticsProvider.capture(mockEvent) |
||||||
|
val userPropertiesSlot = slot<Map<String, Any>>() |
||||||
|
|
||||||
|
verify { mockPosthog.capture(event = "MockEventName", any(), any(), userProperties = capture(userPropertiesSlot)) } |
||||||
|
|
||||||
|
assertThat(userPropertiesSlot.captured).isNotNull() |
||||||
|
assertThat(userPropertiesSlot.captured["verificationState"] as String).isEqualTo(testUserPropertiesUpdate.verificationState?.name) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue