Implement/test Auto ID need
This commit is contained in:
parent
a576a876e0
commit
de4df4109c
@ -78,17 +78,16 @@
|
||||
<version>2.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.10.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>2.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<version>2.0.20</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -1,5 +1,7 @@
|
||||
package solutions.bitbadger.documents.common
|
||||
|
||||
import kotlin.reflect.full.*
|
||||
|
||||
/**
|
||||
* Strategies for automatic document IDs
|
||||
*/
|
||||
@ -32,8 +34,46 @@ enum class AutoId {
|
||||
fun generateRandomString(length: Int): String =
|
||||
kotlin.random.Random.nextBytes((length + 2) / 2)
|
||||
.joinToString("") { String.format("%02x", it) }
|
||||
.substring(0, length - 1)
|
||||
.substring(0, length)
|
||||
|
||||
// TODO: fun <T> needsAutoId(strategy: AutoId, document: T, idProp: String): Boolean
|
||||
/**
|
||||
* Determine if a document needs an automatic ID applied
|
||||
*
|
||||
* @param strategy The auto ID strategy for which the document is evaluated
|
||||
* @param document The document whose need of an automatic ID should be determined
|
||||
* @param idProp The name of the document property containing the ID
|
||||
* @return `true` if the document needs an automatic ID, `false` if not
|
||||
* @throws IllegalArgumentException If bad input prevents the determination
|
||||
*/
|
||||
fun <T> needsAutoId(strategy: AutoId, document: T, idProp: String): Boolean {
|
||||
if (document == null) throw IllegalArgumentException("document cannot be null")
|
||||
|
||||
if (strategy == DISABLED) return false;
|
||||
|
||||
val id = document!!::class.memberProperties.find { it.name == idProp }
|
||||
if (id == null) throw IllegalArgumentException("$idProp not found in document")
|
||||
|
||||
if (strategy == NUMBER) {
|
||||
if (id.returnType == Byte::class.createType()) {
|
||||
return id.call(document) == 0.toByte()
|
||||
}
|
||||
if (id.returnType == Short::class.createType()) {
|
||||
return id.call(document) == 0.toShort()
|
||||
}
|
||||
if (id.returnType == Int::class.createType()) {
|
||||
return id.call(document) == 0
|
||||
}
|
||||
if (id.returnType == Long::class.createType()) {
|
||||
return id.call(document) == 0.toLong()
|
||||
}
|
||||
throw IllegalArgumentException("$idProp was not a number; cannot auto-generate number ID")
|
||||
}
|
||||
|
||||
if (id.returnType == String::class.createType()) {
|
||||
return id.call(document) == ""
|
||||
}
|
||||
|
||||
throw IllegalArgumentException("$idProp was not a string; cannot auto-generate UUID or random string")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ object Configuration {
|
||||
// TODO: var jsonOpts = Json { some cool options }
|
||||
|
||||
/** The field in which a document's ID is stored */
|
||||
var idField = "Id"
|
||||
var idField = "id"
|
||||
|
||||
/** The automatic ID strategy to use */
|
||||
var autoIdStrategy = AutoId.DISABLED
|
||||
|
@ -44,12 +44,12 @@ object Query {
|
||||
* @param dialect The SQL dialect to use when creating this index
|
||||
* @return A query to create the field index
|
||||
*/
|
||||
fun ensureIndexOn(tableName: String, indexName: String, fields: List<String>, dialect: Dialect): String {
|
||||
fun ensureIndexOn(tableName: String, indexName: String, fields: Collection<String>, dialect: Dialect): String {
|
||||
val (_, tbl) = splitSchemaAndTable(tableName)
|
||||
val jsonFields = fields.joinToString(", ") {
|
||||
val parts = it.split(' ')
|
||||
val direction = if (parts.size > 1) " ${parts[1]}" else ""
|
||||
"(" + Field.nameToPath(parts[0], dialect, FieldFormat.SQL) + ") $direction"
|
||||
"(" + Field.nameToPath(parts[0], dialect, FieldFormat.SQL) + ")$direction"
|
||||
}
|
||||
return "CREATE INDEX IF NOT EXISTS idx_${tbl}_$indexName ON $tableName ($jsonFields)"
|
||||
}
|
||||
@ -81,8 +81,7 @@ object Query {
|
||||
* @return A query to save a document
|
||||
*/
|
||||
fun save(tableName: String): String =
|
||||
String.format("INSERT INTO %s VALUES (:data) ON CONFLICT ((data->>'%s')) DO UPDATE SET data = EXCLUDED.data",
|
||||
tableName, Configuration.idField)
|
||||
"${insert(tableName)} ON CONFLICT ((data->>'${Configuration.idField}')) DO UPDATE SET data = EXCLUDED.data"
|
||||
|
||||
/**
|
||||
* Query to count documents in a table (this query has no `WHERE` clause)
|
||||
@ -137,7 +136,7 @@ object Query {
|
||||
* @param dialect The SQL dialect for the generated clause
|
||||
* @return An `ORDER BY` clause for the given fields
|
||||
*/
|
||||
fun orderBy(fields: List<Field<*>>, dialect: Dialect): String {
|
||||
fun orderBy(fields: Collection<Field<*>>, dialect: Dialect): String {
|
||||
if (fields.isEmpty()) return ""
|
||||
val orderFields = fields.joinToString(", ") {
|
||||
val (field, direction) =
|
||||
@ -165,6 +164,6 @@ object Query {
|
||||
}
|
||||
"$path${direction ?: ""}"
|
||||
}
|
||||
return "ORDER BY $orderFields"
|
||||
return " ORDER BY $orderFields"
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,159 @@ package solutions.bitbadger.documents.common
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class AutoIdTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Generates a UUID string")
|
||||
fun testGenerateUUID() {
|
||||
val generated = AutoId.generateUUID()
|
||||
assertNotNull(generated, "The UUID string should not have been null")
|
||||
assertEquals(32, generated.length, "The UUID should have been a 32-character string")
|
||||
fun generateUUID() {
|
||||
assertEquals(32, AutoId.generateUUID().length, "The UUID should have been a 32-character string")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Generates a random hex character string of an even length")
|
||||
fun generateRandomStringEven() {
|
||||
val result = AutoId.generateRandomString(8)
|
||||
assertEquals(8, result.length, "There should have been 8 characters in $result")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Generates a random hex character string of an odd length")
|
||||
fun generateRandomStringOdd() {
|
||||
val result = AutoId.generateRandomString(11)
|
||||
assertEquals(11, result.length, "There should have been 11 characters in $result")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Generates different random hex character strings")
|
||||
fun generateRandomStringIsRandom() {
|
||||
val result1 = AutoId.generateRandomString(16)
|
||||
val result2 = AutoId.generateRandomString(16)
|
||||
assertNotEquals(result1, result2, "There should have been 2 different strings generated")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId fails for null document")
|
||||
fun needsAutoIdFailsForNullDocument() {
|
||||
assertThrows<IllegalArgumentException> { AutoId.needsAutoId(AutoId.DISABLED, null, "id") }
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId fails for missing ID property")
|
||||
fun needsAutoIdFailsForMissingId() {
|
||||
assertThrows<IllegalArgumentException> { AutoId.needsAutoId(AutoId.UUID, IntIdClass(0), "Id") }
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false if disabled")
|
||||
fun needsAutoIdFalseIfDisabled() {
|
||||
assertFalse(AutoId.needsAutoId(AutoId.DISABLED, "", ""), "Disabled Auto ID should always return false")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for Number strategy and byte ID of 0")
|
||||
fun needsAutoIdTrueForByteWithZero() {
|
||||
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(0), "id"), "Number Auto ID with 0 should return true")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for Number strategy and byte ID of non-0")
|
||||
fun needsAutoIdFalseForByteWithNonZero() {
|
||||
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(77), "id"),
|
||||
"Number Auto ID with 77 should return false")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for Number strategy and short ID of 0")
|
||||
fun needsAutoIdTrueForShortWithZero() {
|
||||
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(0), "id"), "Number Auto ID with 0 should return true")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for Number strategy and short ID of non-0")
|
||||
fun needsAutoIdFalseForShortWithNonZero() {
|
||||
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(31), "id"),
|
||||
"Number Auto ID with 31 should return false")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for Number strategy and int ID of 0")
|
||||
fun needsAutoIdTrueForIntWithZero() {
|
||||
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(0), "id"), "Number Auto ID with 0 should return true")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for Number strategy and int ID of non-0")
|
||||
fun needsAutoIdFalseForIntWithNonZero() {
|
||||
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(6), "id"), "Number Auto ID with 6 should return false")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for Number strategy and long ID of 0")
|
||||
fun needsAutoIdTrueForLongWithZero() {
|
||||
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(0), "id"), "Number Auto ID with 0 should return true")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for Number strategy and long ID of non-0")
|
||||
fun needsAutoIdFalseForLongWithNonZero() {
|
||||
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(2), "id"), "Number Auto ID with 2 should return false")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId fails for Number strategy and non-number ID")
|
||||
fun needsAutoIdFailsForNumberWithStringId() {
|
||||
assertThrows<IllegalArgumentException> { AutoId.needsAutoId(AutoId.NUMBER, StringIdClass(""), "id") }
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for UUID strategy and blank ID")
|
||||
fun needsAutoIdTrueForUUIDWithBlank() {
|
||||
assertTrue(AutoId.needsAutoId(AutoId.UUID, StringIdClass(""), "id"),
|
||||
"UUID Auto ID with blank should return true")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for UUID strategy and non-blank ID")
|
||||
fun needsAutoIdFalseForUUIDNotBlank() {
|
||||
assertFalse(AutoId.needsAutoId(AutoId.UUID, StringIdClass("howdy"), "id"),
|
||||
"UUID Auto ID with non-blank should return false")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId fails for UUID strategy and non-string ID")
|
||||
fun needsAutoIdFailsForUUIDNonString() {
|
||||
assertThrows<IllegalArgumentException> { AutoId.needsAutoId(AutoId.UUID, IntIdClass(5), "id") }
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns true for Random String strategy and blank ID")
|
||||
fun needsAutoIdTrueForRandomWithBlank() {
|
||||
assertTrue(AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass(""), "id"),
|
||||
"Random String Auto ID with blank should return true")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId returns false for Random String strategy and non-blank ID")
|
||||
fun needsAutoIdFalseForRandomNotBlank() {
|
||||
assertFalse(AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass("full"), "id"),
|
||||
"Random String Auto ID with non-blank should return false")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("needsAutoId fails for Random String strategy and non-string ID")
|
||||
fun needsAutoIdFailsForRandomNonString() {
|
||||
assertThrows<IllegalArgumentException> { AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id") }
|
||||
}
|
||||
}
|
||||
|
||||
data class ByteIdClass(var id: Byte)
|
||||
data class ShortIdClass(var id: Short)
|
||||
data class IntIdClass(var id: Int)
|
||||
data class LongIdClass(var id: Long)
|
||||
data class StringIdClass(var id: String)
|
||||
|
26
src/common/src/test/kotlin/ConfigurationTest.kt
Normal file
26
src/common/src/test/kotlin/ConfigurationTest.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package solutions.bitbadger.documents.common
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ConfigurationTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Default ID field is `id`")
|
||||
fun defaultIdField() {
|
||||
assertEquals("id", Configuration.idField, "Default ID field incorrect")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Default Auto ID strategy is `DISABLED`")
|
||||
fun defaultAutoId() {
|
||||
assertEquals(AutoId.DISABLED, Configuration.autoIdStrategy, "Default Auto ID strategy should be `disabled`")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Default ID string length should be 16")
|
||||
fun defaultIdStringLength() {
|
||||
assertEquals(16, Configuration.idStringLength, "Default ID string length should be 16")
|
||||
}
|
||||
}
|
181
src/common/src/test/kotlin/QueryTest.kt
Normal file
181
src/common/src/test/kotlin/QueryTest.kt
Normal file
@ -0,0 +1,181 @@
|
||||
package solutions.bitbadger.documents.common
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class QueryTest {
|
||||
|
||||
/** Test table name */
|
||||
private val tbl = "test_table"
|
||||
|
||||
@Test
|
||||
@DisplayName("statementWhere generates correctly")
|
||||
fun statementWhere() {
|
||||
assertEquals("x WHERE y", Query.statementWhere("x", "y"), "Statements not combined correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Definition.ensureTableFor generates correctly")
|
||||
fun ensureTableFor() {
|
||||
assertEquals("CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)",
|
||||
Query.Definition.ensureTableFor("my.table", "JSONB"), "CREATE TABLE statement not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Definition.ensureKey generates correctly with schema")
|
||||
fun ensureKeyWithSchema() {
|
||||
assertEquals("CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'id'))",
|
||||
Query.Definition.ensureKey("test.table", Dialect.POSTGRESQL),
|
||||
"CREATE INDEX for key statement with schema not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Definition.ensureKey generates correctly without schema")
|
||||
fun ensureKeyWithoutSchema() {
|
||||
assertEquals("CREATE UNIQUE INDEX IF NOT EXISTS idx_${tbl}_key ON $tbl ((data->>'id'))",
|
||||
Query.Definition.ensureKey(tbl, Dialect.SQLITE),
|
||||
"CREATE INDEX for key statement without schema not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Definition.ensureIndexOn generates multiple fields and directions")
|
||||
fun ensureIndexOnMultipleFields() {
|
||||
assertEquals(
|
||||
"CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table ((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)",
|
||||
Query.Definition.ensureIndexOn("test.table", "gibberish", listOf("taco", "guac DESC", "salsa ASC"),
|
||||
Dialect.POSTGRESQL),
|
||||
"CREATE INDEX for multiple field statement not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Definition.ensureIndexOn generates nested PostgreSQL field")
|
||||
fun ensureIndexOnNestedPostgres() {
|
||||
assertEquals("CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data#>>'{a,b,c}'))",
|
||||
Query.Definition.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.POSTGRESQL),
|
||||
"CREATE INDEX for nested PostgreSQL field incorrect")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Definition.ensureIndexOn generates nested SQLite field")
|
||||
fun ensureIndexOnNestedSQLite() {
|
||||
assertEquals("CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data->'a'->'b'->>'c'))",
|
||||
Query.Definition.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.SQLITE),
|
||||
"CREATE INDEX for nested SQLite field incorrect")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("insert generates correctly")
|
||||
fun insert() {
|
||||
assertEquals("INSERT INTO $tbl VALUES (:data)", Query.insert(tbl), "INSERT statement not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("save generates correctly")
|
||||
fun save() {
|
||||
assertEquals("INSERT INTO $tbl VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data",
|
||||
Query.save(tbl), "INSERT ON CONFLICT UPDATE statement not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("count generates correctly")
|
||||
fun count() {
|
||||
assertEquals("SELECT COUNT(*) AS it FROM $tbl", Query.count(tbl), "Count query not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("exists generates correctly")
|
||||
fun exists() {
|
||||
assertEquals("SELECT EXISTS (SELECT 1 FROM $tbl WHERE turkey) AS it", Query.exists(tbl, "turkey"),
|
||||
"Exists query not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("find generates correctly")
|
||||
fun find() {
|
||||
assertEquals("SELECT data FROM $tbl", Query.find(tbl), "Find query not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("update generates successfully")
|
||||
fun update() {
|
||||
assertEquals("UPDATE $tbl SET data = :data", Query.update(tbl), "Update query not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("delete generates successfully")
|
||||
fun delete() {
|
||||
assertEquals("DELETE FROM $tbl", Query.delete(tbl), "Delete query not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates for no fields")
|
||||
fun orderByNone() {
|
||||
assertEquals("", Query.orderBy(listOf(), Dialect.POSTGRESQL), "ORDER BY should have been blank (PostgreSQL)")
|
||||
assertEquals("", Query.orderBy(listOf(), Dialect.SQLITE), "ORDER BY should have been blank (SQLite)")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates single, no direction for PostgreSQL")
|
||||
fun orderBySinglePostgres() {
|
||||
assertEquals(" ORDER BY data->>'TestField'",
|
||||
Query.orderBy(listOf(Field.named("TestField")), Dialect.POSTGRESQL), "ORDER BY not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates single, no direction for SQLite")
|
||||
fun orderBySingleSQLite() {
|
||||
assertEquals(" ORDER BY data->>'TestField'", Query.orderBy(listOf(Field.named("TestField")), Dialect.SQLITE),
|
||||
"ORDER BY not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates multiple with direction for PostgreSQL")
|
||||
fun orderByMultiplePostgres() {
|
||||
assertEquals(" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
|
||||
Query.orderBy(
|
||||
listOf(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")),
|
||||
Dialect.POSTGRESQL),
|
||||
"ORDER BY not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates multiple with direction for SQLite")
|
||||
fun orderByMultipleSQLite() {
|
||||
assertEquals(" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
|
||||
Query.orderBy(
|
||||
listOf(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")),
|
||||
Dialect.SQLITE),
|
||||
"ORDER BY not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates numeric ordering PostgreSQL")
|
||||
fun orderByNumericPostgres() {
|
||||
assertEquals(" ORDER BY (data->>'Test')::numeric",
|
||||
Query.orderBy(listOf(Field.named("n:Test")), Dialect.POSTGRESQL), "ORDER BY not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates numeric ordering for SQLite")
|
||||
fun orderByNumericSQLite() {
|
||||
assertEquals(" ORDER BY data->>'Test'", Query.orderBy(listOf(Field.named("n:Test")), Dialect.SQLITE),
|
||||
"ORDER BY not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates case-insensitive ordering for PostgreSQL")
|
||||
fun orderByCIPostgres() {
|
||||
assertEquals(" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST",
|
||||
Query.orderBy(listOf(Field.named("i:Test.Field DESC NULLS FIRST")), Dialect.POSTGRESQL),
|
||||
"ORDER BY not constructed correctly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("orderBy generates case-insensitive ordering for SQLite")
|
||||
fun orderByCISQLite() {
|
||||
assertEquals(" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST",
|
||||
Query.orderBy(listOf(Field.named("i:Test.Field ASC NULLS LAST")), Dialect.SQLITE),
|
||||
"ORDER BY not constructed correctly")
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user