Skip to content

Failure result subtypes #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
May 4, 2022
Merged
3 changes: 1 addition & 2 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,21 @@ object Modules {
const val core = ":core"
const val operationsApi = ":operations-api"
const val utils = ":utils"
const val kotlinTest = "test"
}

object Versions {
const val kotlin = "1.6.20"
const val axion = "1.13.6"
const val detekt = "1.19.0"
const val nexus = "1.0.0"
const val kotlinxSerialization = "1.3.2"
const val kotest = "5.0.2"
const val crashkios = "0.4.0"
const val kotlinxDatetime = "0.3.2"
}

object Libs {
object KotlinX {
const val serializationJson = "org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.kotlinxSerialization}"
const val datetime = "org.jetbrains.kotlinx:kotlinx-datetime:${Versions.kotlinxDatetime}"
}

Expand Down
3 changes: 1 addition & 2 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ kotlin {

val commonTest by getting {
dependencies {
implementation(Libs.KotlinX.serializationJson)
implementation(kotlin("test"))
implementation(kotlin(Modules.kotlinTest))
implementation(Libs.Kotest.assertionsCore)
implementation(Libs.Kotest.frameworkEngine)
implementation(Libs.Kotest.frameworkDataset)
Expand Down
6 changes: 3 additions & 3 deletions core/src/commonMain/kotlin/CommonJsonLogicEngine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ internal class CommonJsonLogicEngine(private val evaluator: LogicEvaluator) : Js
it.isNotEmpty()
}?.let {
safeEvaluate(expression, data)
} ?: JsonLogicResult.Failure("JsonLogic expression mustn't be empty.")
} ?: JsonLogicResult.EmptyExpressionFailure

private fun safeEvaluate(expression: Map<String, Any?>, data: Any?) = runCatching {
evaluator.evaluateLogic(expression, data)
}.fold(
onSuccess = ::toJsonLogicResult,
onFailure = { JsonLogicResult.Failure(it.message) }
onFailure = { JsonLogicResult.MissingOperationFailure }
)

private fun toJsonLogicResult(evaluatedValue: Any?) = evaluatedValue?.let { notNullResult ->
JsonLogicResult.Success(notNullResult)
} ?: JsonLogicResult.Failure("Evaluated expression has returned null.")
} ?: JsonLogicResult.NullResultFailure
}
4 changes: 3 additions & 1 deletion core/src/commonMain/kotlin/JsonLogicResult.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
sealed class JsonLogicResult {
data class Success(val value: Any) : JsonLogicResult()
data class Failure(val message: String? = null) : JsonLogicResult()
object NullResultFailure : JsonLogicResult()
object EmptyExpressionFailure : JsonLogicResult()
object MissingOperationFailure : JsonLogicResult()
}
9 changes: 9 additions & 0 deletions core/src/commonTest/kotlin/AssertionUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import io.kotest.matchers.shouldBe

infix fun JsonLogicResult.valueShouldBe(other: JsonLogicResult) {
if(this is JsonLogicResult.Success && other is JsonLogicResult.Success) {
value shouldBe other.value
} else {
this shouldBe other
}
}
21 changes: 17 additions & 4 deletions core/src/commonTest/kotlin/CommonJsonLogicEngineTest.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeTypeOf

class CommonJsonLogicEngineTest : BehaviorSpec({
val logicEngine = JsonLogicEngine.Builder().build()

given("An expression with unknown operation") {
val logicExpression = mapOf("unknown" to listOf(1, 2))

`when`("on evaluation") {
val result = logicEngine.evaluate(logicExpression, null)

then("returns missing operation failure result") {
result shouldBe JsonLogicResult.MissingOperationFailure
}
}
}

given("An empty logic expression") {
val logicExpression = emptyMap<String, Any?>()

`when`("on evaluation") {
val result = logicEngine.evaluate(logicExpression, null)

then("returns failure result") {
result.shouldBeTypeOf<JsonLogicResult.Failure>()
then("returns empty expression failure result") {
result shouldBe JsonLogicResult.EmptyExpressionFailure
}
}
}
Expand All @@ -22,8 +35,8 @@ class CommonJsonLogicEngineTest : BehaviorSpec({
`when`("on evaluation") {
val result = logicEngine.evaluate(logicExpression, null)

then("returns failure result") {
result.shouldBeTypeOf<JsonLogicResult.Failure>()
then("returns null failure result") {
result shouldBe JsonLogicResult.NullResultFailure
}
}
}
Expand Down
50 changes: 0 additions & 50 deletions core/src/commonTest/kotlin/LogicOperationTests.kt

This file was deleted.

20 changes: 5 additions & 15 deletions core/src/commonTest/kotlin/TestInput.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
sealed class TestInput(
open val expression: Map<String, Any?>,
open val data: Any?,
) {
data class Successful(
override val expression: Map<String, Any?>,
override val data: Any? = emptyMap<String, Any>(),
val resultValue: Any?
) : TestInput(expression, data)

data class Unsuccessful(
override val expression: Map<String, Any?>,
override val data: Any? = emptyMap<String, Any>()
) : TestInput(expression, data)
}
data class TestInput(
val expression: Map<String, Any?>,
val data: Any? = emptyMap<String, Any>(),
val result: JsonLogicResult
)
188 changes: 97 additions & 91 deletions core/src/commonTest/kotlin/operations/InTest.kt
Original file line number Diff line number Diff line change
@@ -1,101 +1,107 @@
package operations

import TestInput.Successful
import JsonLogicEngine
import JsonLogicResult
import TestInput
import io.kotest.core.spec.style.FunSpec
import testWithSuccessResultData
import io.kotest.datatest.withData
import valueShouldBe

class InTest : FunSpec({
val logicEngine = JsonLogicEngine.Builder().build()

context("JsonLogic evaluation with In operation") {
testWithSuccessResultData(
logicEngine,
listOf(
Successful(
expression = mapOf(
"in" to listOf(
mapOf("var" to "filling"),
listOf("apple", "cherry")
)
),
data = mapOf("filling" to "apple"),
resultValue = true
),
Successful(
expression = mapOf("in" to listOf("Bart", listOf("Bart", "Homer", "Lisa", "Marge", "Maggie"))),
resultValue = true
),
Successful(
expression = mapOf("in" to listOf("Milhouse", listOf("Bart", "Homer", "Lisa", "Marge", "Maggie"))),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf("Spring", "Springfield")),
resultValue = true
),
Successful(
expression = mapOf("in" to listOf("i", "team")),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf("t", listOf("team"))),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf("1", 133)),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf(1, "133")),
resultValue = true
),
Successful(
expression = mapOf("in" to listOf(1, 133)),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf("t", true)),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf(true, "true gold")),
resultValue = true
),
Successful(
expression = mapOf("in" to listOf(null, "banana")),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf("n", null)),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf(null, listOf(null, null))),
resultValue = true
),
Successful(
expression = mapOf("in" to listOf(null, listOf(listOf(null), "banana"))),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf(null, listOf("", ""))),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf("t", "true")),
resultValue = true
),
Successful(
expression = mapOf("in" to listOf("j", "apple", "juice")),
resultValue = false
),
Successful(
expression = mapOf("in" to listOf("Spring", "Springfield")),
resultValue = true
),
)
withData(
nameFn = { input -> "Should evaluated ${input.expression} with given ${input.data} result in ${input.result}" },
ts = listOf(
TestInput(
expression = mapOf(
"in" to listOf(
mapOf("var" to "filling"),
listOf("apple", "cherry")
)
),
data = mapOf("filling" to "apple"),
result = JsonLogicResult.Success(true)
),
TestInput(
expression = mapOf("in" to listOf("Bart", listOf("Bart", "Homer", "Lisa", "Marge", "Maggie"))),
result = JsonLogicResult.Success(true)
),
TestInput(
expression = mapOf("in" to listOf("Milhouse", listOf("Bart", "Homer", "Lisa", "Marge", "Maggie"))),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf("Spring", "Springfield")),
result = JsonLogicResult.Success(true)
),
TestInput(
expression = mapOf("in" to listOf("i", "team")),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf("t", listOf("team"))),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf("1", 133)),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf(1, "133")),
result = JsonLogicResult.Success(true)
),
TestInput(
expression = mapOf("in" to listOf(1, 133)),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf("t", true)),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf(true, "true gold")),
result = JsonLogicResult.Success(true)
),
TestInput(
expression = mapOf("in" to listOf(null, "banana")),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf("n", null)),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf(null, listOf(null, null))),
result = JsonLogicResult.Success(true)
),
TestInput(
expression = mapOf("in" to listOf(null, listOf(listOf(null), "banana"))),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf(null, listOf("", ""))),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf("t", "true")),
result = JsonLogicResult.Success(true)
),
TestInput(
expression = mapOf("in" to listOf("j", "apple", "juice")),
result = JsonLogicResult.Success(false)
),
TestInput(
expression = mapOf("in" to listOf("Spring", "Springfield")),
result = JsonLogicResult.Success(true)
),
)
// given
) { testInput: TestInput ->
// when
val evaluationResult = logicEngine.evaluate(testInput.expression, testInput.data)

// then
evaluationResult valueShouldBe testInput.result
}
})


Loading