All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonTest.EvaluationEngineImplTest.kt Maven / Gradle / Ivy

package com.amplitude.experiment.evaluation

import kotlin.test.Test
import kotlin.test.assertEquals

private const val AMPLITUDE_ID_BUCKETING_KEY = "amplitude_id"
private const val USER_ID_BUCKETING_KEY = "user_id"

class EvaluationEngineImplTest {

    // use the actual implementation to test the non-interface exposed methods directly
    private var evaluationEngine = EvaluationEngineImpl()

    @Test
    fun testEmptyFlagConfigs() {
        run {
            assertEquals(
                mapOf(),
                evaluationEngine.evaluate(arrayListOf(), SkylabUser())
            )
        }
    }

    @Test
    fun testEvalExclusionsAndInclusions_1() {
        // exclude only: Variant "A" has "id-1" in exclusion list and the flag is 100% rolled out to A
        val flagConfig = FlagConfig(
            "flag-1", true, "user_id", "abcdef", "false",
            arrayListOf(Variant("A"), Variant("B"), Variant("C")),
            mapOf("A" to setOf("id-1")), mapOf(),
            SegmentTargetingConfig(
                "default-segment", listOf(), 100,
                mapOf("A" to 1), USER_ID_BUCKETING_KEY
            ),
            arrayListOf()
        )
        assertEquals(
            flagConfig.defaultValue,
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(userId = "id-1"),
            ).variant.key
        )
    }

    @Test
    fun testEvalExclusionsAndInclusions_2() {
        // include and exclude both: Variant "A" has "id-1" under inclusion and exclusion, user can't be served A
        val flagConfig = FlagConfig(
            "flag-1", true, "user_id", "abcdef", "false",
            arrayListOf(Variant("A"), Variant("B"), Variant("C")),
            mapOf("A" to setOf("id-1")), mapOf("A" to setOf("id-1")),
            SegmentTargetingConfig(
                "default-segment", listOf(), 100,
                mapOf("A" to 1), USER_ID_BUCKETING_KEY
            ),
            arrayListOf()
        )
        assertEquals(
            flagConfig.defaultValue,
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(userId = "id-1")
            ).variant.key
        )
    }

    @Test
    fun testEvalExclusionsAndInclusions_3() {
        // include and exclude both: Variant "A" has "id-1" under inclusion and exclusion, user can't be served A.
        // experiment is rolled out to 100% of users and rolloutWeight is {"B":1}. User should be served B
        val flagConfig = FlagConfig(
            "flag-1", true, "user_id", "abcdef", "false",
            arrayListOf(Variant("A"), Variant("B"), Variant("C")),
            mapOf("A" to setOf("id-1")), mapOf("A" to setOf("id-1")),
            SegmentTargetingConfig(
                "default-segment", listOf(), 100,
                mapOf("B" to 1), USER_ID_BUCKETING_KEY
            ),
            arrayListOf()
        )
        assertEquals(
            "B",
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(userId = "id-1")
            ).variant.key
        )
    }

    @Test
    fun testEvalExclusionsAndInclusions_4() {
        run {

            // include only: Variant "A" has "id-1" and "id-2" in inclusion lists, null exclusionss
            val flagConfig = FlagConfig(
                "flag-1", true, "user_id", "abcdef", "false",
                arrayListOf(Variant("A"), Variant("B"), Variant("C")), null,
                mapOf("A" to setOf("id-1", "id-2")), EmptySegmentTargetingConfig(), arrayListOf()
            )
            assertEquals(
                "A",
                evaluationEngine.evaluateFlag(
                    flagConfig,
                    SkylabUser(userId = "id-2")
                ).variant.key
            )
        }
        run {

            // include only: Variant "A" has "id-1" and "id-2" in inclusion lists
            val flagConfig = FlagConfig(
                "flag-1", true, "user_id", "abcdef", "false",
                arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
                mapOf("A" to setOf("id-1", "id-2")), EmptySegmentTargetingConfig(), arrayListOf()
            )
            assertEquals(
                "A",
                evaluationEngine.evaluateFlag(
                    flagConfig,
                    SkylabUser(userId = "id-2")
                ).variant.key
            )
        }
    }

    @Test
    fun testEvalExclusionsAndInclusions_5() {
        // exclude only: Variant "A" has "id-2" in exclusion list and the flag is 100% rolled out to A and B evenly
        val flagConfig = FlagConfig(
            "flag-1", true, "user_id", "abcdef", "false",
            arrayListOf(Variant("A"), Variant("B"), Variant("C")),
            mapOf("A" to setOf("id-2")), mapOf(),
            SegmentTargetingConfig(
                "default-segment", listOf(), 100,
                mapOf("A" to 1, "B" to 1), USER_ID_BUCKETING_KEY
            ),
            arrayListOf()
        )
        assertEquals(
            flagConfig.defaultValue,
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(userId = "id-2")
            ).variant.key
        )
    }

    @Test
    fun testGetVariantBasedOnRollout() {
        run {

            // 100% rolled out; B is 1,000,000 times more likely than A
            val variants: List = arrayListOf(Variant("A"), Variant("B"), Variant("C"))
            val percentage = 100
            val rolloutWeightsPerVariantKey: Map =
                mapOf("A" to 1, "B" to 1000000)
            val allocations: List = arrayListOf(
                fromCentilePercentage(percentage, rolloutWeightsPerVariantKey)
            )
            val flagVariant = evaluationEngine.getVariantBasedOnRollout(
                variants, allocations, "false",
                setOf(), "bucketingSalt", "id-1"
            ).key
            assertEquals("B", flagVariant)
        }
        run {

            // 50% rolled out; B is approximately equal to A
            val variants: List =
                arrayListOf(Variant("A"), Variant("B"), Variant("C"))
            val percentage = 100
            val rolloutWeightsPerVariantKey: Map =
                mapOf("A" to 1, "B" to 1)
            val allocations: List = arrayListOf(
                fromCentilePercentage(percentage, rolloutWeightsPerVariantKey)
            )
            var countA = 0
            var countB = 0
            for (i in 0..99) {
                val flagVariant: Variant = evaluationEngine.getVariantBasedOnRollout(
                    variants, allocations, "false",
                    setOf(), "bucketingSalt", "id-$i"
                )
                if ("A" == flagVariant.key) {
                    countA++
                }
                if ("B" == flagVariant.key) {
                    countB++
                }
            }
            assertEquals(52, countA.toLong())
            assertEquals(48, countB.toLong())
        }
    }

//    @Test
//    fun testGetVariantMaxHash() {
//        val spy: EvaluationEngineImpl = Mockito.spy(evaluationEngine)
//        Mockito.doReturn(EvaluationEngineImpl.MAX_HASH_VALUE).`when`(spy).getHash(Mockito.anyString())
//        run {
//
//            // 100% rolled out; B is 1,000,000 times more likely than A
//            val variants: List = arrayListOf(Variant("A"), Variant("B"), Variant("C"))
//            val percentage = 100
//            val rolloutWeightsPerVariantKey: Map = mapOf("B" to 1)
//            val allocations: List = Lists
//                .newArrayList(Allocation.fromCentilePercentage(percentage, rolloutWeightsPerVariantKey))
//            val flagVariant: String = spy.getVariantBasedOnRollout(
//                variants, allocations, "false", setOf(),
//                "bucketingSalt", "id-1"
//            ).key
//            assertEquals("B", flagVariant)
//        }
//    }

    @Test
    fun testGetVariantBasedOnRolloutMultipleAllocations() {
        run {

            // Two rollouts of 25% each; B is 1,000,000 times more likely than A in the first rollout, A is 1,000,000
            // times more likely than B in the second rollout
            // Should be roughly equal
            val variants: List = arrayListOf(Variant("A"), Variant("B"), Variant("C"))
            val rolloutWeightsPerVariantKey: Map =
                mapOf("A" to 1, "B" to 1000000)
            val rolloutWeightsPerVariantKey2: Map =
                mapOf("A" to 1000000, "B" to 1)
            val allocations: List = arrayListOf(
                Allocation(2500, rolloutWeightsPerVariantKey),
                Allocation(2500, rolloutWeightsPerVariantKey2)
            )
            var countA = 0
            var countB = 0
            var countDefault = 0
            for (i in 0..999) {
                val flagVariant: Variant = evaluationEngine.getVariantBasedOnRollout(
                    variants, allocations, "false",
                    setOf(), "bucketingSalt", "id-$i"
                )
                if ("A" == flagVariant.key) {
                    countA++
                }
                if ("B" == flagVariant.key) {
                    countB++
                }
                if ("false" == flagVariant.key) {
                    countDefault++
                }
            }
            assertEquals(226, countA.toLong())
            assertEquals(272, countB.toLong())
            assertEquals(502, countDefault.toLong())
        }
        run {

            // Two rollouts of 25% each; B is 1,000,000 times more likely than A in the first rollout
            // Should have the same distribution as the first allocation in the previous test
            val variants: List =
                arrayListOf(Variant("A"), Variant("B"), Variant("C"))
            val rolloutWeightsPerVariantKey: Map =
                mapOf("A" to 1, "B" to 1000000)
            val allocations: List =
                arrayListOf(Allocation(2500, rolloutWeightsPerVariantKey))
            var countA = 0
            var countB = 0
            var countDefault = 0
            for (i in 0..999) {
                val flagVariant: Variant = evaluationEngine.getVariantBasedOnRollout(
                    variants, allocations, "false",
                    setOf(), "bucketingSalt", "id-$i"
                )
                if ("A" == flagVariant.key) {
                    countA++
                }
                if ("B" == flagVariant.key) {
                    countB++
                }
                if ("false" == flagVariant.key) {
                    countDefault++
                }
            }
            assertEquals(0, countA.toLong())
            assertEquals(272, countB.toLong())
            assertEquals(728, countDefault.toLong())
        }
    }

    @Test
    fun testGetHash() {
        // Source: https://cimi.io/murmurhash3js-revisited/ (x86 32bit)
        run {
            val testString = "My hovercraft is full of eels."
            assertEquals(2953494853L, evaluationEngine.getHash(testString))
        }
        run {
            val testString = "My 🚀 is full of 🦎."
            assertEquals(1818098979L, evaluationEngine.getHash(testString))
        }
        run {
            val testString = "吉 星 高 照"
            assertEquals(3435142074L, evaluationEngine.getHash(testString))
        }
    }

    @Test
    fun testEvalCustomTargetingSegmentConfig() {
        run {

            // everyone in the ALL_USERS segment gets A
            // everyone in custom-target segment 1 gets B
            // everyone in custom-target segment 2 gets C
            val flagConfig = FlagConfig(
                "flag-1", true, "user_id", "abcdef", "false",
                arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
                mapOf(),
                SegmentTargetingConfig(
                    "default-segment", listOf(), 100,
                    mapOf("A" to 1), USER_ID_BUCKETING_KEY
                ),
                arrayListOf(
                    SegmentTargetingConfig(
                        "segment-1",
                        listOf(
                            UserPropertyFilter("country", Operator.IS, setOf("Canada"))
                        ),
                        100, mapOf("B" to 1), USER_ID_BUCKETING_KEY
                    ),
                    SegmentTargetingConfig(
                        "segment-2",
                        listOf(
                            UserPropertyFilter(
                                "platform", Operator.IS,
                                setOf("android")
                            )
                        ),
                        100, mapOf("C" to 1), USER_ID_BUCKETING_KEY
                    )
                ),
            )

            // no user properties, so fails to match custom segment filters
            assertEquals(
                "A",
                evaluationEngine.evaluateFlag(
                    flagConfig,
                    SkylabUser(userId = "id-1")
                ).variant.key
            )

            // country = "Canada", gets segment 1 config which returns "B"
            assertEquals(
                "B",
                evaluationEngine.evaluateFlag(
                    flagConfig,
                    SkylabUser(userId = "id-1", country = "Canada")
                ).variant.key
            )

            // platform = "android", gets segment 2 config which returns "C"
            assertEquals(
                "C",
                evaluationEngine.evaluateFlag(
                    flagConfig,
                    SkylabUser(userId = "id-1", platform = "android")
                ).variant.key
            )

            // mismatch country and matches platform (2nd segment)
            assertEquals(
                "C",
                evaluationEngine.evaluateFlag(
                    flagConfig, SkylabUser(userId = "id-1", country = "Italy", platform = "android")
                ).variant.key
            )

            // matches both segments, first one is returned
            assertEquals(
                "B",
                evaluationEngine.evaluateFlag(
                    flagConfig, SkylabUser(userId = "id-1", country = "Canada", platform = "android")
                ).variant.key
            )

            // has properties, but doesn't match both segments, ALL_USERS_SEGMENT info is returned
            assertEquals(
                "A",
                evaluationEngine.evaluateFlag(
                    flagConfig, SkylabUser(userId = "id-1", country = "France", platform = "iPhone")
                ).variant.key
            )
        }
    }

    @Test
    fun testEvaluate() {
        // everyone gets A
        val flagConfig1 = FlagConfig(
            "test-evaluate-flag-1", true, "user_id", "abcdef", "default-value",
            arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
            mapOf(),
            SegmentTargetingConfig(
                "default-segment", listOf(), 100,
                mapOf("A" to 1), USER_ID_BUCKETING_KEY
            ),
            arrayListOf(),
        )

        // flag is disabled, everyone gets default value
        val flagConfig2 = FlagConfig(
            "test-evaluate-flag-2", false, "user_id", "abcdef", "default-value",
            arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
            mapOf(),
            SegmentTargetingConfig(
                "default-segment", listOf(), 100,
                mapOf(), USER_ID_BUCKETING_KEY
            ),
            arrayListOf(),
        )
        val evaluationResult: Map = evaluationEngine.evaluate(
            arrayListOf(flagConfig1, flagConfig2),
            SkylabUser(userId = "test-evaluate-user")
        )
        val expectedEvaluationResult: Map = mapOf(
            "test-evaluate-flag-1" to
                FlagResult(Variant("A"), "fully-rolled-out-variant", false),
            "test-evaluate-flag-2" to
                FlagResult(Variant("default-value"), "flag-disabled", true)
        )
        assertEquals(expectedEvaluationResult, evaluationResult)
    }

    @Test
    fun testFullyRolledOutVariant() {
        run {

            // variant has been fully rolled out and everyone should get A
            val flagConfig1 = FlagConfig(
                "test-evaluate-flag-1", true, "user_id", "abcdef", "default-value",
                arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
                mapOf(),
                SegmentTargetingConfig(
                    "default-segment", listOf(), 100,
                    mapOf("A" to 1), USER_ID_BUCKETING_KEY
                ),
                arrayListOf(),
            )

            // no context passed. It should return fully rolled out variant instead of the default value
            assertEquals(
                "A",
                evaluationEngine.evaluateFlag(flagConfig1, SkylabUser()).variant.key
            )
        }
        run {

            // there's only one variant A; flag has been 100% rolled out and no roll-out weight is assigned, so assume
            // even distribution
            val flagConfig = FlagConfig(
                "test-fully-rolled-out-variant", true, "user_id",
                "abcdef", "default-value",
                arrayListOf(Variant("A")), mapOf(), mapOf(),
                SegmentTargetingConfig(
                    "default-segment", listOf(), 100, mapOf(),
                    USER_ID_BUCKETING_KEY
                ),
                arrayListOf()
            )
            // no context passed. It should return fully rolled out variant instead of the default value
            assertEquals(
                "A",
                evaluationEngine.evaluateFlag(flagConfig, SkylabUser()).variant.key
            )
        }
    }

    @Test
    fun testNullMultiPropFilterInSegment() {
        // variant has been fully rolled out and everyone should get A
        val flagConfig1 = FlagConfig(
            "test-evaluate-flag-1", true, "user_id", "abcdef", "default-value",
            arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
            mapOf(),
            SegmentTargetingConfig("default-segment", null, 0, mapOf("A" to 1), USER_ID_BUCKETING_KEY),
            arrayListOf(
                SegmentTargetingConfig(
                    "segment-1", null, 100, mapOf("B" to 1),
                    USER_ID_BUCKETING_KEY
                )
            )
        )
        assertEquals(
            "B",
            evaluationEngine.evaluateFlag(
                flagConfig1,
                SkylabUser(userId = "does-not-matter")
            ).variant.key
        )
    }

    @Test
    fun testCustomUserPropertyFilter() {
        // everyone in the ALL_USERS segment gets A
        // everyone in custom-target segment 1 gets B
        // everyone in custom-target segment 2 gets C
        val flagConfig = FlagConfig(
            "flag-1", true, "user_id", "abcdef", "false",
            arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
            mapOf(),
            SegmentTargetingConfig(
                "default-segment", listOf(), 100,
                mapOf("A" to 1), USER_ID_BUCKETING_KEY
            ),
            arrayListOf(
                SegmentTargetingConfig(
                    "segment-1",
                    listOf(
                        UserPropertyFilter(
                            "gp:custom-prop-1", Operator.IS,
                            setOf("prop-1-value-1")
                        )
                    ),
                    100, mapOf("B" to 1), USER_ID_BUCKETING_KEY
                ),
                SegmentTargetingConfig(
                    "segment-2",
                    listOf(
                        UserPropertyFilter(
                            "gp:custom-prop-2", Operator.IS,
                            setOf("prop-2-value-1")
                        )
                    ),
                    100, mapOf("C" to 1), USER_ID_BUCKETING_KEY
                )
            ),
        )

        // no user properties, so fails to match custom segment filters and falls back to the general segment
        assertEquals(
            "A",
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(userId = "id-1")
            ).variant.key
        )

        // custom-prop-1 = "prop-1-value-1", gets segment 1 config which returns "B"
        assertEquals(
            "B",
            evaluationEngine.evaluateFlag(
                flagConfig, SkylabUser(userId = "id-1", userProperties = mapOf("custom-prop-1" to "prop-1-value-1"))
            ).variant.key
        )

        // custom-prop-2 = "prop-2-value-1", gets segment 2 config which returns "C"
        assertEquals(
            "C",
            evaluationEngine.evaluateFlag(
                flagConfig, SkylabUser(userId = "id-1", userProperties = mapOf("custom-prop-2" to "prop-2-value-1"))
            ).variant.key
        )

        // mismatch custom-prop-1 and matches custom-prop-2 (2nd segment)
        assertEquals(
            "C",
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(
                    userId = "id-1",
                    userProperties = mapOf(
                        "custom-prop-1" to "prop-1-value-2",
                        "custom-prop-2" to "prop-2-value-1",
                    )
                )
            ).variant.key
        )

        // matches both segments, first one is returned
        assertEquals(
            "B",
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(
                    userId = "id-1",
                    userProperties = mapOf(
                        "custom-prop-1" to "prop-1-value-1",
                        "custom-prop-2" to "prop-2-value-1",
                    )
                )
            ).variant.key
        )

        // has properties, but doesn't match both segments, ALL_USERS_SEGMENT info is returned
        assertEquals(
            "A",
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(
                    userId = "id-1",
                    userProperties = mapOf(
                        "custom-prop-1" to "prop-1-value-not-match",
                        "custom-prop-2" to "prop-2-value-not-match",
                    )
                )
            ).variant.key
        )
    }

    @Test
    fun testNullUser() {
        // should return default value which is null in this case
        val flagConfig1 = FlagConfig(
            "test-evaluate-flag-1", true, "user_id", "abcdef", null,
            arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
            mapOf(),
            SegmentTargetingConfig(
                "default-segment", null, 0,
                mapOf("A" to 1, "B" to 1, "C" to 1), USER_ID_BUCKETING_KEY
            ),
            arrayListOf(),
        )
        assertEquals(null, evaluationEngine.evaluateFlag(flagConfig1, null).variant.key)
    }

    @Test
    fun testEmptyStringUserId() {
        val flagConfig1 = FlagConfig(
            "test-evaluate-flag-1", true, "user_id", "abcdef", null,
            arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
            mapOf(),
            SegmentTargetingConfig(
                "default-segment", null, 0,
                mapOf("A" to 1, "B" to 1, "C" to 1), USER_ID_BUCKETING_KEY
            ),
            arrayListOf(),
        )
        assertEquals(
            null,
            evaluationEngine.evaluateFlag(
                flagConfig1,
                SkylabUser(userId = "")
            ).variant.key
        )
    }

    @Test
    fun testEmptySkylabUser() {
        val flagConfig1 = FlagConfig(
            "test-evaluate-flag-1", true, "user_id", "abcdef", null,
            arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
            mapOf(),
            SegmentTargetingConfig(
                "default-segment", null, 0,
                mapOf("A" to 1, "B" to 1, "C" to 1), USER_ID_BUCKETING_KEY
            ),
            arrayListOf(),
        )
        assertEquals(
            null,
            evaluationEngine.evaluateFlag(flagConfig1, SkylabUser()).variant.key
        )
    }

    @Test
    fun testCustomTargetSegment_DefaultVariant() {
        // everyone in segment-1 gets default-variant (null)
        // everyone in segment-2 gets default-variant (null)
        // everyone else gets A
        val flagConfig = FlagConfig(
            "flag-1", true, "user_id", "abcdef", null,
            arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
            mapOf(),
            SegmentTargetingConfig(
                "default-segment", listOf(), 100,
                mapOf("A" to 1), USER_ID_BUCKETING_KEY
            ),
            arrayListOf(
                SegmentTargetingConfig(
                    "segment-1",
                    listOf(
                        UserPropertyFilter(
                            "gp:custom-prop-1", Operator.IS,
                            setOf("prop-1-value-1")
                        )
                    ),
                    0, mapOf(), USER_ID_BUCKETING_KEY
                ),
                SegmentTargetingConfig(
                    "segment-1",
                    listOf(
                        UserPropertyFilter(
                            "gp:custom-prop-2", Operator.IS,
                            setOf("prop-2-value-1")
                        )
                    ),
                    0, mapOf(), USER_ID_BUCKETING_KEY
                )
            ),
        )

        // custom-prop-1 = "prop-1-value-1", gets segment 1 config which returns "B"
        assertEquals(
            null,
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(
                    userId = "id-1",
                    userProperties = mapOf("custom-prop-1" to "prop-1-value-1")
                )
            ).variant.key
        )

        // custom-prop-2 = "prop-2-value-1", gets segment 2 config which returns "C"
        assertEquals(
            null,
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(
                    userId = "id-1",
                    userProperties = mapOf("custom-prop-2" to "prop-2-value-1")
                )
            ).variant.key
        )

        // no user properties, so fails to match custom segment filters and falls back to the general segment
        assertEquals(
            "A",
            evaluationEngine.evaluateFlag(
                flagConfig,
                SkylabUser(userId = "id-1")
            ).variant.key
        )
    }

    @Test
    fun testDefaultVariantNull() {
        // flag is disabled, everyone gets default value
        val flagConfig = FlagConfig(
            "test-default-variant-null", false, "user_id", "abcdef", null,
            arrayListOf(Variant("A"), Variant("B"), Variant("C")), mapOf(),
            mapOf(),
            SegmentTargetingConfig(
                "default-segment", listOf(), 100,
                mapOf(), USER_ID_BUCKETING_KEY
            ),
            arrayListOf(),
        )
        val evaluationResult: Map = evaluationEngine.evaluate(
            arrayListOf(flagConfig),
            SkylabUser(userId = "user-id")
        )
        val expectedEvaluationResult: Map = mapOf(
            "test-default-variant-null" to
                FlagResult(Variant(null), "flag-disabled", true)
        )
        assertEquals(expectedEvaluationResult, evaluationResult)
    }

//    @Test
//    fun testPayloads() {
//        // everyone gets A
//        val payload = JSONObject()
//        payload.put("bool", true)
//        val flagConfig = FlagConfig(
//            "test-payloads", "Flag Name", 0, true, "user_id", "abcdef", false,
//            "globalHoldbackSalt", 0, MutualExclusionConfig(), "default-value",
//            arrayListOf(Variant("A", payload), Variant("B"), Variant("C")), mapOf(),
//            mapOf(), SegmentTargetingConfig(
//                "default-segment", listOf(), 100,
//                mapOf("A" to 1), USER_ID_BUCKETING_KEY
//            ),
//            arrayListOf(), null
//        )
//        val evaluationResult: Map = evaluationEngine.evaluate(
//            arrayListOf(flagConfig),
//            SkylabUser(userId = "user-id")
//        )
//        val expectedPayload = JSONObject()
//        expectedPayload.put("bool", true)
//        val expectedEvaluationResult: Map = mapOf(
//            "test-payloads",
//            FlagResult(Variant("A", expectedPayload), "fully-rolled-out-variant", false)
//        )
//        assertEquals(expectedEvaluationResult, evaluationResult)
//    }
//
//    @Test
//    fun testGlobalHoldback() {
//        val spy: EvaluationEngineImpl = Mockito.spy(evaluationEngine)
//        Mockito.doReturn(java.lang.Math.floor(.499 * EvaluationEngineImpl.MAX_HASH_VALUE) as Long).`when`(spy)
//            .getHash(Mockito.startsWith("gh/"))
//        val flagConfig = FlagConfig(
//            "test-global-holdback", "Flag Name", 0, true, AMPLITUDE_ID_BUCKETING_KEY,
//            "bucketingSalt", false, "globalHoldbackSalt", 5000, null, "defaultValue",
//            arrayListOf(Variant("A"), Variant("B")), mapOf(), mapOf(), null,
//            null, null
//        )
//        val evaluationResult: Map = spy.evaluate(
//            arrayListOf(flagConfig),
//            SkylabUser(amplitudeId = 1)
//        )
//        val expectedEvaluationResult: Map = mapOf(
//            "test-global-holdback",
//            FlagResult(Variant("defaultValue"), "global-holdback", true)
//        )
//        assertEquals(expectedEvaluationResult, evaluationResult)
//    }
//
//    @Test
//    fun testMutualExclusionGroupInBounds() {
//        run {
//            val spy: EvaluationEngineImpl = Mockito.spy(evaluationEngine)
//            Mockito.doReturn(java.lang.Math.floor(.201 * EvaluationEngineImpl.MAX_HASH_VALUE) as Long).`when`(spy)
//                .getHash(Mockito.startsWith("me/"))
//            val flagConfig = FlagConfig(
//                "test-mutual-exclusion", "Flag Name", 0, true,
//                AMPLITUDE_ID_BUCKETING_KEY, "bucketingSalt", false, "globalHoldbackSalt", 0,
//                MutualExclusionConfig("mutualExclusionGroupSalt", 2000, 3000), "defaultValue",
//                arrayListOf(Variant("A"), Variant("B")), mapOf(), mapOf(),
//                SegmentTargetingConfig(
//                    "default-segment", listOf(), 100,
//                    mapOf("A" to 1), AMPLITUDE_ID_BUCKETING_KEY
//                ),
//                null, null
//            )
//            val evaluationResult: Map = spy.evaluate(
//                arrayListOf(flagConfig),
//                SkylabUser(amplitudeId = 1)
//            )
//            val expectedEvaluationResult: Map = mapOf(
//                "test-mutual-exclusion",
//                FlagResult(Variant("A"), "fully-rolled-out-variant", false)
//            )
//            assertEquals(expectedEvaluationResult, evaluationResult)
//        }
//        run {
//            val spy: EvaluationEngineImpl = Mockito.spy(evaluationEngine)
//            Mockito.doReturn(java.lang.Math.floor(.499 * EvaluationEngineImpl.MAX_HASH_VALUE) as Long)
//                .`when`(spy)
//                .getHash(Mockito.startsWith("me/"))
//            val flagConfig = FlagConfig(
//                "test-mutual-exclusion", "Flag Name", 0, true,
//                AMPLITUDE_ID_BUCKETING_KEY, "bucketingSalt", false, "globalHoldbackSalt", 0,
//                MutualExclusionConfig("mutualExclusionGroupSalt", 2000, 3000), "defaultValue",
//                arrayListOf(Variant("A"), Variant("B")), mapOf(), mapOf(),
//                SegmentTargetingConfig(
//                    "default-segment", listOf(), 100,
//                    mapOf("A" to 1), AMPLITUDE_ID_BUCKETING_KEY
//                ),
//                null, null
//            )
//            val evaluationResult: Map = spy.evaluate(
//                arrayListOf(flagConfig),
//                SkylabUser(amplitudeId = 1)
//            )
//            val expectedEvaluationResult: Map = mapOf(
//                "test-mutual-exclusion",
//                FlagResult(Variant("A"), "fully-rolled-out-variant", false)
//            )
//            assertEquals(expectedEvaluationResult, evaluationResult)
//        }
//    }
//
//    @Test
//    fun testMutualExclusionGroupOutOfBounds() {
//        run {
//            val spy: EvaluationEngineImpl = Mockito.spy(evaluationEngine)
//            Mockito.doReturn(java.lang.Math.floor(.199 * EvaluationEngineImpl.MAX_HASH_VALUE) as Long).`when`(spy)
//                .getHash(Mockito.startsWith("me/"))
//            val flagConfig = FlagConfig(
//                "test-mutual-exclusion", "Flag Name", 0, true,
//                AMPLITUDE_ID_BUCKETING_KEY, "bucketingSalt", false, "globalHoldbackSalt", 0,
//                MutualExclusionConfig("mutualExclusionGroupSalt", 2000, 3000), "defaultValue",
//                arrayListOf(Variant("A"), Variant("B")), mapOf(), mapOf(),
//                SegmentTargetingConfig(
//                    "default-segment", listOf(), 100,
//                    mapOf("A" to 1), AMPLITUDE_ID_BUCKETING_KEY
//                ),
//                null, null
//            )
//            val evaluationResult: Map = spy.evaluate(
//                arrayListOf(flagConfig),
//                SkylabUser(amplitudeId = 1)
//            )
//            val expectedEvaluationResult: Map = mapOf(
//                "test-mutual-exclusion",
//                FlagResult(Variant("defaultValue"), "mutual-exclusion-group", true)
//            )
//            assertEquals(expectedEvaluationResult, evaluationResult)
//        }
//        run {
//            val spy: EvaluationEngineImpl = Mockito.spy(evaluationEngine)
//            Mockito.doReturn(java.lang.Math.floor(.501 * EvaluationEngineImpl.MAX_HASH_VALUE) as Long)
//                .`when`(spy)
//                .getHash(Mockito.startsWith("me/"))
//            val flagConfig = FlagConfig(
//                "test-mutual-exclusion", "Flag Name", 0, true,
//                AMPLITUDE_ID_BUCKETING_KEY, "bucketingSalt", false, "globalHoldbackSalt", 0,
//                MutualExclusionConfig("mutualExclusionGroupSalt", 2000, 3000), "defaultValue",
//                arrayListOf(Variant("A"), Variant("B")), mapOf(), mapOf(),
//                SegmentTargetingConfig(
//                    "default-segment", listOf(), 100,
//                    mapOf("A" to 1), AMPLITUDE_ID_BUCKETING_KEY
//                ),
//                null, null
//            )
//            val evaluationResult: Map = spy.evaluate(
//                arrayListOf(flagConfig),
//                SkylabUser(amplitudeId = 1)
//            )
//            val expectedEvaluationResult: Map = mapOf(
//                "test-mutual-exclusion",
//                FlagResult(Variant("defaultValue"), "mutual-exclusion-group", true)
//            )
//            assertEquals(expectedEvaluationResult, evaluationResult)
//        }
//    }

//    @Test
//    fun testCustomBucketingKey() {
//        run {
//            val flagConfig = FlagConfig(
//                "test-custom-bucketing-key", "Flag Name", 0, true, "gp:custom",
//                "bucketingSalt", true, "globalHoldbackSalt", 0, MutualExclusionConfig(), "defaultValue",
//                arrayListOf(Variant("A"), Variant("B")), mapOf(), mapOf(),
//                SegmentTargetingConfig(
//                    "default-segment", listOf(), 1,
//                    mapOf("A" to 1), null
//                ),
//                null, null
//            )
//            val user: SkylabUser = SkylabUser.builder().setAmplitudeId(1).setUserProperty("custom", "value").build()
//            val spy: EvaluationEngineImpl = Mockito.spy(evaluationEngine)
//            Mockito.doReturn(0L).`when`(spy).getHash(Mockito.matches(flagConfig.bucketingSalt.toString() + "/value"))
//            val evaluationResult: Map =
//                spy.evaluate(arrayListOf(flagConfig), user)
//            val expectedEvaluationResult: Map = mapOf(
//                "test-custom-bucketing-key",
//                FlagResult(Variant("A"), "default-segment", false)
//            )
//            assertEquals(expectedEvaluationResult, evaluationResult)
//        }
//    }
}

internal fun SegmentTargetingConfig(
    segmentName: String,
    segmentFilters: List?,
    percentage: Int,
    rolloutWeights: Map?,
    bucketingKey: String
): SegmentTargetingConfig {
    return SegmentTargetingConfig(
        segmentName, segmentFilters ?: listOf(),
        listOf(fromCentilePercentage(percentage, rolloutWeights)), bucketingKey
    )
}

internal fun EmptySegmentTargetingConfig(): SegmentTargetingConfig {
    return SegmentTargetingConfig("", listOf(), listOf(), "")
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy