diff --git a/build.gradle.kts b/build.gradle.kts index 56aabf3..9f4da93 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,9 @@ * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.2.1/userguide/building_java_projects.html in the Gradle documentation. */ +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent + plugins { // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. id("org.jetbrains.kotlin.jvm") version "1.8.20" @@ -66,4 +69,10 @@ java { tasks.named("test") { // Use JUnit Platform for unit tests. useJUnitPlatform() + // Log useful information when running tests. + testLogging { + exceptionFormat = TestExceptionFormat.FULL + events = setOf(TestLogEvent.SKIPPED, TestLogEvent.PASSED, TestLogEvent.FAILED) + showStandardStreams = true + } } diff --git a/src/main/kotlin/com/featurevisor/sdk/Conditions.kt b/src/main/kotlin/com/featurevisor/sdk/Conditions.kt index 5a0f9a5..5c1b507 100644 --- a/src/main/kotlin/com/featurevisor/sdk/Conditions.kt +++ b/src/main/kotlin/com/featurevisor/sdk/Conditions.kt @@ -22,9 +22,11 @@ object Conditions { Operator.notEquals -> return contextValue.value != conditionValue.value Operator.contains -> return contextValue.value.contains(conditionValue.value) Operator.notContains -> - return !contextValue.value.contains(conditionValue.value) + return !contextValue.value.contains(conditionValue.value) + Operator.startsWith -> - return contextValue.value.startsWith(conditionValue.value) + return contextValue.value.startsWith(conditionValue.value) + Operator.endsWith -> return contextValue.value.endsWith(conditionValue.value) else -> return false } diff --git a/src/main/kotlin/com/featurevisor/sdk/MurmurHash3.kt b/src/main/kotlin/com/featurevisor/sdk/MurmurHash3.kt index 443fac2..cf7c2cf 100644 --- a/src/main/kotlin/com/featurevisor/sdk/MurmurHash3.kt +++ b/src/main/kotlin/com/featurevisor/sdk/MurmurHash3.kt @@ -63,20 +63,20 @@ public class MurmurHash3(private val seed: UInt = 1.toUInt()) { private fun ByteArray.getLittleEndianUInt(index: Int): UInt { return this.getUInt(index) or - (this.getUInt(index + 1) shl 8) or - (this.getUInt(index + 2) shl 16) or - (this.getUInt(index + 3) shl 24) + (this.getUInt(index + 1) shl 8) or + (this.getUInt(index + 2) shl 16) or + (this.getUInt(index + 3) shl 24) } private fun ByteArray.getLittleEndianLong(index: Int): ULong { return this.getULong(index) or - (this.getULong(index + 1) shl 8) or - (this.getULong(index + 2) shl 16) or - (this.getULong(index + 3) shl 24) or - (this.getULong(index + 4) shl 32) or - (this.getULong(index + 5) shl 40) or - (this.getULong(index + 6) shl 48) or - (this.getULong(index + 7) shl 56) + (this.getULong(index + 1) shl 8) or + (this.getULong(index + 2) shl 16) or + (this.getULong(index + 3) shl 24) or + (this.getULong(index + 4) shl 32) or + (this.getULong(index + 5) shl 40) or + (this.getULong(index + 6) shl 48) or + (this.getULong(index + 7) shl 56) } private fun UInt.mix(r: Int, c1: UInt, c2: UInt): UInt { diff --git a/src/main/kotlin/com/featurevisor/types/Types.kt b/src/main/kotlin/com/featurevisor/types/Types.kt index 8707a6a..c641b51 100644 --- a/src/main/kotlin/com/featurevisor/types/Types.kt +++ b/src/main/kotlin/com/featurevisor/types/Types.kt @@ -7,6 +7,7 @@ sealed class AttributeValue { data class IntValue(val value: Int) : AttributeValue() data class DoubleValue(val value: Double) : AttributeValue() data class BooleanValue(val value: Boolean) : AttributeValue() + // @TODO: implement Date object NullValue : AttributeValue() } @@ -17,7 +18,7 @@ data class Attribute( val key: AttributeKey, val type: String, val archived: Boolean?, - val capture: Boolean? + val capture: Boolean?, ) enum class Operator(val value: String) { @@ -59,6 +60,7 @@ sealed class ConditionValue { data class DoubleValue(val value: Double) : ConditionValue() data class BooleanValue(val value: Boolean) : ConditionValue() data class ArrayValue(val values: Array) : ConditionValue() + // @TODO: implement Date object NullValue : ConditionValue() } @@ -66,19 +68,19 @@ sealed class ConditionValue { data class PlainCondition( val attribute: AttributeKey, val operator: Operator, - val value: ConditionValue + val value: ConditionValue, ) data class AndCondition( - val and: Array + val and: Array, ) data class OrCondition( - val or: Array + val or: Array, ) data class NotCondition( - val not: Array + val not: Array, ) sealed class Condition { @@ -95,21 +97,21 @@ typealias SegmentKey = String data class Segment( val archived: Boolean?, val key: SegmentKey, - val conditions: Condition + val conditions: Condition, ) typealias PlainGroupSegment = SegmentKey data class AndGroupSegment( - val and: Array + val and: Array, ) data class OrGroupSegment( - val or: Array + val or: Array, ) data class NotGroupSegment( - val not: Array + val not: Array, ) sealed class GroupSegment { @@ -152,13 +154,13 @@ data class VariableOverride( // one of the below must be present in YAML val conditions: Condition?, - val segments: GroupSegment? + val segments: GroupSegment?, ) data class Variable( val key: VariableKey, val value: VariableValue, - val overrides: Array? + val overrides: Array?, ) data class Variation( @@ -170,13 +172,13 @@ data class Variation( // 0 to 100 (available from parsed YAML, but not in datafile) val weight: Double?, - val variables: Array? + val variables: Array?, ) data class VariableSchema( val key: VariableKey, val type: VariableType, - val defaultValue: VariableValue + val defaultValue: VariableValue, ) typealias FeatureKey = String @@ -190,7 +192,7 @@ data class Force( val enabled: Boolean?, val variation: VariationValue?, - val variables: VariableValues? + val variables: VariableValues?, ) data class Slot( @@ -198,13 +200,13 @@ data class Slot( val feature: FeatureKey?, // 0 to 100 - val percentage: Weight + val percentage: Weight, ) data class Group( val key: String, val description: String, - val slots: Array + val slots: Array, ) typealias BucketKey = String @@ -220,12 +222,12 @@ typealias Percentage = Int data class Range( val start: Percentage, - val end: Percentage + val end: Percentage, ) data class Allocation( val variation: VariationValue, - val range: Range + val range: Range, ) data class Traffic( @@ -237,7 +239,7 @@ data class Traffic( val variation: VariationValue?, val variables: VariableValues?, - val allocation: Array + val allocation: Array, ) typealias PlainBucketBy = String @@ -245,7 +247,7 @@ typealias PlainBucketBy = String typealias AndBucketBy = Array data class OrBucketBy( - val or: Array + val or: Array, ) sealed class BucketBy { @@ -256,7 +258,7 @@ sealed class BucketBy { data class RequiredWithVariation( val key: FeatureKey, - val variation: VariationValue + val variation: VariationValue, ) sealed class Required { @@ -275,7 +277,7 @@ data class Feature( val force: Array?, // if in a Group (mutex), these are available slot ranges - val ranges: Array? + val ranges: Array?, ) data class DatafileContent( @@ -283,13 +285,13 @@ data class DatafileContent( val revision: String, val attributes: Array, val segments: Array, - val features: Array + val features: Array, ) data class OverrideFeature( val enabled: Boolean, val variation: VariationValue?, - val variables: VariableValues? + val variables: VariableValues?, ) typealias StickyFeatures = Map @@ -313,13 +315,13 @@ data class Rule( val enabled: Boolean?, val variation: VariationValue?, - val variables: VariableValues? + val variables: VariableValues?, ) data class Environment( val expose: Boolean?, val rules: Array, - val force: Array? + val force: Array?, ) typealias Environments = Map @@ -340,7 +342,7 @@ data class ParsedFeature( val variablesSchema: Array?, val variations: Array?, - val environments: Environments + val environments: Environments, ) /** @@ -353,23 +355,23 @@ data class FeatureAssertion( val context: Context, val expectedToBeEnabled: Boolean, val expectedVariation: VariationValue?, - val expectedVariables: VariableValues? + val expectedVariables: VariableValues?, ) data class TestFeature( val key: FeatureKey, - val assertions: Array + val assertions: Array, ) data class SegmentAssertion( val description: String?, val context: Context, - val expectedToMatch: Boolean + val expectedToMatch: Boolean, ) data class TestSegment( val key: SegmentKey, - val assertions: Array + val assertions: Array, ) data class Test( @@ -381,9 +383,9 @@ data class Test( val features: Array?, // needed for segment testing - val segments: Array? + val segments: Array?, ) data class Spec( - val tests: Array + val tests: Array, ) diff --git a/src/test/kotlin/com/featurevisor/sdk/BucketTest.kt b/src/test/kotlin/com/featurevisor/sdk/BucketTest.kt index 7b5d10f..4c89378 100644 --- a/src/test/kotlin/com/featurevisor/sdk/BucketTest.kt +++ b/src/test/kotlin/com/featurevisor/sdk/BucketTest.kt @@ -8,14 +8,14 @@ class BucketTest { fun getBucketedNumberReturnsExpectedValues() { val expectedResults = - mapOf( - "foo" to 20602, - "bar" to 89144, - "123.foo" to 3151, - "123.bar" to 9710, - "123.456.foo" to 14432, - "123.456.bar" to 1982 - ) + mapOf( + "foo" to 20602, + "bar" to 89144, + "123.foo" to 3151, + "123.bar" to 9710, + "123.456.foo" to 14432, + "123.456.bar" to 1982 + ) for ((key, value) in expectedResults) { val result = Bucket.getBucketedNumber(key) diff --git a/src/test/kotlin/com/featurevisor/sdk/ConditionsTest.kt b/src/test/kotlin/com/featurevisor/sdk/ConditionsTest.kt index 12e452a..0d50adc 100644 --- a/src/test/kotlin/com/featurevisor/sdk/ConditionsTest.kt +++ b/src/test/kotlin/com/featurevisor/sdk/ConditionsTest.kt @@ -11,56 +11,56 @@ class ConditionsTest { @Test fun testEqualsOperatorForStrings() { val condition = - PlainCondition( - "browser_type", - Operator.equals, - ConditionValue.StringValue("chrome") - ) + PlainCondition( + "browser_type", + Operator.equals, + ConditionValue.StringValue("chrome") + ) // match assertEquals( - true, - Conditions.conditionIsMatched( - condition, - mapOf("browser_type" to AttributeValue.StringValue("chrome")) - ) + true, + Conditions.conditionIsMatched( + condition, + mapOf("browser_type" to AttributeValue.StringValue("chrome")) + ) ) // not match assertEquals( - false, - Conditions.conditionIsMatched( - condition, - mapOf("browser_type" to AttributeValue.StringValue("firefox")) - ) + false, + Conditions.conditionIsMatched( + condition, + mapOf("browser_type" to AttributeValue.StringValue("firefox")) + ) ) } @Test fun testNotEqualsOperatorForStrings() { val condition = - PlainCondition( - "browser_type", - Operator.notEquals, - ConditionValue.StringValue("chrome") - ) + PlainCondition( + "browser_type", + Operator.notEquals, + ConditionValue.StringValue("chrome") + ) // match assertEquals( - true, - Conditions.conditionIsMatched( - condition, - mapOf("browser_type" to AttributeValue.StringValue("firefox")) - ) + true, + Conditions.conditionIsMatched( + condition, + mapOf("browser_type" to AttributeValue.StringValue("firefox")) + ) ) // not match assertEquals( - false, - Conditions.conditionIsMatched( - condition, - mapOf("browser_type" to AttributeValue.StringValue("chrome")) - ) + false, + Conditions.conditionIsMatched( + condition, + mapOf("browser_type" to AttributeValue.StringValue("chrome")) + ) ) } @@ -70,20 +70,20 @@ class ConditionsTest { // match assertEquals( - true, - Conditions.conditionIsMatched( - condition, - mapOf("age" to AttributeValue.IntValue(19)) - ) + true, + Conditions.conditionIsMatched( + condition, + mapOf("age" to AttributeValue.IntValue(19)) + ) ) // not match assertEquals( - false, - Conditions.conditionIsMatched( - condition, - mapOf("age" to AttributeValue.IntValue(17)) - ) + false, + Conditions.conditionIsMatched( + condition, + mapOf("age" to AttributeValue.IntValue(17)) + ) ) } @@ -93,20 +93,20 @@ class ConditionsTest { // match assertEquals( - true, - Conditions.conditionIsMatched( - condition, - mapOf("age" to AttributeValue.IntValue(17)) - ) + true, + Conditions.conditionIsMatched( + condition, + mapOf("age" to AttributeValue.IntValue(17)) + ) ) // not match assertEquals( - false, - Conditions.conditionIsMatched( - condition, - mapOf("age" to AttributeValue.IntValue(19)) - ) + false, + Conditions.conditionIsMatched( + condition, + mapOf("age" to AttributeValue.IntValue(19)) + ) ) } } diff --git a/src/test/kotlin/com/featurevisor/sdk/LibraryTest.kt b/src/test/kotlin/com/featurevisor/sdk/LibraryTest.kt index 6353133..1424f81 100644 --- a/src/test/kotlin/com/featurevisor/sdk/LibraryTest.kt +++ b/src/test/kotlin/com/featurevisor/sdk/LibraryTest.kt @@ -7,7 +7,8 @@ import kotlin.test.Test import kotlin.test.assertTrue class LibraryTest { - @Test fun someLibraryMethodReturnsTrue() { + @Test + fun someLibraryMethodReturnsTrue() { val classUnderTest = Library() assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'") }