From fabd6e72de43363f5c53a93ec90a94be9f43538f Mon Sep 17 00:00:00 2001 From: Mansi Pandya Date: Mon, 17 Nov 2025 16:00:48 -0500 Subject: [PATCH 1/3] fix: reorder attribute merge to apply user attributes first --- src/main/kotlin/com/mparticle/kits/RoktKit.kt | 4 +- .../kotlin/com/mparticle/kits/RoktKitTests.kt | 59 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/mparticle/kits/RoktKit.kt b/src/main/kotlin/com/mparticle/kits/RoktKit.kt index 234d9dd..26c78a2 100644 --- a/src/main/kotlin/com/mparticle/kits/RoktKit.kt +++ b/src/main/kotlin/com/mparticle/kits/RoktKit.kt @@ -212,7 +212,7 @@ class RoktKit : filterUser: FilteredMParticleUser?, attributes: Map, ): Map { - val finalAttributes = filterAttributes(attributes, configuration) + val finalAttributes = mutableMapOf() filterUser?.userAttributes?.let { userAttrs -> for ((key, value) in userAttrs) { @@ -222,6 +222,8 @@ class RoktKit : } } + finalAttributes.putAll(filterAttributes(attributes, configuration)) + filterUser?.id?.toString()?.let { mpid -> finalAttributes[MPID] = mpid } ?: Logger.warning("RoktKit: No user ID available for placement") diff --git a/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt b/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt index 0b5eec8..af10b60 100644 --- a/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt +++ b/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt @@ -326,6 +326,65 @@ class RoktKitTests { unmockkObject(Rokt) } + + @Test + fun test_prepareFinalAttributes_handlesSameKeysInAttributesAndUserAttributes() { + val mockFilterUser = mock(FilteredMParticleUser::class.java) + Mockito.`when`(mockFilterUser.userIdentities).thenReturn(HashMap()) + + // Include a null value and a non-null non-string value to verify toString() behavior + val userAttributes = HashMap() + userAttributes["attr_non_null_string"] = "value" + userAttributes["attr_null"] = null + userAttributes["attr_non_string"] = 123 + userAttributes["user_key"] = "1231545" + Mockito.`when`(mockFilterUser.userAttributes).thenReturn(userAttributes) + // Set up the configuration with our test filters + val jsonObject = JSONObject() + try { + val filteredKey: String = KitUtils.hashForFiltering("ShouldFilter").toString() + val filteredKey2: String = KitUtils.hashForFiltering("ShouldFilter_key_2").toString() + jsonObject.put(filteredKey, 0) + jsonObject.put(filteredKey2, 1) + } catch (e: Exception) { + println("Exception occurred: ${e.message}") + } + val json = JSONObject() + json.put("ua", jsonObject) + + + roktKit.configuration = MockKitConfiguration.createKitConfiguration(JSONObject().put("hs", json)) + val method: Method = RoktKit::class.java.getDeclaredMethod( + "prepareFinalAttributes", + FilteredMParticleUser::class.java, + Map::class.java, + ) + method.isAccessible = true + + val inputAttributes: Map = mapOf( + "key1" to "value1", + "key2" to "value2", + "key3" to "value3", + "user_key" to "2223333", + ) + val result = method.invoke(roktKit, mockFilterUser, inputAttributes) as Map<*, *> + assertEquals(7, result.size) + + assertTrue(result.containsKey("user_key")) + //It should always use the value from attributes + assertEquals("2223333", result["user_key"]) + assertTrue(result.containsKey("key1")) + assertTrue(result.containsKey("key2")) + assertTrue(result.containsKey("key3")) + assertTrue(result.containsKey("attr_non_null_string")) + assertEquals("value", result["attr_non_null_string"]) + + assertFalse(result.containsKey("attr_null")) + + assertTrue(result.containsKey("attr_non_string")) + assertEquals("123", result["attr_non_string"]) + } + private inner class TestKitManager : KitManagerImpl(context, null, TestCoreCallbacks(), mock(MParticleOptions::class.java)) { var attributes = HashMap() From eeebcf94233cb8325864259325f48caf0811bf4b Mon Sep 17 00:00:00 2001 From: Mansi Pandya Date: Mon, 17 Nov 2025 17:32:45 -0500 Subject: [PATCH 2/3] update test case as per review comments --- src/test/kotlin/com/mparticle/kits/RoktKitTests.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt b/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt index af10b60..9af2750 100644 --- a/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt +++ b/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt @@ -366,6 +366,7 @@ class RoktKitTests { "key2" to "value2", "key3" to "value3", "user_key" to "2223333", + "ShouldFilter" to "testData" ) val result = method.invoke(roktKit, mockFilterUser, inputAttributes) as Map<*, *> assertEquals(7, result.size) @@ -373,16 +374,18 @@ class RoktKitTests { assertTrue(result.containsKey("user_key")) //It should always use the value from attributes assertEquals("2223333", result["user_key"]) + assertEquals("123", result["attr_non_string"]) + assertEquals("value", result["attr_non_null_string"]) + + assertFalse(result.containsKey("ShouldFilter")) + assertFalse(result.containsKey("ShouldFilter_key_2")) + assertTrue(result.containsKey("key1")) assertTrue(result.containsKey("key2")) assertTrue(result.containsKey("key3")) assertTrue(result.containsKey("attr_non_null_string")) - assertEquals("value", result["attr_non_null_string"]) - assertFalse(result.containsKey("attr_null")) - assertTrue(result.containsKey("attr_non_string")) - assertEquals("123", result["attr_non_string"]) } private inner class TestKitManager : From dc0eb56f03d06d91aed9e65e254a855c3196456e Mon Sep 17 00:00:00 2001 From: Mansi Pandya Date: Tue, 18 Nov 2025 11:40:43 -0500 Subject: [PATCH 3/3] update test cases based on code review feedback --- .../kotlin/com/mparticle/kits/RoktKitTests.kt | 64 +++++++++++-------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt b/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt index 9af2750..5dd583e 100644 --- a/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt +++ b/src/test/kotlin/com/mparticle/kits/RoktKitTests.kt @@ -329,10 +329,24 @@ class RoktKitTests { @Test fun test_prepareFinalAttributes_handlesSameKeysInAttributesAndUserAttributes() { + // Arrange + mockkObject(Rokt) + val capturedAttributesSlot = slot>() + every { + Rokt.execute( + any(), + capture(capturedAttributesSlot), + any(), + null, + null, + null, + ) + } just runs + val mockFilterUser = mock(FilteredMParticleUser::class.java) Mockito.`when`(mockFilterUser.userIdentities).thenReturn(HashMap()) + Mockito.`when`(mockFilterUser.id).thenReturn(12345L) - // Include a null value and a non-null non-string value to verify toString() behavior val userAttributes = HashMap() userAttributes["attr_non_null_string"] = "value" userAttributes["attr_null"] = null @@ -351,16 +365,7 @@ class RoktKitTests { } val json = JSONObject() json.put("ua", jsonObject) - - roktKit.configuration = MockKitConfiguration.createKitConfiguration(JSONObject().put("hs", json)) - val method: Method = RoktKit::class.java.getDeclaredMethod( - "prepareFinalAttributes", - FilteredMParticleUser::class.java, - Map::class.java, - ) - method.isAccessible = true - val inputAttributes: Map = mapOf( "key1" to "value1", "key2" to "value2", @@ -368,24 +373,33 @@ class RoktKitTests { "user_key" to "2223333", "ShouldFilter" to "testData" ) - val result = method.invoke(roktKit, mockFilterUser, inputAttributes) as Map<*, *> - assertEquals(7, result.size) + // Act + roktKit.execute( + viewName = "test", + attributes = inputAttributes, + mpRoktEventCallback = null, + placeHolders = null, + fontTypefaces = null, + filterUser = mockFilterUser, + mpRoktConfig = null, + ) - assertTrue(result.containsKey("user_key")) - //It should always use the value from attributes - assertEquals("2223333", result["user_key"]) - assertEquals("123", result["attr_non_string"]) - assertEquals("value", result["attr_non_null_string"]) + // Assert + val capturedAttributes = capturedAttributesSlot.captured - assertFalse(result.containsKey("ShouldFilter")) - assertFalse(result.containsKey("ShouldFilter_key_2")) - assertTrue(result.containsKey("key1")) - assertTrue(result.containsKey("key2")) - assertTrue(result.containsKey("key3")) - assertTrue(result.containsKey("attr_non_null_string")) - assertFalse(result.containsKey("attr_null")) - assertTrue(result.containsKey("attr_non_string")) + assertEquals(7, capturedAttributes.size) + assertEquals("value", capturedAttributes["attr_non_null_string"]) + assertEquals("123", capturedAttributes["attr_non_string"]) + assertEquals("2223333", capturedAttributes["user_key"]) + assertEquals("value1", capturedAttributes["key1"]) + assertEquals("value2", capturedAttributes["key2"]) + assertEquals("value3", capturedAttributes["key3"]) + assertEquals("12345", capturedAttributes["mpid"]) + + assertFalse(capturedAttributes.containsKey("ShouldFilter")) + assertFalse(capturedAttributes.containsKey("ShouldFilter_key_2")) + unmockkObject(Rokt) } private inner class TestKitManager :