Skip to content

Commit

Permalink
add integration tests; fix bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
bgiori committed Jul 6, 2023
1 parent 2a69b76 commit 252b5dc
Show file tree
Hide file tree
Showing 5 changed files with 899 additions and 38 deletions.
3 changes: 3 additions & 0 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ object Versions {
const val serializationRuntime = "1.4.1"
const val npmPublishPlugin = "2.0.2"
const val kotlinLint = "10.2.1"

// Testing
const val ktorVersion = "2.3.1"
}
4 changes: 4 additions & 0 deletions evaluation-core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Versions.ktorVersion

plugins {
kotlin("multiplatform")
kotlin("plugin.serialization") version Versions.serializationPlugin
Expand Down Expand Up @@ -36,6 +38,8 @@ kotlin {
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-cio:$ktorVersion")
}
}
}
Expand Down
52 changes: 14 additions & 38 deletions evaluation-core/src/commonMain/kotlin/EvaluationEngine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import kotlinx.serialization.json.JsonArray

private const val MAX_HASH_VALUE = 4294967295L
private const val MAX_VARIANT_HASH_VALUE = MAX_HASH_VALUE.floorDiv(100)
private const val VERSION = "version"

interface EvaluationEngine {
fun evaluate(
Expand Down Expand Up @@ -59,7 +58,7 @@ class EvaluationEngineImpl(private val log: Logger? = DefaultLogger()) : Evaluat
if (result != null) {
// Merge all metadata into the result
val metadata = mergeMetadata(flag.metadata, segment.metadata, result.metadata)
result = EvaluationVariant(result.key, result.value, metadata)
result = EvaluationVariant(result.key, result.value, result.payload, metadata)
log?.verbose { "Flag evaluation returned result $result on segment $segment." }
break
}
Expand Down Expand Up @@ -104,8 +103,18 @@ class EvaluationEngineImpl(private val log: Logger? = DefaultLogger()) : Evaluat

private fun matchCondition(target: EvaluationTarget, condition: EvaluationCondition): Boolean {
val propValue = target.select(condition.selector)
val op = transformOperator(condition.op, condition.selector)
return match(propValue, op, condition.values)
// We need special matching for null properties and set type prop values
// and operators. All other values are matched as strings, since the
// filter values are always strings.
if (propValue == null) {
return matchNull(condition.op, condition.values)
} else if (isSetOperator(condition.op)) {
val propValueStringList = coerceStringList(propValue) ?: return false
return matchSet(propValueStringList, condition.op, condition.values)
} else {
val propValueString = coerceString(propValue) ?: return false
return matchString(propValueString, condition.op, condition.values)
}
}

private fun getHash(key: String): Long {
Expand Down Expand Up @@ -174,39 +183,6 @@ class EvaluationEngineImpl(private val log: Logger? = DefaultLogger()) : Evaluat
}
}

private fun transformOperator(op: String, selector: List<String>?): String {
var operator = op
if (selector != null && selector.isNotEmpty()) {
// if it's a version field, map the operator into an operator that supports semantic versioning. Nova
// doesn't have to do this, because dash does it while creating a nova query
if (selector[selector.size - 1].contains(VERSION)) {
operator = when (op) {
EvaluationOperator.LESS_THAN -> EvaluationOperator.VERSION_LESS_THAN
EvaluationOperator.LESS_THAN_EQUALS -> EvaluationOperator.VERSION_LESS_THAN_EQUALS
EvaluationOperator.GREATER_THAN -> EvaluationOperator.VERSION_GREATER_THAN
EvaluationOperator.GREATER_THAN_EQUALS -> EvaluationOperator.VERSION_GREATER_THAN_EQUALS
else -> op
}
}
}
return operator
}

private fun match(propValue: Any?, op: String, filterValues: Set<String>): Boolean {
// We need special matching for null properties and set type prop values
// and operators. All other values are matched as strings, since the
// filter values are always strings.
if (propValue == null) {
return matchNull(op, filterValues)
} else if (isSetOperator(op)) {
val propValueStringList = coerceStringList(propValue) ?: return false
return matchSet(propValueStringList, op, filterValues)
} else {
val propValueString = coerceString(propValue) ?: return false
return matchString(propValueString, op, filterValues)
}
}

private fun matchNull(op: String, filterValues: Set<String>): Boolean {
val containsNone = containsNone(filterValues)
return when (op) {
Expand Down Expand Up @@ -266,7 +242,7 @@ class EvaluationEngineImpl(private val log: Logger? = DefaultLogger()) : Evaluat

private fun matchesContains(propValue: String, filterValues: Set<String>): Boolean {
for (filterValue in filterValues) {
if (filterValue.lowercase().contains(propValue.lowercase())) {
if (propValue.lowercase().contains(filterValue.lowercase())) {
return true
}
}
Expand Down
Loading

0 comments on commit 252b5dc

Please sign in to comment.