Initial Development #1

Merged
danieljsummers merged 88 commits from v1-rc into main 2025-04-16 01:29:20 +00:00
6 changed files with 204 additions and 107 deletions
Showing only changes of commit e14fd23ead - Show all commits

View File

@ -17,7 +17,7 @@ object Count {
JsonDocument.load(db) JsonDocument.load(db)
assertEquals( assertEquals(
3L, 3L,
db.conn.countByFields(TEST_TABLE, listOf(Field.between("num_value", 10, 20))), db.conn.countByFields(TEST_TABLE, listOf(Field.between("numValue", 10, 20))),
"There should have been 3 matching documents" "There should have been 3 matching documents"
) )
} }
@ -53,7 +53,7 @@ object Count {
JsonDocument.load(db) JsonDocument.load(db)
assertEquals( assertEquals(
2L, 2L,
db.conn.countByJsonPath(TEST_TABLE, "$.num_value ? (@ < 5)"), db.conn.countByJsonPath(TEST_TABLE, "$.numValue ? (@ < 5)"),
"There should have been 2 matching documents" "There should have been 2 matching documents"
) )
} }
@ -62,7 +62,7 @@ object Count {
JsonDocument.load(db) JsonDocument.load(db)
assertEquals( assertEquals(
0L, 0L,
db.conn.countByJsonPath(TEST_TABLE, "$.num_value ? (@ > 100)"), db.conn.countByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)"),
"There should have been no matching documents" "There should have been no matching documents"
) )
} }

View File

@ -1,25 +1,43 @@
package solutions.bitbadger.documents package solutions.bitbadger.documents
/**
* Information required to generate a JSON field comparison
*/
interface Comparison<T> { interface Comparison<T> {
/** The operation for the field comparison */
val op: Op val op: Op
val isNumeric: Boolean /** The value against which the comparison will be made */
val value: T val value: T
/** Whether the value should be considered numeric */
val isNumeric: Boolean
} }
/** /**
* A single-value comparison against a field in a JSON document * Function to determine if a value is numeric
* *
* @property op The operation for the field comparison * @param it The value in question
* @property value The value against which the comparison will be made * @return True if it is a numeric type, false if not
*/ */
class SingleComparison<T>(override val op: Op, override val value: T) : Comparison<T> { private fun <T> isNumeric(it: T) =
it is Byte || it is Short || it is Int || it is Long
/** Is the value for this comparison a numeric value? */ /**
override val isNumeric: Boolean * A single-value comparison against a field in a JSON document
get() = value.let { it is Byte || it is Short || it is Int || it is Long } */
class ComparisonSingle<T>(override val op: Op, override val value: T) : Comparison<T> {
init {
when (op) {
Op.BETWEEN, Op.IN, Op.IN_ARRAY ->
throw DocumentException("Cannot use single comparison for multiple values")
else -> { }
}
}
override val isNumeric = isNumeric(value)
override fun toString() = override fun toString() =
"$op $value" "$op $value"
@ -28,27 +46,23 @@ class SingleComparison<T>(override val op: Op, override val value: T) : Comparis
/** /**
* A range comparison against a field in a JSON document * A range comparison against a field in a JSON document
*/ */
class BetweenComparison<T>(override val op: Op = Op.BETWEEN, override val value: Pair<T, T>) : Comparison<Pair<T, T>> { class ComparisonBetween<T>(override val value: Pair<T, T>) : Comparison<Pair<T, T>> {
override val op = Op.BETWEEN
override val isNumeric: Boolean override val isNumeric = isNumeric(value.first)
get() = value.first.let { it is Byte || it is Short || it is Int || it is Long }
} }
/** /**
* A check within a collection of values * A check within a collection of values
*/ */
class InComparison<T>(override val op: Op = Op.IN, override val value: Collection<T>) : Comparison<Collection<T>> { class ComparisonIn<T>(override val value: Collection<T>) : Comparison<Collection<T>> {
override val op = Op.IN
override val isNumeric: Boolean override val isNumeric = !value.isEmpty() && isNumeric(value.elementAt(0))
get() = !value.isEmpty() && value.elementAt(0).let { it is Byte || it is Short || it is Int || it is Long }
} }
/** /**
* A check within a collection of values * A check within a collection of values against an array in a document
*/ */
class InArrayComparison<T>(override val op: Op = Op.IN_ARRAY, override val value: Pair<String, Collection<T>>) : Comparison<Pair<String, Collection<T>>> { class ComparisonInArray<T>(override val value: Pair<String, Collection<T>>) : Comparison<Pair<String, Collection<T>>> {
override val op = Op.IN_ARRAY
override val isNumeric: Boolean override val isNumeric = false
get() = !value.second.isEmpty() && value.second.elementAt(0)
.let { it is Byte || it is Short || it is Int || it is Long }
} }

View File

@ -101,18 +101,18 @@ class Field<T> private constructor(
fun appendParameter(existing: MutableCollection<Parameter<*>>): MutableCollection<Parameter<*>> { fun appendParameter(existing: MutableCollection<Parameter<*>>): MutableCollection<Parameter<*>> {
val typ = if (comparison.isNumeric) ParameterType.NUMBER else ParameterType.STRING val typ = if (comparison.isNumeric) ParameterType.NUMBER else ParameterType.STRING
when (comparison) { when (comparison) {
is BetweenComparison<*> -> { is ComparisonBetween<*> -> {
existing.add(Parameter("${parameterName}min", typ, comparison.value.first)) existing.add(Parameter("${parameterName}min", typ, comparison.value.first))
existing.add(Parameter("${parameterName}max", typ, comparison.value.second)) existing.add(Parameter("${parameterName}max", typ, comparison.value.second))
} }
is InComparison<*> -> { is ComparisonIn<*> -> {
comparison.value.forEachIndexed { index, item -> comparison.value.forEachIndexed { index, item ->
existing.add(Parameter("${parameterName}_$index", typ, item)) existing.add(Parameter("${parameterName}_$index", typ, item))
} }
} }
is InArrayComparison<*> -> { is ComparisonInArray<*> -> {
val mkString = Configuration.dialect("append parameters for InArray") == Dialect.POSTGRESQL val mkString = Configuration.dialect("append parameters for InArray") == Dialect.POSTGRESQL
// TODO: I think this is actually Pair<String, Collection<*>> // TODO: I think this is actually Pair<String, Collection<*>>
comparison.value.second.forEachIndexed { index, item -> comparison.value.second.forEachIndexed { index, item ->
@ -147,7 +147,7 @@ class Field<T> private constructor(
* @return A `Field` with the given comparison * @return A `Field` with the given comparison
*/ */
fun <T> equal(name: String, value: T, paramName: String? = null) = fun <T> equal(name: String, value: T, paramName: String? = null) =
Field(name, SingleComparison(Op.EQUAL, value), paramName) Field(name, ComparisonSingle(Op.EQUAL, value), paramName)
/** /**
* Create a field greater-than comparison * Create a field greater-than comparison
@ -158,7 +158,7 @@ class Field<T> private constructor(
* @return A `Field` with the given comparison * @return A `Field` with the given comparison
*/ */
fun <T> greater(name: String, value: T, paramName: String? = null) = fun <T> greater(name: String, value: T, paramName: String? = null) =
Field(name, SingleComparison(Op.GREATER, value), paramName) Field(name, ComparisonSingle(Op.GREATER, value), paramName)
/** /**
* Create a field greater-than-or-equal-to comparison * Create a field greater-than-or-equal-to comparison
@ -169,7 +169,7 @@ class Field<T> private constructor(
* @return A `Field` with the given comparison * @return A `Field` with the given comparison
*/ */
fun <T> greaterOrEqual(name: String, value: T, paramName: String? = null) = fun <T> greaterOrEqual(name: String, value: T, paramName: String? = null) =
Field(name, SingleComparison(Op.GREATER_OR_EQUAL, value), paramName) Field(name, ComparisonSingle(Op.GREATER_OR_EQUAL, value), paramName)
/** /**
* Create a field less-than comparison * Create a field less-than comparison
@ -180,7 +180,7 @@ class Field<T> private constructor(
* @return A `Field` with the given comparison * @return A `Field` with the given comparison
*/ */
fun <T> less(name: String, value: T, paramName: String? = null) = fun <T> less(name: String, value: T, paramName: String? = null) =
Field(name, SingleComparison(Op.LESS, value), paramName) Field(name, ComparisonSingle(Op.LESS, value), paramName)
/** /**
* Create a field less-than-or-equal-to comparison * Create a field less-than-or-equal-to comparison
@ -191,7 +191,7 @@ class Field<T> private constructor(
* @return A `Field` with the given comparison * @return A `Field` with the given comparison
*/ */
fun <T> lessOrEqual(name: String, value: T, paramName: String? = null) = fun <T> lessOrEqual(name: String, value: T, paramName: String? = null) =
Field(name, SingleComparison(Op.LESS_OR_EQUAL, value), paramName) Field(name, ComparisonSingle(Op.LESS_OR_EQUAL, value), paramName)
/** /**
* Create a field inequality comparison * Create a field inequality comparison
@ -202,7 +202,7 @@ class Field<T> private constructor(
* @return A `Field` with the given comparison * @return A `Field` with the given comparison
*/ */
fun <T> notEqual(name: String, value: T, paramName: String? = null) = fun <T> notEqual(name: String, value: T, paramName: String? = null) =
Field(name, SingleComparison(Op.NOT_EQUAL, value), paramName) Field(name, ComparisonSingle(Op.NOT_EQUAL, value), paramName)
/** /**
* Create a field range comparison * Create a field range comparison
@ -214,7 +214,7 @@ class Field<T> private constructor(
* @return A `Field` with the given comparison * @return A `Field` with the given comparison
*/ */
fun <T> between(name: String, minValue: T, maxValue: T, paramName: String? = null) = fun <T> between(name: String, minValue: T, maxValue: T, paramName: String? = null) =
Field(name, BetweenComparison(value = Pair(minValue, maxValue)), paramName) Field(name, ComparisonBetween(Pair(minValue, maxValue)), paramName)
/** /**
* Create a field where any values match (SQL `IN`) * Create a field where any values match (SQL `IN`)
@ -225,7 +225,7 @@ class Field<T> private constructor(
* @return A `Field` with the given comparison * @return A `Field` with the given comparison
*/ */
fun <T> any(name: String, values: Collection<T>, paramName: String? = null) = fun <T> any(name: String, values: Collection<T>, paramName: String? = null) =
Field(name, InComparison(value = values), paramName) Field(name, ComparisonIn(values), paramName)
/** /**
* Create a field where values should exist in a document's array * Create a field where values should exist in a document's array
@ -237,16 +237,16 @@ class Field<T> private constructor(
* @return A `Field` with the given comparison * @return A `Field` with the given comparison
*/ */
fun <T> inArray(name: String, tableName: String, values: Collection<T>, paramName: String? = null) = fun <T> inArray(name: String, tableName: String, values: Collection<T>, paramName: String? = null) =
Field(name, InArrayComparison(value = Pair(tableName, values)), paramName) Field(name, ComparisonInArray(Pair(tableName, values)), paramName)
fun exists(name: String) = fun exists(name: String) =
Field(name, SingleComparison(Op.EXISTS, "")) Field(name, ComparisonSingle(Op.EXISTS, ""))
fun notExists(name: String) = fun notExists(name: String) =
Field(name, SingleComparison(Op.NOT_EXISTS, "")) Field(name, ComparisonSingle(Op.NOT_EXISTS, ""))
fun named(name: String) = fun named(name: String) =
Field(name, SingleComparison(Op.EQUAL, "")) Field(name, ComparisonSingle(Op.EQUAL, ""))
fun nameToPath(name: String, dialect: Dialect, format: FieldFormat): String { fun nameToPath(name: String, dialect: Dialect, format: FieldFormat): String {
val path = StringBuilder("data") val path = StringBuilder("data")

View File

@ -1,5 +1,8 @@
package solutions.bitbadger.documents package solutions.bitbadger.documents
import java.sql.PreparedStatement
import java.sql.Types
/** /**
* A parameter to use for a query * A parameter to use for a query
* *
@ -8,11 +11,45 @@ package solutions.bitbadger.documents
* @property value The value of the parameter * @property value The value of the parameter
*/ */
class Parameter<T>(val name: String, val type: ParameterType, val value: T) { class Parameter<T>(val name: String, val type: ParameterType, val value: T) {
init { init {
if (!name.startsWith(':') && !name.startsWith('@')) if (!name.startsWith(':') && !name.startsWith('@'))
throw DocumentException("Name must start with : or @ ($name)") throw DocumentException("Name must start with : or @ ($name)")
} }
/**
* Bind this parameter to a prepared statement at the given index
*
* @param stmt The prepared statement to which this parameter should be bound
* @param index The index (1-based) to which the parameter should be bound
*/
fun bind(stmt: PreparedStatement, index: Int) {
when (type) {
ParameterType.NUMBER -> {
when (value) {
null -> stmt.setNull(index, Types.NULL)
is Byte -> stmt.setByte(index, value)
is Short -> stmt.setShort(index, value)
is Int -> stmt.setInt(index, value)
is Long -> stmt.setLong(index, value)
else -> throw DocumentException(
"Number parameter must be Byte, Short, Int, or Long (${value!!::class.simpleName})"
)
}
}
ParameterType.STRING -> {
when (value) {
null -> stmt.setNull(index, Types.NULL)
is String -> stmt.setString(index, value)
else -> stmt.setString(index, value.toString())
}
}
ParameterType.JSON -> stmt.setObject(index, value as String, Types.OTHER)
}
}
override fun toString() = override fun toString() =
"$type[$name] = $value" "$type[$name] = $value"
} }

View File

@ -3,7 +3,6 @@ package solutions.bitbadger.documents
import java.sql.Connection import java.sql.Connection
import java.sql.PreparedStatement import java.sql.PreparedStatement
import java.sql.SQLException import java.sql.SQLException
import java.sql.Types
/** /**
* Functions to assist with the creation and implementation of parameters for SQL queries * Functions to assist with the creation and implementation of parameters for SQL queries
@ -58,7 +57,7 @@ object Parameters {
*/ */
fun replaceNamesInQuery(query: String, parameters: Collection<Parameter<*>>) = fun replaceNamesInQuery(query: String, parameters: Collection<Parameter<*>>) =
parameters.sortedByDescending { it.name.length }.fold(query) { acc, param -> acc.replace(param.name, "?") } parameters.sortedByDescending { it.name.length }.fold(query) { acc, param -> acc.replace(param.name, "?") }
.also(::println)
/** /**
* Apply the given parameters to the given query, returning a prepared statement * Apply the given parameters to the given query, returning a prepared statement
* *
@ -69,6 +68,7 @@ object Parameters {
* @throws DocumentException If parameter names are invalid or number value types are invalid * @throws DocumentException If parameter names are invalid or number value types are invalid
*/ */
fun apply(conn: Connection, query: String, parameters: Collection<Parameter<*>>): PreparedStatement { fun apply(conn: Connection, query: String, parameters: Collection<Parameter<*>>): PreparedStatement {
if (parameters.isEmpty()) return try { if (parameters.isEmpty()) return try {
conn.prepareStatement(query) conn.prepareStatement(query)
} catch (ex: SQLException) { } catch (ex: SQLException) {
@ -88,34 +88,9 @@ object Parameters {
replaceNamesInQuery(query, parameters) replaceNamesInQuery(query, parameters)
.let { conn.prepareStatement(it) } .let { conn.prepareStatement(it) }
.also { stmt -> .also { stmt ->
replacements.sortedBy { it.first }.map { it.second }.forEachIndexed { index, param -> replacements.sortedBy { it.first }
val idx = index + 1 .map { it.second }
when (param.type) { .forEachIndexed { index, param -> param.bind(stmt, index + 1) }
ParameterType.NUMBER -> {
when (param.value) {
null -> stmt.setNull(idx, Types.NULL)
is Byte -> stmt.setByte(idx, param.value)
is Short -> stmt.setShort(idx, param.value)
is Int -> stmt.setInt(idx, param.value)
is Long -> stmt.setLong(idx, param.value)
else -> throw DocumentException(
"Number parameter must be Byte, Short, Int, or Long " +
"(${param.value::class.simpleName})"
)
}
}
ParameterType.STRING -> {
when (param.value) {
null -> stmt.setNull(idx, Types.NULL)
is String -> stmt.setString(idx, param.value)
else -> stmt.setString(idx, param.value.toString())
}
}
ParameterType.JSON -> stmt.setObject(idx, param.value as String, Types.OTHER)
}
}
} }
} catch (ex: SQLException) { } catch (ex: SQLException) {
throw DocumentException("Error creating query / binding parameters: ${ex.message}", ex) throw DocumentException("Error creating query / binding parameters: ${ex.message}", ex)

View File

@ -2,94 +2,165 @@ package solutions.bitbadger.documents
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
/** /**
* Unit tests for the `Comparison` class * Unit tests for the `ComparisonBetween` class
*/ */
@DisplayName("Comparison") @DisplayName("ComparisonBetween")
class ComparisonTest { class ComparisonBetweenTest {
@Test
@DisplayName("op is set to BETWEEN")
fun op() =
assertEquals(Op.BETWEEN, ComparisonBetween(Pair(0, 0)).op, "Between comparison should have BETWEEN op")
@Test
@DisplayName("isNumeric is false with strings")
fun isNumericFalseForStringsAndBetween() =
assertFalse(ComparisonBetween(Pair("eh", "zed")).isNumeric,
"A BETWEEN with strings should not be numeric")
@Test
@DisplayName("isNumeric is true with bytes")
fun isNumericTrueForByteAndBetween() =
assertTrue(ComparisonBetween(Pair<Byte, Byte>(7, 11)).isNumeric, "A BETWEEN with bytes should be numeric")
@Test
@DisplayName("isNumeric is true with shorts")
fun isNumericTrueForShortAndBetween() =
assertTrue(ComparisonBetween(Pair<Short, Short>(0, 9)).isNumeric,
"A BETWEEN with shorts should be numeric")
@Test
@DisplayName("isNumeric is true with ints")
fun isNumericTrueForIntAndBetween() =
assertTrue(ComparisonBetween(Pair(15, 44)).isNumeric, "A BETWEEN with ints should be numeric")
@Test
@DisplayName("isNumeric is true with longs")
fun isNumericTrueForLongAndBetween() =
assertTrue(ComparisonBetween(Pair(9L, 12L)).isNumeric, "A BETWEEN with longs should be numeric")
}
/**
* Unit tests for the `ComparisonIn` class
*/
@DisplayName("ComparisonIn")
class ComparisonInTest {
@Test
@DisplayName("op is set to IN")
fun op() =
assertEquals(Op.IN, ComparisonIn(listOf<String>()).op, "In comparison should have IN op")
@Test @Test
@DisplayName("isNumeric is false for empty list of values") @DisplayName("isNumeric is false for empty list of values")
fun isNumericFalseForEmptyList() = fun isNumericFalseForEmptyList() =
assertFalse(InComparison(Op.IN, listOf<Int>()).isNumeric, "An IN with empty list should not be numeric") assertFalse(ComparisonIn(listOf<Int>()).isNumeric, "An IN with empty list should not be numeric")
@Test @Test
@DisplayName("isNumeric is false for IN with strings") @DisplayName("isNumeric is false with strings")
fun isNumericFalseForStringsAndIn() = fun isNumericFalseForStringsAndIn() =
assertFalse(InComparison(Op.IN, listOf("a", "b", "c")).isNumeric, "An IN with strings should not be numeric") assertFalse(ComparisonIn(listOf("a", "b", "c")).isNumeric, "An IN with strings should not be numeric")
@Test @Test
@DisplayName("isNumeric is true for IN with bytes") @DisplayName("isNumeric is true with bytes")
fun isNumericTrueForByteAndIn() = fun isNumericTrueForByteAndIn() =
assertTrue(InComparison(Op.IN, listOf<Byte>(4, 8)).isNumeric, "An IN with bytes should be numeric") assertTrue(ComparisonIn(listOf<Byte>(4, 8)).isNumeric, "An IN with bytes should be numeric")
@Test @Test
@DisplayName("isNumeric is true for IN with shorts") @DisplayName("isNumeric is true with shorts")
fun isNumericTrueForShortAndIn() = fun isNumericTrueForShortAndIn() =
assertTrue(InComparison(Op.IN, listOf<Short>(18, 22)).isNumeric, "An IN with shorts should be numeric") assertTrue(ComparisonIn(listOf<Short>(18, 22)).isNumeric, "An IN with shorts should be numeric")
@Test @Test
@DisplayName("isNumeric is true for IN with ints") @DisplayName("isNumeric is true with ints")
fun isNumericTrueForIntAndIn() = fun isNumericTrueForIntAndIn() =
assertTrue(InComparison(Op.IN, listOf(7, 8, 9)).isNumeric, "An IN with ints should be numeric") assertTrue(ComparisonIn(listOf(7, 8, 9)).isNumeric, "An IN with ints should be numeric")
@Test @Test
@DisplayName("isNumeric is true for IN with longs") @DisplayName("isNumeric is true with longs")
fun isNumericTrueForLongAndIn() = fun isNumericTrueForLongAndIn() =
assertTrue(InComparison(Op.IN, listOf(3L)).isNumeric, "An IN with longs should be numeric") assertTrue(ComparisonIn(listOf(3L)).isNumeric, "An IN with longs should be numeric")
}
/**
* Unit tests for the `ComparisonInArray` class
*/
@DisplayName("ComparisonInArray")
class ComparisonInArrayTest {
@Test @Test
@DisplayName("isNumeric is false for BETWEEN with strings") @DisplayName("op is set to IN_ARRAY")
fun isNumericFalseForStringsAndBetween() = fun op() =
assertFalse(BetweenComparison(Op.BETWEEN, Pair("eh", "zed")).isNumeric, assertEquals(
"A BETWEEN with strings should not be numeric") Op.IN_ARRAY,
ComparisonInArray(Pair(TEST_TABLE, listOf<String>())).op,
"InArray comparison should have IN_ARRAY op"
)
@Test @Test
@DisplayName("isNumeric is true for BETWEEN with bytes") @DisplayName("isNumeric is false for empty list of values")
fun isNumericTrueForByteAndBetween() = fun isNumericFalseForEmptyList() =
assertTrue(BetweenComparison(Op.BETWEEN, Pair<Byte, Byte>(7, 11)).isNumeric, "A BETWEEN with bytes should be numeric") assertFalse(ComparisonIn(listOf<Int>()).isNumeric, "An IN_ARRAY with empty list should not be numeric")
@Test @Test
@DisplayName("isNumeric is true for BETWEEN with shorts") @DisplayName("isNumeric is false with strings")
fun isNumericTrueForShortAndBetween() = fun isNumericFalseForStringsAndIn() =
assertTrue(BetweenComparison(Op.BETWEEN, Pair<Short, Short>(0, 9)).isNumeric, assertFalse(ComparisonIn(listOf("a", "b", "c")).isNumeric, "An IN_ARRAY with strings should not be numeric")
"A BETWEEN with shorts should be numeric")
@Test @Test
@DisplayName("isNumeric is true for BETWEEN with ints") @DisplayName("isNumeric is false with bytes")
fun isNumericTrueForIntAndBetween() = fun isNumericTrueForByteAndIn() =
assertTrue(BetweenComparison(Op.BETWEEN, Pair(15, 44)).isNumeric, "A BETWEEN with ints should be numeric") assertTrue(ComparisonIn(listOf<Byte>(4, 8)).isNumeric, "An IN_ARRAY with bytes should not be numeric")
@Test @Test
@DisplayName("isNumeric is true for BETWEEN with longs") @DisplayName("isNumeric is false with shorts")
fun isNumericTrueForLongAndBetween() = fun isNumericTrueForShortAndIn() =
assertTrue(BetweenComparison(Op.BETWEEN, Pair(9L, 12L)).isNumeric, "A BETWEEN with longs should be numeric") assertTrue(ComparisonIn(listOf<Short>(18, 22)).isNumeric, "An IN_ARRAY with shorts should not be numeric")
@Test
@DisplayName("isNumeric is false with ints")
fun isNumericTrueForIntAndIn() =
assertTrue(ComparisonIn(listOf(7, 8, 9)).isNumeric, "An IN_ARRAY with ints should not be numeric")
@Test
@DisplayName("isNumeric is false with longs")
fun isNumericTrueForLongAndIn() =
assertTrue(ComparisonIn(listOf(3L)).isNumeric, "An IN_ARRAY with longs should not be numeric")
}
/**
* Unit tests for the `ComparisonSingle` class
*/
@DisplayName("ComparisonSingle")
class ComparisonSingleTest {
@Test @Test
@DisplayName("isNumeric is false for string value") @DisplayName("isNumeric is false for string value")
fun isNumericFalseForString() = fun isNumericFalseForString() =
assertFalse(SingleComparison(Op.EQUAL, "80").isNumeric, "A string should not be numeric") assertFalse(ComparisonSingle(Op.EQUAL, "80").isNumeric, "A string should not be numeric")
@Test @Test
@DisplayName("isNumeric is true for byte value") @DisplayName("isNumeric is true for byte value")
fun isNumericTrueForByte() = fun isNumericTrueForByte() =
assertTrue(SingleComparison(Op.EQUAL, 47.toByte()).isNumeric, "A byte should be numeric") assertTrue(ComparisonSingle(Op.EQUAL, 47.toByte()).isNumeric, "A byte should be numeric")
@Test @Test
@DisplayName("isNumeric is true for short value") @DisplayName("isNumeric is true for short value")
fun isNumericTrueForShort() = fun isNumericTrueForShort() =
assertTrue(SingleComparison(Op.EQUAL, 2.toShort()).isNumeric, "A short should be numeric") assertTrue(ComparisonSingle(Op.EQUAL, 2.toShort()).isNumeric, "A short should be numeric")
@Test @Test
@DisplayName("isNumeric is true for int value") @DisplayName("isNumeric is true for int value")
fun isNumericTrueForInt() = fun isNumericTrueForInt() =
assertTrue(SingleComparison(Op.EQUAL, 555).isNumeric, "An int should be numeric") assertTrue(ComparisonSingle(Op.EQUAL, 555).isNumeric, "An int should be numeric")
@Test @Test
@DisplayName("isNumeric is true for long value") @DisplayName("isNumeric is true for long value")
fun isNumericTrueForLong() = fun isNumericTrueForLong() =
assertTrue(SingleComparison(Op.EQUAL, 82L).isNumeric, "A long should be numeric") assertTrue(ComparisonSingle(Op.EQUAL, 82L).isNumeric, "A long should be numeric")
} }