Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
209 changes: 209 additions & 0 deletions src/test/kotlin/com/mparticle/kits/RoktKitTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.mparticle.WrapperSdkVersion
import com.mparticle.identity.IdentityApi
import com.mparticle.internal.CoreCallbacks
import com.mparticle.internal.CoreCallbacks.KitListener
import com.mparticle.internal.MPUtility
import com.mparticle.kits.mocks.MockKitConfiguration
import com.rokt.roktsdk.FulfillmentAttributes
import com.rokt.roktsdk.Rokt
Expand All @@ -36,11 +37,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 +1153,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(MPUtility::class.java).use { mockedMPUtility ->
// Create a mock AdIdInfo and set its fields using reflection
val mockAdIdInfo = mock(MPUtility.AdIdInfo::class.java)

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

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

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

val mockFilterUser = mock(FilteredMParticleUser::class.java)
val userIdentities = HashMap<IdentityType, String>()
userIdentities[IdentityType.Email] = "test@example.com"
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("test@example.com", 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(MPUtility::class.java).use { mockedMPUtility ->
// Mock getAdIdInfo to return null (ad tracking not available or not authorized)
mockedMPUtility.`when`<MPUtility.AdIdInfo> {
MPUtility.getAdIdInfo(mockitoAny(Context::class.java))
}.thenReturn(null)

val mockFilterUser = mock(FilteredMParticleUser::class.java)
val userIdentities = HashMap<IdentityType, String>()
userIdentities[IdentityType.Email] = "test@example.com"
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("test@example.com", 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(MPUtility::class.java).use { mockedMPUtility ->
// Create a mock AdIdInfo and set its fields using reflection
val mockAdIdInfo = mock(MPUtility.AdIdInfo::class.java)

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

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

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

val mockFilterUser = mock(FilteredMParticleUser::class.java)
val userIdentities = HashMap<IdentityType, String>()
userIdentities[IdentityType.Email] = "test@example.com"
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(MPUtility::class.java).use { mockedMPUtility ->
// Create a mock AdIdInfo with Amazon advertiser (not Google)
val mockAdIdInfo = mock(MPUtility.AdIdInfo::class.java)

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

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

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

val mockFilterUser = mock(FilteredMParticleUser::class.java)
val userIdentities = HashMap<IdentityType, String>()
userIdentities[IdentityType.Email] = "test@example.com"
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("test@example.com", result["email"])
}
}

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