Initial Development #1

Merged
danieljsummers merged 88 commits from v1-rc into main 2025-04-16 01:29:20 +00:00
4 changed files with 235 additions and 15 deletions
Showing only changes of commit 448a9646d6 - Show all commits

View File

@ -80,9 +80,11 @@ object Parameters {
is Long -> stmt.setLong(idx, param.value) is Long -> stmt.setLong(idx, param.value)
else -> throw DocumentException( else -> throw DocumentException(
"Number parameter must be Byte, Short, Int, or Long " + "Number parameter must be Byte, Short, Int, or Long " +
"(${param.value::class.simpleName})") "(${param.value::class.simpleName})"
)
} }
} }
ParameterType.STRING -> { ParameterType.STRING -> {
when (param.value) { when (param.value) {
null -> stmt.setNull(idx, Types.NULL) null -> stmt.setNull(idx, Types.NULL)
@ -90,6 +92,7 @@ object Parameters {
else -> stmt.setString(idx, param.value.toString()) else -> stmt.setString(idx, param.value.toString())
} }
} }
ParameterType.JSON -> stmt.setString(idx, Configuration.json.encodeToString(param.value)) ParameterType.JSON -> stmt.setString(idx, Configuration.json.encodeToString(param.value))
} }
} }
@ -108,11 +111,10 @@ object Parameters {
*/ */
fun fieldNames(names: Collection<String>, parameterName: String = ":name") = fun fieldNames(names: Collection<String>, parameterName: String = ":name") =
when (Configuration.dialect("generate field name parameters")) { when (Configuration.dialect("generate field name parameters")) {
Dialect.POSTGRESQL -> listOf(Parameter(parameterName, ParameterType.STRING, if (names.size == 1) { Dialect.POSTGRESQL -> listOf(
names.elementAt(0) Parameter(parameterName, ParameterType.STRING, names.joinToString(",").let { "{$it}" })
} else { )
names.joinToString(",").let { "{$it}" }
}))
Dialect.SQLITE -> names.mapIndexed { index, name -> Dialect.SQLITE -> names.mapIndexed { index, name ->
Parameter("$parameterName$index", ParameterType.STRING, name) Parameter("$parameterName$index", ParameterType.STRING, name)
} }

View File

@ -2,6 +2,7 @@ package solutions.bitbadger.documents
import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.assertThrows
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotSame import kotlin.test.assertNotSame
@ -14,7 +15,7 @@ import kotlin.test.assertSame
class ParametersTest { class ParametersTest {
/** /**
* Clear the connection string (resets Dialect) * Reset the dialect
*/ */
@AfterEach @AfterEach
fun cleanUp() { fun cleanUp() {
@ -65,7 +66,7 @@ class ParametersTest {
assertEquals(1, nameParams.size, "There should be one name parameter") assertEquals(1, nameParams.size, "There should be one name parameter")
assertEquals(":name", nameParams[0].name, "The parameter name is incorrect") assertEquals(":name", nameParams[0].name, "The parameter name is incorrect")
assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect") assertEquals(ParameterType.STRING, nameParams[0].type, "The parameter type is incorrect")
assertEquals("test", nameParams[0].value, "The parameter value is incorrect") assertEquals("{test}", nameParams[0].value, "The parameter value is incorrect")
} }
@Test @Test
@ -106,4 +107,10 @@ class ParametersTest {
assertEquals(ParameterType.STRING, nameParams[2].type, "The third parameter type is incorrect") assertEquals(ParameterType.STRING, nameParams[2].type, "The third parameter type is incorrect")
assertEquals("today", nameParams[2].value, "The third parameter value is incorrect") assertEquals("today", nameParams[2].value, "The third parameter value is incorrect")
} }
@Test
@DisplayName("fieldNames fails if dialect not set")
fun fieldNamesFails() {
assertThrows<DocumentException> { Parameters.fieldNames(listOf()) }
}
} }

View File

@ -0,0 +1,105 @@
package solutions.bitbadger.documents.query
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field
import kotlin.test.assertEquals
/**
* Unit tests for the `Patch` object
*/
@DisplayName("Patch (Query)")
class PatchTest {
/** Test table name */
private val tbl = "test_table"
/**
* Reset the dialect
*/
@AfterEach
fun cleanUp() {
Configuration.dialectValue = null
}
@Test
@DisplayName("byId generates correctly (PostgreSQL)")
fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data->>'id' = :id",
Patch.byId<String>(tbl), "Patch query not constructed correctly"
)
}
@Test
@DisplayName("byId generates correctly (SQLite)")
fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id",
Patch.byId<String>(tbl), "Patch query not constructed correctly"
)
}
@Test
@DisplayName("byFields generates correctly (PostgreSQL)")
fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data->>'z' = :y",
Patch.byFields(tbl, listOf(Field.equal("z", "", ":y"))),
"Patch query not constructed correctly"
)
}
@Test
@DisplayName("byFields generates correctly (SQLite)")
fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y",
Patch.byFields(tbl, listOf(Field.equal("z", "", ":y"))),
"Patch query not constructed correctly"
)
}
@Test
@DisplayName("byContains generates correctly (PostgreSQL)")
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data @> :criteria", Patch.byContains(tbl),
"Patch query not constructed correctly"
)
}
@Test
@DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Patch.byContains(tbl) }
}
@Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)")
fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"UPDATE $tbl SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)",
Patch.byJsonPath(tbl), "Patch query not constructed correctly"
)
}
@Test
@DisplayName("byJsonPath fails (SQLite)")
fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { Patch.byJsonPath(tbl) }
}
}

View File

@ -0,0 +1,106 @@
package solutions.bitbadger.documents.query
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.*
import kotlin.test.assertEquals
/**
* Unit tests for the `RemoveFields` object
*/
@DisplayName("RemoveFields (Query)")
class RemoveFieldsTest {
/** Test table name */
private val tbl = "test_table"
/**
* Reset the dialect
*/
@AfterEach
fun cleanUp() {
Configuration.dialectValue = null
}
@Test
@DisplayName("byId generates correctly (PostgreSQL)")
fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data->>'id' = :id",
RemoveFields.byId<String>(tbl, Parameters.fieldNames(listOf("a", "z"))),
"Remove Fields query not constructed correctly"
)
}
@Test
@DisplayName("byId generates correctly (SQLite)")
fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id",
RemoveFields.byId<String>(tbl, Parameters.fieldNames(listOf("a", "z"))),
"Remove Field query not constructed correctly"
)
}
@Test
@DisplayName("byFields generates correctly (PostgreSQL)")
fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data->>'f' > :g",
RemoveFields.byFields(tbl, Parameters.fieldNames(listOf("b", "c")), listOf(Field.greater("f", "", ":g"))),
"Remove Field query not constructed correctly"
)
}
@Test
@DisplayName("byFields generates correctly (SQLite)")
fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertEquals(
"UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g",
RemoveFields.byFields(tbl, Parameters.fieldNames(listOf("b", "c")), listOf(Field.greater("f", "", ":g"))),
"Remove Field query not constructed correctly"
)
}
@Test
@DisplayName("byContains generates correctly (PostgreSQL)")
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data @> :criteria",
RemoveFields.byContains(tbl, Parameters.fieldNames(listOf("m", "n"))),
"Remove Field query not constructed correctly"
)
}
@Test
@DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { RemoveFields.byContains(tbl, listOf()) }
}
@Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)")
fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)",
RemoveFields.byJsonPath(tbl, Parameters.fieldNames(listOf("o", "p"))),
"Remove Field query not constructed correctly"
)
}
@Test
@DisplayName("byJsonPath fails (SQLite)")
fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { RemoveFields.byJsonPath(tbl, listOf()) }
}
}