test: cover reverseFlipsTheCharactersOfAString

This commit is contained in:
dualfroz committed 2026-09-08 22:10:44 +02:00
1 parent 5eedad6404
commit 795ba03620
1 file changed
+173
+173
View File
@@ -289,4 +289,177 @@ class EvaluatorTest {
assertFailsWith<KestrelException> { evalConstant(CallExpression("MOD", listOf(intLit(1), intLit(0)))) } assertFailsWith<KestrelException> { evalConstant(CallExpression("MOD", listOf(intLit(1), intLit(0)))) }
} }
@Test
fun reverseFlipsTheCharactersOfAString() {
assertEquals(Value.TextValue("cba"), evalConstant(CallExpression("REVERSE", listOf(textLit("abc")))))
assertEquals(Value.TextValue(""), evalConstant(CallExpression("REVERSE", listOf(textLit("")))))
assertFailsWith<KestrelException> { evalConstant(CallExpression("REVERSE", listOf(intLit(1)))) }
}
@Test
fun leftAndRightTakeCharactersFromEachEndAndClampToTheStringsLength() {
assertEquals(Value.TextValue("hel"), evalConstant(CallExpression("LEFT", listOf(textLit("hello"), intLit(3)))))
assertEquals(Value.TextValue("llo"), evalConstant(CallExpression("RIGHT", listOf(textLit("hello"), intLit(3)))))
assertEquals(Value.TextValue("hello"), evalConstant(CallExpression("LEFT", listOf(textLit("hello"), intLit(100)))))
assertEquals(Value.TextValue("hello"), evalConstant(CallExpression("RIGHT", listOf(textLit("hello"), intLit(100)))))
assertEquals(Value.TextValue(""), evalConstant(CallExpression("LEFT", listOf(textLit("hello"), intLit(0)))))
assertEquals(Value.TextValue(""), evalConstant(CallExpression("RIGHT", listOf(textLit("hello"), intLit(-1)))))
}
@Test
fun signReturnsMinusOneZeroOrOneAcrossEveryNumericType() {
assertEquals(Value.IntegerValue(1), evalConstant(CallExpression("SIGN", listOf(intLit(5)))))
assertEquals(Value.IntegerValue(-1), evalConstant(CallExpression("SIGN", listOf(intLit(-5)))))
assertEquals(Value.IntegerValue(0), evalConstant(CallExpression("SIGN", listOf(intLit(0)))))
assertEquals(Value.IntegerValue(1), evalConstant(CallExpression("SIGN", listOf(realLit(0.1)))))
assertEquals(Value.IntegerValue(-1), evalConstant(CallExpression("SIGN", listOf(realLit(-0.1)))))
val negativeDecimal = LiteralExpression(Value.DecimalValue(BigDecimal("-3.50")))
assertEquals(Value.IntegerValue(-1), evalConstant(CallExpression("SIGN", listOf(negativeDecimal))))
assertFailsWith<KestrelException> { evalConstant(CallExpression("SIGN", listOf(textLit("x")))) }
}
@Test
fun truncCutsDigitsTowardZeroWithoutRoundingUnlikeRound() {
assertEquals(Value.IntegerValue(4), evalConstant(CallExpression("TRUNC", listOf(intLit(4)))))
assertEquals(Value.RealValue(1.9), evalConstant(CallExpression("TRUNC", listOf(realLit(1.99), intLit(1)))))
assertEquals(Value.RealValue(-1.9), evalConstant(CallExpression("TRUNC", listOf(realLit(-1.99), intLit(1)))))
val decimal = LiteralExpression(Value.DecimalValue(BigDecimal("1.999")))
assertEquals(
Value.DecimalValue(BigDecimal("1.99")),
evalConstant(CallExpression("TRUNC", listOf(decimal, intLit(2))))
)
// TRUNC never rounds up, exactly the case where it must differ from ROUND's HALF_UP default.
assertEquals(Value.RealValue(1.0), evalConstant(CallExpression("TRUNC", listOf(realLit(1.9)))))
assertEquals(Value.RealValue(2.0), evalConstant(CallExpression("ROUND", listOf(realLit(1.9)))))
}
@Test
fun greatestAndLeastPickTheOrderingExtremeAcrossMixedNumericTypes() {
assertEquals(
Value.IntegerValue(7),
evalConstant(CallExpression("GREATEST", listOf(intLit(3), intLit(7), intLit(1))))
)
assertEquals(
Value.IntegerValue(1),
evalConstant(CallExpression("LEAST", listOf(intLit(3), intLit(7), intLit(1))))
)
assertEquals(
Value.RealValue(7.5),
evalConstant(CallExpression("GREATEST", listOf(intLit(3), realLit(7.5), intLit(1))))
)
assertEquals(Value.TextValue("banana"), evalConstant(CallExpression("GREATEST", listOf(textLit("apple"), textLit("banana")))))
}
@Test
fun greatestAndLeastPropagateNullAndRequireAtLeastTwoArguments() {
assertEquals(Value.NullValue, evalConstant(CallExpression("GREATEST", listOf(intLit(1), nullLit))))
assertEquals(Value.NullValue, evalConstant(CallExpression("LEAST", listOf(nullLit, intLit(1)))))
assertFailsWith<KestrelException> { evalConstant(CallExpression("GREATEST", listOf(intLit(1)))) }
}
@Test
fun functionRequiringTextRejectsWrongType() {
assertFailsWith<KestrelException> { evalConstant(CallExpression("UPPER", listOf(intLit(1)))) }
}
@Test
fun isNullDetectsNullness() {
assertEquals(Value.BooleanValue(true), evalConstant(IsNullExpression(nullLit, negated = false)))
assertEquals(Value.BooleanValue(false), evalConstant(IsNullExpression(intLit(1), negated = false)))
assertEquals(Value.BooleanValue(true), evalConstant(IsNullExpression(intLit(1), negated = true)))
assertEquals(Value.BooleanValue(false), evalConstant(IsNullExpression(nullLit, negated = true)))
}
@Test
fun inMatchesAnyListMember() {
assertEquals(
Value.BooleanValue(true),
evalConstant(InExpression(intLit(2), listOf(intLit(1), intLit(2), intLit(3)), negated = false))
)
assertEquals(
Value.BooleanValue(false),
evalConstant(InExpression(intLit(9), listOf(intLit(1), intLit(2)), negated = false))
)
assertEquals(
Value.BooleanValue(false),
evalConstant(InExpression(intLit(2), listOf(intLit(1), intLit(2)), negated = true))
)
}
@Test
fun inWithNullOperandIsNull() {
assertEquals(Value.NullValue, evalConstant(InExpression(nullLit, listOf(intLit(1)), negated = false)))
}
@Test
fun inWithUnmatchedNullInListIsNullNotFalse() {
assertEquals(
Value.NullValue,
evalConstant(InExpression(intLit(9), listOf(intLit(1), nullLit), negated = false))
)
}
@Test
fun betweenIsInclusiveOnBothEnds() {
assertEquals(Value.BooleanValue(true), evalConstant(BetweenExpression(intLit(18), intLit(18), intLit(65), negated = false)))
assertEquals(Value.BooleanValue(true), evalConstant(BetweenExpression(intLit(65), intLit(18), intLit(65), negated = false)))
assertEquals(Value.BooleanValue(false), evalConstant(BetweenExpression(intLit(17), intLit(18), intLit(65), negated = false)))
assertEquals(Value.BooleanValue(true), evalConstant(BetweenExpression(intLit(17), intLit(18), intLit(65), negated = true)))
}
@Test
fun likeSupportsPercentAndUnderscoreWildcards() {
assertEquals(Value.BooleanValue(true), evalConstant(LikeExpression(textLit("hello"), textLit("h%"), negated = false)))
assertEquals(Value.BooleanValue(true), evalConstant(LikeExpression(textLit("hello"), textLit("h_llo"), negated = false)))
assertEquals(Value.BooleanValue(false), evalConstant(LikeExpression(textLit("hello"), textLit("world"), negated = false)))
assertEquals(Value.BooleanValue(false), evalConstant(LikeExpression(textLit("hello"), textLit("h%"), negated = true)))
}
@Test
fun likeEscapesRegexMetacharactersInPattern() {
assertEquals(Value.BooleanValue(true), evalConstant(LikeExpression(textLit("a.b"), textLit("a.b"), negated = false)))
assertEquals(Value.BooleanValue(false), evalConstant(LikeExpression(textLit("axb"), textLit("a.b"), negated = false)))
}
@Test
fun searchedCaseReturnsFirstMatchingBranch() {
val expression = CaseExpression(
operand = null,
branches = listOf(
CaseBranch(bin(BinaryOperator.Less, intLit(10), intLit(5)), textLit("small")),
CaseBranch(bin(BinaryOperator.GreaterEqual, intLit(10), intLit(5)), textLit("big"))
),
elseExpression = textLit("none")
)
assertEquals(Value.TextValue("big"), evalConstant(expression))
}
@Test
fun simpleCaseComparesOperandAgainstBranches() {
val expression = CaseExpression(
operand = intLit(2),
branches = listOf(CaseBranch(intLit(1), textLit("one")), CaseBranch(intLit(2), textLit("two"))),
elseExpression = textLit("other")
)
assertEquals(Value.TextValue("two"), evalConstant(expression))
}
@Test
fun caseFallsThroughToElseWhenNoBranchMatches() {
val expression = CaseExpression(
operand = null,
branches = listOf(CaseBranch(falseLit, textLit("nope"))),
elseExpression = textLit("fallback")
)
assertEquals(Value.TextValue("fallback"), evalConstant(expression))
}
@Test
fun caseWithoutElseYieldsNullWhenNoBranchMatches() {
val expression = CaseExpression(operand = null, branches = listOf(CaseBranch(falseLit, textLit("nope"))), elseExpression = null)
assertEquals(Value.NullValue, evalConstant(expression))
}
private fun cast(operand: Expression, type: ColumnType, precision: Int? = null, scale: Int? = null): Expression =
CastExpression(operand, type, precision, scale)
} }