From 23cd03431ee077961d51e6aa9cd19a4c702374e2 Mon Sep 17 00:00:00 2001 From: Philipp Ossler Date: Thu, 22 Aug 2024 09:11:04 +0200 Subject: [PATCH 1/3] refactor: Migrate to new test style (cherry picked from commit 315b7320ca1cd8fbf0af429a85a2cfc7a04e0d21) --- .../builtin/BuiltinStringFunctionsTest.scala | 89 ++++++++++--------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala index c93605846..3c299be7a 100644 --- a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala +++ b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala @@ -16,138 +16,145 @@ */ package org.camunda.feel.impl.builtin -import org.camunda.feel.impl.FeelIntegrationTest -import org.scalatest.matchers.should.Matchers +import org.camunda.feel.impl.{EvaluationResultMatchers, FeelEngineTest} import org.scalatest.flatspec.AnyFlatSpec -import org.camunda.feel.syntaxtree._ - -import scala.math.BigDecimal.int2bigDecimal +import org.scalatest.matchers.should.Matchers /** @author * Philipp */ -class BuiltinStringFunctionsTest extends AnyFlatSpec with Matchers with FeelIntegrationTest { +class BuiltinStringFunctionsTest + extends AnyFlatSpec + with Matchers + with FeelEngineTest + with EvaluationResultMatchers { "A substring() function" should "return string with _ characters" in { - eval(""" substring("foobar",3) """) should be(ValString("obar")) + evaluateExpression(""" substring("foobar",3) """) should returnResult("obar") } it should "return string with _ characters starting at _" in { - eval(""" substring("foobar",3,3) """) should be(ValString("oba")) + evaluateExpression(""" substring("foobar",3,3) """) should returnResult("oba") } it should "return string with _ characters starting at negative _" in { - eval(""" substring("foobar",-2,1) """) should be(ValString("a")) + evaluateExpression(""" substring("foobar",-2,1) """) should returnResult("a") } it should "be invoked with named parameters" in { - eval(""" substring(string: "foobar", start position:3) """) should be(ValString("obar")) + evaluateExpression(""" substring(string: "foobar", start position:3) """) should returnResult( + "obar" + ) } "A string length() function" should "return the length of a String" in { - eval(""" string length("foo") """) should be(ValNumber(3)) + evaluateExpression(""" string length("foo") """) should returnResult(3) } "A upper case() function" should "return uppercased String" in { - eval(""" upper case("aBc4") """) should be(ValString("ABC4")) + evaluateExpression(""" upper case("aBc4") """) should returnResult("ABC4") } "A lower case() function" should "return lowercased String" in { - eval(""" lower case("aBc4") """) should be(ValString("abc4")) + evaluateExpression(""" lower case("aBc4") """) should returnResult("abc4") } "A substring before() function" should "return substring before match" in { - eval(""" substring before("foobar", "bar") """) should be(ValString("foo")) + evaluateExpression(""" substring before("foobar", "bar") """) should returnResult("foo") - eval(""" substring before("foobar", "xyz") """) should be(ValString("")) + evaluateExpression(""" substring before("foobar", "xyz") """) should returnResult("") } "A substring after() function" should "return substring after match" in { - eval(""" substring after("foobar", "ob") """) should be(ValString("ar")) + evaluateExpression(""" substring after("foobar", "ob") """) should returnResult("ar") - eval(""" substring after("", "a") """) should be(ValString("")) + evaluateExpression(""" substring after("", "a") """) should returnResult("") - eval(""" substring after("foo", "") """) should be(ValString("foo")) + evaluateExpression(""" substring after("foo", "") """) should returnResult("foo") } "A replace() function" should "replace a String" in { - eval(""" replace("abcd", "(ab)|(a)", "[1=$1][2=$2]") """) should be(ValString("[1=ab][2=]cd")) + evaluateExpression(""" replace("abcd", "(ab)|(a)", "[1=$1][2=$2]") """) should returnResult( + "[1=ab][2=]cd" + ) } - it should "replace a String with regex pattern" in (eval( + it should "replace a String with regex pattern" in (evaluateExpression( """ replace("0123456789", "(\d{3})(\d{3})(\d{4})", "($1) $2-$3") """ - ) should be(ValString("(012) 345-6789"))) + ) should returnResult("(012) 345-6789")) it should "return null if the pattern is invalid" in { - eval(""" replace("abc", "([a-z)", "$1") """) should be(ValNull) + evaluateExpression(""" replace("abc", "([a-z)", "$1") """) should returnNull() } "A contains() function" should "return if contains the match" in { - eval(""" contains("foobar", "ob") """) should be(ValBoolean(true)) + evaluateExpression(""" contains("foobar", "ob") """) should returnResult(true) - eval(""" contains("foobar", "of") """) should be(ValBoolean(false)) + evaluateExpression(""" contains("foobar", "of") """) should returnResult(false) } "A starts with() function" should "return if starts with match" in { - eval(""" starts with("foobar", "fo") """) should be(ValBoolean(true)) + evaluateExpression(""" starts with("foobar", "fo") """) should returnResult(true) - eval(""" starts with("foobar", "ba") """) should be(ValBoolean(false)) + evaluateExpression(""" starts with("foobar", "ba") """) should returnResult(false) } "A ends with() function" should "return if ends with match" in { - eval(""" ends with("foobar", "r") """) should be(ValBoolean(true)) + evaluateExpression(""" ends with("foobar", "r") """) should returnResult(true) - eval(""" ends with("foobar", "o") """) should be(ValBoolean(false)) + evaluateExpression(""" ends with("foobar", "o") """) should returnResult(false) } "A matches() function" should "return if String matches a pattern" in { - eval(""" matches("foobar", "^fo*b") """) should be(ValBoolean(true)) + evaluateExpression(""" matches("foobar", "^fo*b") """) should returnResult(true) - eval(""" matches("foobar", "^fo*z") """) should be(ValBoolean(false)) + evaluateExpression(""" matches("foobar", "^fo*z") """) should returnResult(false) } it should "return null if the pattern is invalid" in { - eval(""" matches("abc", "[a-z") """) should be(ValNull) + evaluateExpression(""" matches("abc", "[a-z") """) should returnNull() } "A split() function" should "return a list of substrings" in { - eval(""" split("John Doe", "\s") """) should be( - ValList(List(ValString("John"), ValString("Doe"))) + evaluateExpression(""" split("John Doe", "\s") """) should returnResult( + List("John", "Doe") ) - eval(""" split("a;b;c;;", ";") """) should be( - ValList(List(ValString("a"), ValString("b"), ValString("c"), ValString(""), ValString(""))) + evaluateExpression(""" split("a;b;c;;", ";") """) should returnResult( + List("a", "b", "c", "", "") ) } "An extract() function" should "return a list of strings matching a pattern" in { - eval(""" extract("this is foobar and folbar", "fo[a-z]*") """) should be( - ValList(List(ValString("foobar"), ValString("folbar"))) + evaluateExpression( + """ extract("this is foobar and folbar", "fo[a-z]*") """ + ) should returnResult( + List("foobar", "folbar") ) - eval(""" extract("nothing", "fo[a-z]*") """) should be(ValList(List())) + evaluateExpression(""" extract("nothing", "fo[a-z]*") """) should returnResult(List()) - eval(""" extract("This is fobbar!", "fo[a-z]*") """) should be( - ValList(List(ValString("fobbar"))) + evaluateExpression(""" extract("This is fobbar!", "fo[a-z]*") """) should returnResult( + List("fobbar") ) } it should "return null if the pattern is invalid" in { - eval(""" extract("abc", "[a-z") """) should be(ValNull) + evaluateExpression(""" extract("abc", "[a-z") """) should returnNull() } } From eba36d9f98114a6bce28c6d1686290a91dab900c Mon Sep 17 00:00:00 2001 From: Philipp Ossler Date: Thu, 22 Aug 2024 09:37:27 +0200 Subject: [PATCH 2/3] test: Verify substring() with exceeding length Add a test case to verify that the function `substring()` can handle an exceeding length. If the given length is greater than the string then it returns the remaining characters from the start index. (cherry picked from commit 9af6832e4d84731b2d4e232ec6e21827ed273196) --- .../feel/impl/builtin/BuiltinStringFunctionsTest.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala index 3c299be7a..a15006198 100644 --- a/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala +++ b/src/test/scala/org/camunda/feel/impl/builtin/BuiltinStringFunctionsTest.scala @@ -50,6 +50,13 @@ class BuiltinStringFunctionsTest ) } + it should "return string with remaining characters if the length is greater than the string" in { + evaluateExpression(""" substring("abc", 1, 4) """) should returnResult("abc") + evaluateExpression(""" substring("abc", 2, 4) """) should returnResult("bc") + evaluateExpression(""" substring("abc", -1, 4) """) should returnResult("c") + evaluateExpression(""" substring("abc", 4, 4) """) should returnResult("") + } + "A string length() function" should "return the length of a String" in { evaluateExpression(""" string length("foo") """) should returnResult(3) From 3be78f65f1d82131575cca0fca6d9d402944be49 Mon Sep 17 00:00:00 2001 From: Philipp Ossler Date: Thu, 22 Aug 2024 09:38:24 +0200 Subject: [PATCH 3/3] fix: substring() can handle exceeding length If substring() is called with a given length that is greater than the string then it returns the remaining characters from the start index. (cherry picked from commit 1f7478bd418af618b0023e4cf6da8db2ab566535) --- .../feel/impl/builtin/StringBuiltinFunctions.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/camunda/feel/impl/builtin/StringBuiltinFunctions.scala b/src/main/scala/org/camunda/feel/impl/builtin/StringBuiltinFunctions.scala index 5308b9685..352bbe757 100644 --- a/src/main/scala/org/camunda/feel/impl/builtin/StringBuiltinFunctions.scala +++ b/src/main/scala/org/camunda/feel/impl/builtin/StringBuiltinFunctions.scala @@ -50,11 +50,11 @@ object StringBuiltinFunctions { private def substringFunction3 = builtinFunction( params = List("string", "start position", "length"), invoke = { case List(ValString(string), ValNumber(start), ValNumber(length)) => + val startIndex = stringIndex(string, start.intValue) + val endIndex = Math.min(startIndex + length.intValue, string.length) + ValString( - string.substring( - stringIndex(string, start.intValue), - stringIndex(string, start.intValue) + length.intValue - ) + string.substring(startIndex, endIndex) ) } )