From b5426337269e6af043ebcb00d28a7e81fc081e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20H=2E=20Rapat=C3=A3o?= Date: Fri, 5 Jul 2024 22:27:23 +0100 Subject: [PATCH] fix: Kotlin evaluator is not supporting nullable values (#26) When evaluating an expression using the Kotlin Evaluator, when the input map contains nullable values, the evaluator throws an exception to key not found instead of returning null. This change fixes it, returning null when a key is present but has null as value, throwing the exception only when the key is not present in the input map. --- .../types/builder/extensions/BooleanExtensions.kt | 4 ++-- .../ruleset/jackson/SerializationExamplesBuilder.kt | 10 +++++++++- .../projects/ruleset/jackson/SerializationTest.kt | 2 +- .../ruleset/engine/evaluator/kotlin/KotlinContext.kt | 8 ++++++-- .../projects/ruleset/engine/BaseEvaluatorTest.kt | 2 +- .../projects/ruleset/engine/cases/ExpressionCases.kt | 5 +++++ .../rapatao/projects/ruleset/engine/cases/TestData.kt | 3 +-- 7 files changed, 25 insertions(+), 9 deletions(-) diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BooleanExtensions.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BooleanExtensions.kt index f52abcd..d1132fd 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BooleanExtensions.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BooleanExtensions.kt @@ -23,7 +23,7 @@ fun Any.isFalse(): Expression = ExpressionBuilder.isFalse(this) * @param right The object to compare with. * @return An expression representing the equality comparison between the current object and the given object. */ -infix fun Any.equalsTo(right: Any): Expression = ExpressionBuilder.left(this).equalsTo(right) +infix fun Any.equalsTo(right: Any?): Expression = ExpressionBuilder.left(this).equalsTo(right) /** * Compares this object with the specified [right] object for inequality. @@ -31,5 +31,5 @@ infix fun Any.equalsTo(right: Any): Expression = ExpressionBuilder.left(this).eq * @param right The object to compare with this object. * @return An [Expression] representing the inequality comparison result. */ -infix fun Any.notEqualsTo(right: Any): Expression = ExpressionBuilder.left(this).notEqualsTo(right) +infix fun Any.notEqualsTo(right: Any?): Expression = ExpressionBuilder.left(this).notEqualsTo(right) diff --git a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt index df1d1a8..ae0c0be 100644 --- a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt +++ b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt @@ -102,9 +102,17 @@ internal class SerializationExamplesBuilder { ), ) - private val casesFromTests = TestData.allCases() + private val casesFromTests = TestData.cases() .flatMap { it.get().toList() } .filterIsInstance() + .filter { + // remove from serialization example rules that one of the operands is null + it.isValid() && + ( + (it.left != null && it.right != null) || + !it.noneMatch.isNullOrEmpty() || !it.allMatch.isNullOrEmpty() || !it.anyMatch.isNullOrEmpty() + ) + } .toSet() @Test diff --git a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt index 8351f6c..b5da54b 100644 --- a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt +++ b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt @@ -24,7 +24,7 @@ class SerializationTest { companion object { @JvmStatic - fun tests() = TestData.allCases() + fun tests() = TestData.cases() } @ParameterizedTest diff --git a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt index 2c80b60..9a6991b 100644 --- a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt +++ b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt @@ -51,10 +51,14 @@ class KotlinContext( key.toBooleanStrictOrNull() }, { - inputData.getOrElse(key) { + inputData[key] + }, + { + if (!inputData.containsKey(key)) { throw NoSuchElementException("$key not found") } - }, + null + } ).firstNotNullOfOrNull { it() } } diff --git a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt index 56e4d48..8cef73d 100644 --- a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt +++ b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt @@ -25,7 +25,7 @@ abstract class BaseEvaluatorTest( companion object { @JvmStatic - fun tests() = TestData.allCases() + fun tests() = TestData.cases() @JvmStatic fun onFailure() = TestData.onFailureCases() diff --git a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/cases/ExpressionCases.kt b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/cases/ExpressionCases.kt index 6928a92..815d557 100644 --- a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/cases/ExpressionCases.kt +++ b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/cases/ExpressionCases.kt @@ -146,7 +146,12 @@ object ExpressionCases { "item.price" equalsTo 0, false ), + Arguments.of( + "item.nullableStr" equalsTo null, + true + ) ) + @Suppress("MagicNumber") private fun notEqualsCases() = listOf( Arguments.of( diff --git a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/cases/TestData.kt b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/cases/TestData.kt index f1dfd28..b342d2b 100644 --- a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/cases/TestData.kt +++ b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/cases/TestData.kt @@ -16,6 +16,7 @@ object TestData { val name: String, val tags: List, val arrTags: Array, + val nullableStr: String? = null, ) val inputData = RequestData( @@ -29,8 +30,6 @@ object TestData { ) ) - fun allCases(): List = cases() - fun onFailureCases(): List = (OnFailureCases.cases()) fun cases() =