Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ dependencies {
testImplementation files('libs/java-json.jar')
testImplementation 'com.squareup.assertj:assertj-android:1.2.0'
testImplementation ("io.mockk:mockk:1.13.4")
testImplementation 'org.mockito:mockito-inline:5.2.0'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4'
compileOnly 'androidx.compose.ui:ui'
compileOnly 'androidx.compose.material:material'
Expand Down
12 changes: 12 additions & 0 deletions src/main/kotlin/com/mparticle/kits/RoktKit.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.lang.ref.WeakReference
import java.math.BigDecimal
import com.mparticle.internal.MPUtility
import com.mparticle.internal.MPUtility.AdIdInfo

const val ROKT_ATTRIBUTE_SANDBOX_MODE: String = "sandbox"

Expand Down Expand Up @@ -351,6 +353,15 @@ class RoktKit :
}
}
}

// GAID is only provided if advertising tracking is not limited
applicationContext?.let { context ->
val adIdInfo = MPUtility.getAdIdInfo(context)
if (adIdInfo != null && adIdInfo.advertiser == MPUtility.AdIdInfo.Advertiser.GOOGLE) {
identityAttributes[IDENTITY_TYPE_GAID] = adIdInfo.id
}
}

if (attributes != null) {
attributes.putAll(identityAttributes)
return attributes
Expand Down Expand Up @@ -420,6 +431,7 @@ class RoktKit :
const val ROKT_ACCOUNT_ID = "accountId"
const val HASHED_EMAIL_USER_IDENTITY_TYPE = "hashedEmailUserIdentityType"
const val MPID = "mpid"
const val IDENTITY_TYPE_GAID = "gaid"
const val NO_ROKT_ACCOUNT_ID = "No Rokt account ID provided, can't initialize kit."
const val NO_APP_VERSION_FOUND = "No App version found, can't initialize kit."
}
Expand Down
208 changes: 208 additions & 0 deletions src/test/kotlin/com/mparticle/kits/RoktKitTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ import org.json.JSONArray
import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.mockStatic
import org.mockito.Mockito.`when`
import org.mockito.ArgumentMatchers.any as mockitoAny
import java.lang.ref.WeakReference
import java.lang.reflect.Field
import java.lang.reflect.Method
Expand Down Expand Up @@ -1148,6 +1152,210 @@ class RoktKitTests {
assertEquals("allowed_value", result["allowed_key"])
}

@Test
fun testAddIdentityAttributes_adds_GAID_when_available() {
// Test that GAID is added when MPUtility.getAdIdInfo returns valid Google advertiser

// Set applicationContext using reflection (required for GAID lookup)
val contextField = RoktKit::class.java.getDeclaredField("applicationContext")
contextField.isAccessible = true
contextField.set(roktKit, context)

mockStatic(com.mparticle.internal.MPUtility::class.java).use { mockedMPUtility ->
// Create a mock AdIdInfo and set its fields using reflection
val mockAdIdInfo = mock(com.mparticle.internal.MPUtility.AdIdInfo::class.java)

// Set the id field
val idField = com.mparticle.internal.MPUtility.AdIdInfo::class.java.getDeclaredField("id")
idField.isAccessible = true
idField.set(mockAdIdInfo, "test-gaid-12345")

// Set the advertiser field
val advertiserField = com.mparticle.internal.MPUtility.AdIdInfo::class.java.getDeclaredField("advertiser")
advertiserField.isAccessible = true
advertiserField.set(mockAdIdInfo, com.mparticle.internal.MPUtility.AdIdInfo.Advertiser.GOOGLE)

mockedMPUtility.`when`<com.mparticle.internal.MPUtility.AdIdInfo> {
com.mparticle.internal.MPUtility.getAdIdInfo(mockitoAny(android.content.Context::class.java))
}.thenReturn(mockAdIdInfo)

val mockFilterUser = mock(FilteredMParticleUser::class.java)
val userIdentities = HashMap<IdentityType, String>()
userIdentities[IdentityType.Email] = "[email protected]"
Mockito.`when`(mockFilterUser.userIdentities).thenReturn(userIdentities)

val attributes: MutableMap<String, String> = mutableMapOf("existing_key" to "existing_value")

val method: Method = RoktKit::class.java.getDeclaredMethod(
"addIdentityAttributes",
MutableMap::class.java,
FilteredMParticleUser::class.java,
)
method.isAccessible = true

// Act
val result = method.invoke(roktKit, attributes, mockFilterUser) as Map<String, String>

// Assert
assertTrue("GAID should be present in result", result.containsKey("gaid"))
assertEquals("test-gaid-12345", result["gaid"])

// Verify user identities are still added
assertEquals("[email protected]", result["email"])

// Verify existing attributes are preserved
assertEquals("existing_value", result["existing_key"])
}
}

@Test
fun testAddIdentityAttributes_GAID_not_added_when_null() {
// Test that GAID is not added when MPUtility.getAdIdInfo returns null

// Set applicationContext using reflection (required for GAID lookup)
val contextField = RoktKit::class.java.getDeclaredField("applicationContext")
contextField.isAccessible = true
contextField.set(roktKit, context)

mockStatic(com.mparticle.internal.MPUtility::class.java).use { mockedMPUtility ->
// Mock getAdIdInfo to return null (ad tracking not available or not authorized)
mockedMPUtility.`when`<com.mparticle.internal.MPUtility.AdIdInfo> {
com.mparticle.internal.MPUtility.getAdIdInfo(mockitoAny(android.content.Context::class.java))
}.thenReturn(null)

val mockFilterUser = mock(FilteredMParticleUser::class.java)
val userIdentities = HashMap<IdentityType, String>()
userIdentities[IdentityType.Email] = "[email protected]"
Mockito.`when`(mockFilterUser.userIdentities).thenReturn(userIdentities)

val attributes: MutableMap<String, String> = mutableMapOf()

val method: Method = RoktKit::class.java.getDeclaredMethod(
"addIdentityAttributes",
MutableMap::class.java,
FilteredMParticleUser::class.java,
)
method.isAccessible = true

// Act
val result = method.invoke(roktKit, attributes, mockFilterUser) as Map<String, String>

// Assert - GAID should NOT be present
assertFalse("GAID should not be present when getAdIdInfo returns null", result.containsKey("gaid"))

// Other identities should still be added
assertTrue(result.containsKey("email"))
assertEquals("[email protected]", result["email"])
}
}

@Test
fun testAddIdentityAttributes_GAID_coexists_with_google_user_identity() {
// Test that GAID (gaid key) coexists with Google user identity (google key)

// Set applicationContext using reflection (required for GAID lookup)
val contextField = RoktKit::class.java.getDeclaredField("applicationContext")
contextField.isAccessible = true
contextField.set(roktKit, context)

mockStatic(com.mparticle.internal.MPUtility::class.java).use { mockedMPUtility ->
// Create a mock AdIdInfo and set its fields using reflection
val mockAdIdInfo = mock(com.mparticle.internal.MPUtility.AdIdInfo::class.java)

val idField = com.mparticle.internal.MPUtility.AdIdInfo::class.java.getDeclaredField("id")
idField.isAccessible = true
idField.set(mockAdIdInfo, "device-gaid-value")

val advertiserField = com.mparticle.internal.MPUtility.AdIdInfo::class.java.getDeclaredField("advertiser")
advertiserField.isAccessible = true
advertiserField.set(mockAdIdInfo, com.mparticle.internal.MPUtility.AdIdInfo.Advertiser.GOOGLE)

mockedMPUtility.`when`<com.mparticle.internal.MPUtility.AdIdInfo> {
com.mparticle.internal.MPUtility.getAdIdInfo(mockitoAny(android.content.Context::class.java))
}.thenReturn(mockAdIdInfo)

val mockFilterUser = mock(FilteredMParticleUser::class.java)
val userIdentities = HashMap<IdentityType, String>()
userIdentities[IdentityType.Email] = "[email protected]"
userIdentities[IdentityType.Google] = "user-google-identity"
Mockito.`when`(mockFilterUser.userIdentities).thenReturn(userIdentities)

val attributes: MutableMap<String, String> = mutableMapOf()

val method: Method = RoktKit::class.java.getDeclaredMethod(
"addIdentityAttributes",
MutableMap::class.java,
FilteredMParticleUser::class.java,
)
method.isAccessible = true

// Act
val result = method.invoke(roktKit, attributes, mockFilterUser) as Map<String, String>

// Assert - Both should be present with different keys and values
assertTrue(result.containsKey("google"))
assertEquals("user-google-identity", result["google"])

assertTrue(result.containsKey("gaid"))
assertEquals("device-gaid-value", result["gaid"])

// Verify they don't interfere with each other
assertNotEquals(result["google"], result["gaid"])
}
}

@Test
fun testAddIdentityAttributes_GAID_not_added_for_non_google_advertiser() {
// Test that GAID is NOT added when advertiser is not Google (e.g., Amazon Fire)
// Ensures we only add GAID for actual Google Advertising IDs

// Set applicationContext using reflection (required for GAID lookup)
val contextField = RoktKit::class.java.getDeclaredField("applicationContext")
contextField.isAccessible = true
contextField.set(roktKit, context)

mockStatic(com.mparticle.internal.MPUtility::class.java).use { mockedMPUtility ->
// Create a mock AdIdInfo with Amazon advertiser (not Google)
val mockAdIdInfo = mock(com.mparticle.internal.MPUtility.AdIdInfo::class.java)

val idField = com.mparticle.internal.MPUtility.AdIdInfo::class.java.getDeclaredField("id")
idField.isAccessible = true
idField.set(mockAdIdInfo, "amazon-ad-id-12345")

val advertiserField = com.mparticle.internal.MPUtility.AdIdInfo::class.java.getDeclaredField("advertiser")
advertiserField.isAccessible = true
advertiserField.set(mockAdIdInfo, com.mparticle.internal.MPUtility.AdIdInfo.Advertiser.AMAZON)

mockedMPUtility.`when`<com.mparticle.internal.MPUtility.AdIdInfo> {
com.mparticle.internal.MPUtility.getAdIdInfo(mockitoAny(android.content.Context::class.java))
}.thenReturn(mockAdIdInfo)

val mockFilterUser = mock(FilteredMParticleUser::class.java)
val userIdentities = HashMap<IdentityType, String>()
userIdentities[IdentityType.Email] = "[email protected]"
Mockito.`when`(mockFilterUser.userIdentities).thenReturn(userIdentities)

val attributes: MutableMap<String, String> = mutableMapOf()

val method: Method = RoktKit::class.java.getDeclaredMethod(
"addIdentityAttributes",
MutableMap::class.java,
FilteredMParticleUser::class.java,
)
method.isAccessible = true

// Act
val result = method.invoke(roktKit, attributes, mockFilterUser) as Map<String, String>

// Assert - GAID should NOT be added for Amazon advertiser
assertFalse("GAID should not be added for non-Google advertiser", result.containsKey("gaid"))

// Other identities should still be added
assertTrue(result.containsKey("email"))
assertEquals("[email protected]", result["email"])
}
}

internal inner class TestCoreCallbacks : CoreCallbacks {
override fun isBackgrounded(): Boolean = false
override fun getUserBucket(): Int = 0
Expand Down
Loading