Initial Development #1

Merged
danieljsummers merged 88 commits from v1-rc into main 2025-04-16 01:29:20 +00:00
24 changed files with 1166 additions and 334 deletions
Showing only changes of commit 9f7f2d7859 - Show all commits

View File

@ -1,5 +1,7 @@
package solutions.bitbadger.documents package solutions.bitbadger.documents
import kotlin.jvm.Throws
/** /**
* A field and its comparison * A field and its comparison
* *
@ -44,6 +46,7 @@ class Field<T> private constructor(
* @param format Whether the value should be retrieved as JSON or SQL (optional, default SQL) * @param format Whether the value should be retrieved as JSON or SQL (optional, default SQL)
* @return The path for the field * @return The path for the field
*/ */
@JvmOverloads
fun path(dialect: Dialect, format: FieldFormat = FieldFormat.SQL): String = fun path(dialect: Dialect, format: FieldFormat = FieldFormat.SQL): String =
(if (qualifier == null) "" else "${qualifier}.") + nameToPath(name, dialect, format) (if (qualifier == null) "" else "${qualifier}.") + nameToPath(name, dialect, format)
@ -65,6 +68,7 @@ class Field<T> private constructor(
* @return The `WHERE` clause for this field * @return The `WHERE` clause for this field
* @throws DocumentException If the field has no parameter name or the database dialect has not been set * @throws DocumentException If the field has no parameter name or the database dialect has not been set
*/ */
@Throws(DocumentException::class)
fun toWhere(): String { fun toWhere(): String {
if (parameterName == null && !listOf(Op.EXISTS, Op.NOT_EXISTS).contains(comparison.op)) if (parameterName == null && !listOf(Op.EXISTS, Op.NOT_EXISTS).contains(comparison.op))
throw DocumentException("Parameter for $name must be specified") throw DocumentException("Parameter for $name must be specified")

View File

@ -28,7 +28,9 @@ object CountQuery {
* @param fields The field comparisons for the count * @param fields The field comparisons for the count
* @param howMatched How fields should be compared (optional, defaults to ALL) * @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to count documents matching the given fields * @return A query to count documents matching the given fields
* @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class)
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =

View File

@ -27,7 +27,9 @@ object DeleteQuery {
* @param tableName The table from which documents should be deleted (may include schema) * @param tableName The table from which documents should be deleted (may include schema)
* @param docId The ID of the document (optional, used for type checking) * @param docId The ID of the document (optional, used for type checking)
* @return A query to delete a document by its ID * @return A query to delete a document by its ID
* @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class)
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun <TKey> byId(tableName: String, docId: TKey? = null) = fun <TKey> byId(tableName: String, docId: TKey? = null) =
@ -40,7 +42,9 @@ object DeleteQuery {
* @param fields The field comparisons for documents to be deleted * @param fields The field comparisons for documents to be deleted
* @param howMatched How fields should be compared (optional, defaults to ALL) * @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to delete documents matching for the given fields * @return A query to delete documents matching for the given fields
* @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class)
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =

View File

@ -26,7 +26,9 @@ object ExistsQuery {
* @param tableName The table in which existence should be checked (may include schema) * @param tableName The table in which existence should be checked (may include schema)
* @param docId The ID of the document (optional, used for type checking) * @param docId The ID of the document (optional, used for type checking)
* @return A query to determine document existence by ID * @return A query to determine document existence by ID
* @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class)
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun <TKey> byId(tableName: String, docId: TKey? = null) = fun <TKey> byId(tableName: String, docId: TKey? = null) =
@ -39,7 +41,9 @@ object ExistsQuery {
* @param fields The field comparisons for the existence check * @param fields The field comparisons for the existence check
* @param howMatched How fields should be compared (optional, defaults to ALL) * @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to determine document existence for the given fields * @return A query to determine document existence for the given fields
* @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class)
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =

View File

@ -28,7 +28,9 @@ object FindQuery {
* @param tableName The table from which documents should be retrieved (may include schema) * @param tableName The table from which documents should be retrieved (may include schema)
* @param docId The ID of the document (optional, used for type checking) * @param docId The ID of the document (optional, used for type checking)
* @return A query to retrieve a document by its ID * @return A query to retrieve a document by its ID
* @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class)
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun <TKey> byId(tableName: String, docId: TKey? = null) = fun <TKey> byId(tableName: String, docId: TKey? = null) =
@ -41,7 +43,9 @@ object FindQuery {
* @param fields The field comparisons for matching documents to retrieve * @param fields The field comparisons for matching documents to retrieve
* @param howMatched How fields should be compared (optional, defaults to ALL) * @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to retrieve documents matching the given fields * @return A query to retrieve documents matching the given fields
* @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class)
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =

View File

@ -1,10 +1,8 @@
@file:JvmName("QueryUtils") @file:JvmName("QueryUtils")
package solutions.bitbadger.documents.query package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.Dialect import kotlin.jvm.Throws
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch
// ~~~ TOP-LEVEL FUNCTIONS FOR THE QUERY PACKAGE ~~~ // ~~~ TOP-LEVEL FUNCTIONS FOR THE QUERY PACKAGE ~~~
@ -24,7 +22,9 @@ fun statementWhere(statement: String, where: String) =
* @param statement The SQL statement to be run against a document by its ID * @param statement The SQL statement to be run against a document by its ID
* @param docId The ID of the document targeted * @param docId The ID of the document targeted
* @returns A query addressing a document by its ID * @returns A query addressing a document by its ID
* @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class)
fun <TKey> byId(statement: String, docId: TKey) = fun <TKey> byId(statement: String, docId: TKey) =
statementWhere(statement, Where.byId(docId = docId)) statementWhere(statement, Where.byId(docId = docId))
@ -36,6 +36,7 @@ fun <TKey> byId(statement: String, docId: TKey) =
* @param howMatched Whether to match any or all of the field conditions (optional; default ALL) * @param howMatched Whether to match any or all of the field conditions (optional; default ALL)
* @return A query addressing documents by field matching conditions * @return A query addressing documents by field matching conditions
*/ */
@Throws(DocumentException::class)
@JvmOverloads @JvmOverloads
fun byFields(statement: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(statement: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
statementWhere(statement, Where.byFields(fields, howMatched)) statementWhere(statement, Where.byFields(fields, howMatched))
@ -47,6 +48,7 @@ fun byFields(statement: String, fields: Collection<Field<*>>, howMatched: FieldM
* @param dialect The SQL dialect for the generated clause * @param dialect The SQL dialect for the generated clause
* @return An `ORDER BY` clause for the given fields * @return An `ORDER BY` clause for the given fields
*/ */
@Throws(DocumentException::class)
@JvmOverloads @JvmOverloads
fun orderBy(fields: Collection<Field<*>>, dialect: Dialect? = null): String { fun orderBy(fields: Collection<Field<*>>, dialect: Dialect? = null): String {
val mode = dialect ?: Configuration.dialect("generate ORDER BY clause") val mode = dialect ?: Configuration.dialect("generate ORDER BY clause")

View File

@ -18,7 +18,9 @@ object Where {
* @param fields The fields to be queried * @param fields The fields to be queried
* @param howMatched How the fields should be matched (optional, defaults to `ALL`) * @param howMatched How the fields should be matched (optional, defaults to `ALL`)
* @return A `WHERE` clause fragment to match the given fields * @return A `WHERE` clause fragment to match the given fields
* @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class)
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun byFields(fields: Collection<Field<*>>, howMatched: FieldMatch? = null) = fun byFields(fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
@ -29,7 +31,10 @@ object Where {
* *
* @param parameterName The parameter name to use for the ID placeholder (optional, defaults to ":id") * @param parameterName The parameter name to use for the ID placeholder (optional, defaults to ":id")
* @param docId The ID value (optional; used for type determinations, string assumed if not provided) * @param docId The ID value (optional; used for type determinations, string assumed if not provided)
* @return A `WHERE` clause fragment to match the document's ID
* @throws DocumentException If the dialect has not been set
*/ */
@Throws(DocumentException::class)
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun <TKey> byId(parameterName: String = ":id", docId: TKey? = null) = fun <TKey> byId(parameterName: String = ":id", docId: TKey? = null) =

View File

@ -5,6 +5,7 @@ import org.junit.jupiter.api.AfterEach;
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 solutions.bitbadger.documents.*; import solutions.bitbadger.documents.*;
import solutions.bitbadger.documents.support.ForceDialect;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -22,7 +23,7 @@ final public class FieldTest {
*/ */
@AfterEach @AfterEach
public void cleanUp() { public void cleanUp() {
Configuration.setConnectionString(null); ForceDialect.none();
} }
// ~~~ INSTANCE METHODS ~~~ // ~~~ INSTANCE METHODS ~~~
@ -71,7 +72,7 @@ final public class FieldTest {
} }
@Test @Test
@DisplayName("path generates for simple unqualified PostgreSQL field") @DisplayName("path generates for simple unqualified field | PostgreSQL")
public void pathPostgresSimpleUnqualified() { public void pathPostgresSimpleUnqualified() {
assertEquals("data->>'SomethingCool'", assertEquals("data->>'SomethingCool'",
Field.greaterOrEqual("SomethingCool", 18).path(Dialect.POSTGRESQL, FieldFormat.SQL), Field.greaterOrEqual("SomethingCool", 18).path(Dialect.POSTGRESQL, FieldFormat.SQL),
@ -79,7 +80,7 @@ final public class FieldTest {
} }
@Test @Test
@DisplayName("path generates for simple qualified PostgreSQL field") @DisplayName("path generates for simple qualified field | PostgreSQL")
public void pathPostgresSimpleQualified() { public void pathPostgresSimpleQualified() {
assertEquals("this.data->>'SomethingElse'", assertEquals("this.data->>'SomethingElse'",
Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.POSTGRESQL, FieldFormat.SQL), Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.POSTGRESQL, FieldFormat.SQL),
@ -87,14 +88,14 @@ final public class FieldTest {
} }
@Test @Test
@DisplayName("path generates for nested unqualified PostgreSQL field") @DisplayName("path generates for nested unqualified field | PostgreSQL")
public void pathPostgresNestedUnqualified() { public void pathPostgresNestedUnqualified() {
assertEquals("data#>>'{My,Nested,Field}'", assertEquals("data#>>'{My,Nested,Field}'",
Field.equal("My.Nested.Field", "howdy").path(Dialect.POSTGRESQL, FieldFormat.SQL), "Path not correct"); Field.equal("My.Nested.Field", "howdy").path(Dialect.POSTGRESQL, FieldFormat.SQL), "Path not correct");
} }
@Test @Test
@DisplayName("path generates for nested qualified PostgreSQL field") @DisplayName("path generates for nested qualified field | PostgreSQL")
public void pathPostgresNestedQualified() { public void pathPostgresNestedQualified() {
assertEquals("bird.data#>>'{Nest,Away}'", assertEquals("bird.data#>>'{Nest,Away}'",
Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.POSTGRESQL, FieldFormat.SQL), Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.POSTGRESQL, FieldFormat.SQL),
@ -102,14 +103,14 @@ final public class FieldTest {
} }
@Test @Test
@DisplayName("path generates for simple unqualified SQLite field") @DisplayName("path generates for simple unqualified field | SQLite")
public void pathSQLiteSimpleUnqualified() { public void pathSQLiteSimpleUnqualified() {
assertEquals("data->>'SomethingCool'", assertEquals("data->>'SomethingCool'",
Field.greaterOrEqual("SomethingCool", 18).path(Dialect.SQLITE, FieldFormat.SQL), "Path not correct"); Field.greaterOrEqual("SomethingCool", 18).path(Dialect.SQLITE, FieldFormat.SQL), "Path not correct");
} }
@Test @Test
@DisplayName("path generates for simple qualified SQLite field") @DisplayName("path generates for simple qualified field | SQLite")
public void pathSQLiteSimpleQualified() { public void pathSQLiteSimpleQualified() {
assertEquals("this.data->>'SomethingElse'", assertEquals("this.data->>'SomethingElse'",
Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.SQLITE, FieldFormat.SQL), Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.SQLITE, FieldFormat.SQL),
@ -117,14 +118,14 @@ final public class FieldTest {
} }
@Test @Test
@DisplayName("path generates for nested unqualified SQLite field") @DisplayName("path generates for nested unqualified field | SQLite")
public void pathSQLiteNestedUnqualified() { public void pathSQLiteNestedUnqualified() {
assertEquals("data->'My'->'Nested'->>'Field'", assertEquals("data->'My'->'Nested'->>'Field'",
Field.equal("My.Nested.Field", "howdy").path(Dialect.SQLITE, FieldFormat.SQL), "Path not correct"); Field.equal("My.Nested.Field", "howdy").path(Dialect.SQLITE, FieldFormat.SQL), "Path not correct");
} }
@Test @Test
@DisplayName("path generates for nested qualified SQLite field") @DisplayName("path generates for nested qualified field | SQLite")
public void pathSQLiteNestedQualified() { public void pathSQLiteNestedQualified() {
assertEquals("bird.data->'Nest'->>'Away'", assertEquals("bird.data->'Nest'->>'Away'",
Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.SQLITE, FieldFormat.SQL), Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.SQLITE, FieldFormat.SQL),
@ -132,190 +133,196 @@ final public class FieldTest {
} }
@Test @Test
@DisplayName("toWhere generates for exists w/o qualifier (PostgreSQL)") @DisplayName("toWhere generates for exists w/o qualifier | PostgreSQL")
public void toWhereExistsNoQualPostgres() { public void toWhereExistsNoQualPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("data->>'that_field' IS NOT NULL", Field.exists("that_field").toWhere(), assertEquals("data->>'that_field' IS NOT NULL", Field.exists("that_field").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for exists w/o qualifier (SQLite)") @DisplayName("toWhere generates for exists w/o qualifier | SQLite")
public void toWhereExistsNoQualSQLite() { public void toWhereExistsNoQualSQLite() throws DocumentException {
Configuration.setConnectionString(":sqlite:"); ForceDialect.sqlite();
assertEquals("data->>'that_field' IS NOT NULL", Field.exists("that_field").toWhere(), assertEquals("data->>'that_field' IS NOT NULL", Field.exists("that_field").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for not-exists w/o qualifier (PostgreSQL)") @DisplayName("toWhere generates for not-exists w/o qualifier | PostgreSQL")
public void toWhereNotExistsNoQualPostgres() { public void toWhereNotExistsNoQualPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("data->>'a_field' IS NULL", Field.notExists("a_field").toWhere(), assertEquals("data->>'a_field' IS NULL", Field.notExists("a_field").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for not-exists w/o qualifier (SQLite)") @DisplayName("toWhere generates for not-exists w/o qualifier | SQLite")
public void toWhereNotExistsNoQualSQLite() { public void toWhereNotExistsNoQualSQLite() throws DocumentException {
Configuration.setConnectionString(":sqlite:"); ForceDialect.sqlite();
assertEquals("data->>'a_field' IS NULL", Field.notExists("a_field").toWhere(), assertEquals("data->>'a_field' IS NULL", Field.notExists("a_field").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for BETWEEN w/o qualifier, numeric range (PostgreSQL)") @DisplayName("toWhere generates for BETWEEN w/o qualifier, numeric range | PostgreSQL")
public void toWhereBetweenNoQualNumericPostgres() { public void toWhereBetweenNoQualNumericPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("(data->>'age')::numeric BETWEEN @agemin AND @agemax", assertEquals("(data->>'age')::numeric BETWEEN @agemin AND @agemax",
Field.between("age", 13, 17, "@age").toWhere(), "Field WHERE clause not generated correctly"); Field.between("age", 13, 17, "@age").toWhere(), "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for BETWEEN w/o qualifier, alphanumeric range (PostgreSQL)") @DisplayName("toWhere generates for BETWEEN w/o qualifier, alphanumeric range | PostgreSQL")
public void toWhereBetweenNoQualAlphaPostgres() { public void toWhereBetweenNoQualAlphaPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("data->>'city' BETWEEN :citymin AND :citymax", assertEquals("data->>'city' BETWEEN :citymin AND :citymax",
Field.between("city", "Atlanta", "Chicago", ":city").toWhere(), Field.between("city", "Atlanta", "Chicago", ":city").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for BETWEEN w/o qualifier (SQLite)") @DisplayName("toWhere generates for BETWEEN w/o qualifier | SQLite")
public void toWhereBetweenNoQualSQLite() { public void toWhereBetweenNoQualSQLite() throws DocumentException {
Configuration.setConnectionString(":sqlite:"); ForceDialect.sqlite();
assertEquals("data->>'age' BETWEEN @agemin AND @agemax", Field.between("age", 13, 17, "@age").toWhere(), assertEquals("data->>'age' BETWEEN @agemin AND @agemax", Field.between("age", 13, 17, "@age").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for BETWEEN w/ qualifier, numeric range (PostgreSQL)") @DisplayName("toWhere generates for BETWEEN w/ qualifier, numeric range | PostgreSQL")
public void toWhereBetweenQualNumericPostgres() { public void toWhereBetweenQualNumericPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("(test.data->>'age')::numeric BETWEEN @agemin AND @agemax", assertEquals("(test.data->>'age')::numeric BETWEEN @agemin AND @agemax",
Field.between("age", 13, 17, "@age").withQualifier("test").toWhere(), Field.between("age", 13, 17, "@age").withQualifier("test").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for BETWEEN w/ qualifier, alphanumeric range (PostgreSQL)") @DisplayName("toWhere generates for BETWEEN w/ qualifier, alphanumeric range | PostgreSQL")
public void toWhereBetweenQualAlphaPostgres() { public void toWhereBetweenQualAlphaPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("unit.data->>'city' BETWEEN :citymin AND :citymax", assertEquals("unit.data->>'city' BETWEEN :citymin AND :citymax",
Field.between("city", "Atlanta", "Chicago", ":city").withQualifier("unit").toWhere(), Field.between("city", "Atlanta", "Chicago", ":city").withQualifier("unit").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for BETWEEN w/ qualifier (SQLite)") @DisplayName("toWhere generates for BETWEEN w/ qualifier | SQLite")
public void toWhereBetweenQualSQLite() { public void toWhereBetweenQualSQLite() throws DocumentException {
Configuration.setConnectionString(":sqlite:"); ForceDialect.sqlite();
assertEquals("my.data->>'age' BETWEEN @agemin AND @agemax", assertEquals("my.data->>'age' BETWEEN @agemin AND @agemax",
Field.between("age", 13, 17, "@age").withQualifier("my").toWhere(), Field.between("age", 13, 17, "@age").withQualifier("my").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for IN/any, numeric values (PostgreSQL)") @DisplayName("toWhere generates for IN/any, numeric values | PostgreSQL")
public void toWhereAnyNumericPostgres() { public void toWhereAnyNumericPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)", assertEquals("(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)",
Field.any("even", List.of(2, 4, 6), ":nbr").toWhere(), Field.any("even", List.of(2, 4, 6), ":nbr").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for IN/any, alphanumeric values (PostgreSQL)") @DisplayName("toWhere generates for IN/any, alphanumeric values | PostgreSQL")
public void toWhereAnyAlphaPostgres() { public void toWhereAnyAlphaPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("data->>'test' IN (:city_0, :city_1)", assertEquals("data->>'test' IN (:city_0, :city_1)",
Field.any("test", List.of("Atlanta", "Chicago"), ":city").toWhere(), Field.any("test", List.of("Atlanta", "Chicago"), ":city").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for IN/any (SQLite)") @DisplayName("toWhere generates for IN/any | SQLite")
public void toWhereAnySQLite() { public void toWhereAnySQLite() throws DocumentException {
Configuration.setConnectionString(":sqlite:"); ForceDialect.sqlite();
assertEquals("data->>'test' IN (:city_0, :city_1)", assertEquals("data->>'test' IN (:city_0, :city_1)",
Field.any("test", List.of("Atlanta", "Chicago"), ":city").toWhere(), Field.any("test", List.of("Atlanta", "Chicago"), ":city").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for inArray (PostgreSQL)") @DisplayName("toWhere generates for inArray | PostgreSQL")
public void toWhereInArrayPostgres() { public void toWhereInArrayPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]", assertEquals("data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]",
Field.inArray("even", "tbl", List.of(2, 4, 6, 8), ":it").toWhere(), Field.inArray("even", "tbl", List.of(2, 4, 6, 8), ":it").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for inArray (SQLite)") @DisplayName("toWhere generates for inArray | SQLite")
public void toWhereInArraySQLite() { public void toWhereInArraySQLite() throws DocumentException {
Configuration.setConnectionString(":sqlite:"); ForceDialect.sqlite();
assertEquals("EXISTS (SELECT 1 FROM json_each(tbl.data, '$.test') WHERE value IN (:city_0, :city_1))", assertEquals("EXISTS (SELECT 1 FROM json_each(tbl.data, '$.test') WHERE value IN (:city_0, :city_1))",
Field.inArray("test", "tbl", List.of("Atlanta", "Chicago"), ":city").toWhere(), Field.inArray("test", "tbl", List.of("Atlanta", "Chicago"), ":city").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for others w/o qualifier (PostgreSQL)") @DisplayName("toWhere generates for others w/o qualifier | PostgreSQL")
public void toWhereOtherNoQualPostgres() { public void toWhereOtherNoQualPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("data->>'some_field' = :value", Field.equal("some_field", "", ":value").toWhere(), assertEquals("data->>'some_field' = :value", Field.equal("some_field", "", ":value").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates for others w/o qualifier (SQLite)") @DisplayName("toWhere generates for others w/o qualifier | SQLite")
public void toWhereOtherNoQualSQLite() { public void toWhereOtherNoQualSQLite() throws DocumentException {
Configuration.setConnectionString(":sqlite:"); ForceDialect.sqlite();
assertEquals("data->>'some_field' = :value", Field.equal("some_field", "", ":value").toWhere(), assertEquals("data->>'some_field' = :value", Field.equal("some_field", "", ":value").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates no-parameter w/ qualifier (PostgreSQL)") @DisplayName("toWhere generates no-parameter w/ qualifier | PostgreSQL")
public void toWhereNoParamWithQualPostgres() { public void toWhereNoParamWithQualPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("test.data->>'no_field' IS NOT NULL", Field.exists("no_field").withQualifier("test").toWhere(), assertEquals("test.data->>'no_field' IS NOT NULL", Field.exists("no_field").withQualifier("test").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates no-parameter w/ qualifier (SQLite)") @DisplayName("toWhere generates no-parameter w/ qualifier | SQLite")
public void toWhereNoParamWithQualSQLite() { public void toWhereNoParamWithQualSQLite() throws DocumentException {
Configuration.setConnectionString(":sqlite:"); ForceDialect.sqlite();
assertEquals("test.data->>'no_field' IS NOT NULL", Field.exists("no_field").withQualifier("test").toWhere(), assertEquals("test.data->>'no_field' IS NOT NULL", Field.exists("no_field").withQualifier("test").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates parameter w/ qualifier (PostgreSQL)") @DisplayName("toWhere generates parameter w/ qualifier | PostgreSQL")
public void toWhereParamWithQualPostgres() { public void toWhereParamWithQualPostgres() throws DocumentException {
Configuration.setConnectionString(":postgresql:"); ForceDialect.postgres();
assertEquals("(q.data->>'le_field')::numeric <= :it", assertEquals("(q.data->>'le_field')::numeric <= :it",
Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere(), Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test @Test
@DisplayName("toWhere generates parameter w/ qualifier (SQLite)") @DisplayName("toWhere generates parameter w/ qualifier | SQLite")
public void toWhereParamWithQualSQLite() { public void toWhereParamWithQualSQLite() throws DocumentException {
Configuration.setConnectionString(":sqlite:"); ForceDialect.sqlite();
assertEquals("q.data->>'le_field' <= :it", assertEquals("q.data->>'le_field' <= :it",
Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere(), Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere(),
"Field WHERE clause not generated correctly"); "Field WHERE clause not generated correctly");
} }
@Test
@DisplayName("toWhere fails when dialect is not set")
public void toWhereFailsNoDialect() {
assertThrows(DocumentException.class, () -> Field.equal("a", "oops").toWhere());
}
// ~~~ STATIC TESTS ~~~ // ~~~ STATIC TESTS ~~~
@Test @Test
@DisplayName("equal constructs a field w/o parameter name") @DisplayName("equal constructs a field w/o parameter name")
public void equalCtor() { public void equalCtor() {
Field<Integer> field = Field.equal("Test", 14); final Field<Integer> field = Field.equal("Test", 14);
assertEquals("Test", field.getName(), "Field name not filled correctly"); assertEquals("Test", field.getName(), "Field name not filled correctly");
assertEquals(Op.EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(14, field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals(14, field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -326,7 +333,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("equal constructs a field w/ parameter name") @DisplayName("equal constructs a field w/ parameter name")
public void equalParameterCtor() { public void equalParameterCtor() {
Field<Integer> field = Field.equal("Test", 14, ":w"); final Field<Integer> field = Field.equal("Test", 14, ":w");
assertEquals("Test", field.getName(), "Field name not filled correctly"); assertEquals("Test", field.getName(), "Field name not filled correctly");
assertEquals(Op.EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(14, field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals(14, field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -337,7 +344,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("greater constructs a field w/o parameter name") @DisplayName("greater constructs a field w/o parameter name")
public void greaterCtor() { public void greaterCtor() {
Field<String> field = Field.greater("Great", "night"); final Field<String> field = Field.greater("Great", "night");
assertEquals("Great", field.getName(), "Field name not filled correctly"); assertEquals("Great", field.getName(), "Field name not filled correctly");
assertEquals(Op.GREATER, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.GREATER, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("night", field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals("night", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -348,7 +355,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("greater constructs a field w/ parameter name") @DisplayName("greater constructs a field w/ parameter name")
public void greaterParameterCtor() { public void greaterParameterCtor() {
Field<String> field = Field.greater("Great", "night", ":yeah"); final Field<String> field = Field.greater("Great", "night", ":yeah");
assertEquals("Great", field.getName(), "Field name not filled correctly"); assertEquals("Great", field.getName(), "Field name not filled correctly");
assertEquals(Op.GREATER, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.GREATER, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("night", field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals("night", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -359,7 +366,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("greaterOrEqual constructs a field w/o parameter name") @DisplayName("greaterOrEqual constructs a field w/o parameter name")
public void greaterOrEqualCtor() { public void greaterOrEqualCtor() {
Field<Long> field = Field.greaterOrEqual("Nice", 88L); final Field<Long> field = Field.greaterOrEqual("Nice", 88L);
assertEquals("Nice", field.getName(), "Field name not filled correctly"); assertEquals("Nice", field.getName(), "Field name not filled correctly");
assertEquals(Op.GREATER_OR_EQUAL, field.getComparison().getOp(), assertEquals(Op.GREATER_OR_EQUAL, field.getComparison().getOp(),
"Field comparison operation not filled correctly"); "Field comparison operation not filled correctly");
@ -371,7 +378,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("greaterOrEqual constructs a field w/ parameter name") @DisplayName("greaterOrEqual constructs a field w/ parameter name")
public void greaterOrEqualParameterCtor() { public void greaterOrEqualParameterCtor() {
Field<Long> field = Field.greaterOrEqual("Nice", 88L, ":nice"); final Field<Long> field = Field.greaterOrEqual("Nice", 88L, ":nice");
assertEquals("Nice", field.getName(), "Field name not filled correctly"); assertEquals("Nice", field.getName(), "Field name not filled correctly");
assertEquals(Op.GREATER_OR_EQUAL, field.getComparison().getOp(), assertEquals(Op.GREATER_OR_EQUAL, field.getComparison().getOp(),
"Field comparison operation not filled correctly"); "Field comparison operation not filled correctly");
@ -383,7 +390,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("less constructs a field w/o parameter name") @DisplayName("less constructs a field w/o parameter name")
public void lessCtor() { public void lessCtor() {
Field<String> field = Field.less("Lesser", "seven"); final Field<String> field = Field.less("Lesser", "seven");
assertEquals("Lesser", field.getName(), "Field name not filled correctly"); assertEquals("Lesser", field.getName(), "Field name not filled correctly");
assertEquals(Op.LESS, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.LESS, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("seven", field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals("seven", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -394,7 +401,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("less constructs a field w/ parameter name") @DisplayName("less constructs a field w/ parameter name")
public void lessParameterCtor() { public void lessParameterCtor() {
Field<String> field = Field.less("Lesser", "seven", ":max"); final Field<String> field = Field.less("Lesser", "seven", ":max");
assertEquals("Lesser", field.getName(), "Field name not filled correctly"); assertEquals("Lesser", field.getName(), "Field name not filled correctly");
assertEquals(Op.LESS, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.LESS, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("seven", field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals("seven", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -405,7 +412,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("lessOrEqual constructs a field w/o parameter name") @DisplayName("lessOrEqual constructs a field w/o parameter name")
public void lessOrEqualCtor() { public void lessOrEqualCtor() {
Field<String> field = Field.lessOrEqual("Nobody", "KNOWS"); final Field<String> field = Field.lessOrEqual("Nobody", "KNOWS");
assertEquals("Nobody", field.getName(), "Field name not filled correctly"); assertEquals("Nobody", field.getName(), "Field name not filled correctly");
assertEquals(Op.LESS_OR_EQUAL, field.getComparison().getOp(), assertEquals(Op.LESS_OR_EQUAL, field.getComparison().getOp(),
"Field comparison operation not filled correctly"); "Field comparison operation not filled correctly");
@ -417,7 +424,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("lessOrEqual constructs a field w/ parameter name") @DisplayName("lessOrEqual constructs a field w/ parameter name")
public void lessOrEqualParameterCtor() { public void lessOrEqualParameterCtor() {
Field<String> field = Field.lessOrEqual("Nobody", "KNOWS", ":nope"); final Field<String> field = Field.lessOrEqual("Nobody", "KNOWS", ":nope");
assertEquals("Nobody", field.getName(), "Field name not filled correctly"); assertEquals("Nobody", field.getName(), "Field name not filled correctly");
assertEquals(Op.LESS_OR_EQUAL, field.getComparison().getOp(), assertEquals(Op.LESS_OR_EQUAL, field.getComparison().getOp(),
"Field comparison operation not filled correctly"); "Field comparison operation not filled correctly");
@ -429,7 +436,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("notEqual constructs a field w/o parameter name") @DisplayName("notEqual constructs a field w/o parameter name")
public void notEqualCtor() { public void notEqualCtor() {
Field<String> field = Field.notEqual("Park", "here"); final Field<String> field = Field.notEqual("Park", "here");
assertEquals("Park", field.getName(), "Field name not filled correctly"); assertEquals("Park", field.getName(), "Field name not filled correctly");
assertEquals(Op.NOT_EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.NOT_EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("here", field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals("here", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -440,7 +447,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("notEqual constructs a field w/ parameter name") @DisplayName("notEqual constructs a field w/ parameter name")
public void notEqualParameterCtor() { public void notEqualParameterCtor() {
Field<String> field = Field.notEqual("Park", "here", ":now"); final Field<String> field = Field.notEqual("Park", "here", ":now");
assertEquals("Park", field.getName(), "Field name not filled correctly"); assertEquals("Park", field.getName(), "Field name not filled correctly");
assertEquals(Op.NOT_EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.NOT_EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("here", field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals("here", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -451,7 +458,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("between constructs a field w/o parameter name") @DisplayName("between constructs a field w/o parameter name")
public void betweenCtor() { public void betweenCtor() {
Field<Pair<Integer, Integer>> field = Field.between("Age", 18, 49); final Field<Pair<Integer, Integer>> field = Field.between("Age", 18, 49);
assertEquals("Age", field.getName(), "Field name not filled correctly"); assertEquals("Age", field.getName(), "Field name not filled correctly");
assertEquals(Op.BETWEEN, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.BETWEEN, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(18, field.getComparison().getValue().getFirst(), assertEquals(18, field.getComparison().getValue().getFirst(),
@ -465,7 +472,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("between constructs a field w/ parameter name") @DisplayName("between constructs a field w/ parameter name")
public void betweenParameterCtor() { public void betweenParameterCtor() {
Field<Pair<Integer, Integer>> field = Field.between("Age", 18, 49, ":limit"); final Field<Pair<Integer, Integer>> field = Field.between("Age", 18, 49, ":limit");
assertEquals("Age", field.getName(), "Field name not filled correctly"); assertEquals("Age", field.getName(), "Field name not filled correctly");
assertEquals(Op.BETWEEN, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.BETWEEN, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(18, field.getComparison().getValue().getFirst(), assertEquals(18, field.getComparison().getValue().getFirst(),
@ -479,7 +486,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("any constructs a field w/o parameter name") @DisplayName("any constructs a field w/o parameter name")
public void anyCtor() { public void anyCtor() {
Field<Collection<Integer>> field = Field.any("Here", List.of(8, 16, 32)); final Field<Collection<Integer>> field = Field.any("Here", List.of(8, 16, 32));
assertEquals("Here", field.getName(), "Field name not filled correctly"); assertEquals("Here", field.getName(), "Field name not filled correctly");
assertEquals(Op.IN, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.IN, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(List.of(8, 16, 32), field.getComparison().getValue(), assertEquals(List.of(8, 16, 32), field.getComparison().getValue(),
@ -491,7 +498,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("any constructs a field w/ parameter name") @DisplayName("any constructs a field w/ parameter name")
public void anyParameterCtor() { public void anyParameterCtor() {
Field<Collection<Integer>> field = Field.any("Here", List.of(8, 16, 32), ":list"); final Field<Collection<Integer>> field = Field.any("Here", List.of(8, 16, 32), ":list");
assertEquals("Here", field.getName(), "Field name not filled correctly"); assertEquals("Here", field.getName(), "Field name not filled correctly");
assertEquals(Op.IN, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.IN, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(List.of(8, 16, 32), field.getComparison().getValue(), assertEquals(List.of(8, 16, 32), field.getComparison().getValue(),
@ -503,7 +510,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("inArray constructs a field w/o parameter name") @DisplayName("inArray constructs a field w/o parameter name")
public void inArrayCtor() { public void inArrayCtor() {
Field<Pair<String, Collection<String>>> field = Field.inArray("ArrayField", "table", List.of("z")); final Field<Pair<String, Collection<String>>> field = Field.inArray("ArrayField", "table", List.of("z"));
assertEquals("ArrayField", field.getName(), "Field name not filled correctly"); assertEquals("ArrayField", field.getName(), "Field name not filled correctly");
assertEquals(Op.IN_ARRAY, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.IN_ARRAY, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("table", field.getComparison().getValue().getFirst(), assertEquals("table", field.getComparison().getValue().getFirst(),
@ -517,7 +524,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("inArray constructs a field w/ parameter name") @DisplayName("inArray constructs a field w/ parameter name")
public void inArrayParameterCtor() { public void inArrayParameterCtor() {
Field<Pair<String, Collection<String>>> field = Field.inArray("ArrayField", "table", List.of("z"), ":a"); final Field<Pair<String, Collection<String>>> field = Field.inArray("ArrayField", "table", List.of("z"), ":a");
assertEquals("ArrayField", field.getName(), "Field name not filled correctly"); assertEquals("ArrayField", field.getName(), "Field name not filled correctly");
assertEquals(Op.IN_ARRAY, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.IN_ARRAY, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("table", field.getComparison().getValue().getFirst(), assertEquals("table", field.getComparison().getValue().getFirst(),
@ -531,7 +538,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("exists constructs a field") @DisplayName("exists constructs a field")
public void existsCtor() { public void existsCtor() {
Field<String> field = Field.exists("Groovy"); final Field<String> field = Field.exists("Groovy");
assertEquals("Groovy", field.getName(), "Field name not filled correctly"); assertEquals("Groovy", field.getName(), "Field name not filled correctly");
assertEquals(Op.EXISTS, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.EXISTS, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("", field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals("", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -542,7 +549,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("notExists constructs a field") @DisplayName("notExists constructs a field")
public void notExistsCtor() { public void notExistsCtor() {
Field<String> field = Field.notExists("Groovy"); final Field<String> field = Field.notExists("Groovy");
assertEquals("Groovy", field.getName(), "Field name not filled correctly"); assertEquals("Groovy", field.getName(), "Field name not filled correctly");
assertEquals(Op.NOT_EXISTS, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.NOT_EXISTS, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("", field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals("", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -553,7 +560,7 @@ final public class FieldTest {
@Test @Test
@DisplayName("named constructs a field") @DisplayName("named constructs a field")
public void namedCtor() { public void namedCtor() {
Field<String> field = Field.named("Tacos"); final Field<String> field = Field.named("Tacos");
assertEquals("Tacos", field.getName(), "Field name not filled correctly"); assertEquals("Tacos", field.getName(), "Field name not filled correctly");
assertEquals(Op.EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly"); assertEquals(Op.EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("", field.getComparison().getValue(), "Field comparison value not filled correctly"); assertEquals("", field.getComparison().getValue(), "Field comparison value not filled correctly");

View File

@ -18,7 +18,7 @@ import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
* Unit tests for the `Count` object * Unit tests for the `Count` object
*/ */
@DisplayName("JVM | Java | Query | CountQuery") @DisplayName("JVM | Java | Query | CountQuery")
public class CountQueryTest { final public class CountQueryTest {
/** /**
* Clear the connection string (resets Dialect) * Clear the connection string (resets Dialect)
@ -37,7 +37,7 @@ public class CountQueryTest {
@Test @Test
@DisplayName("byFields generates correctly | PostgreSQL") @DisplayName("byFields generates correctly | PostgreSQL")
public void byFieldsPostgres() { public void byFieldsPostgres() throws DocumentException {
ForceDialect.postgres(); ForceDialect.postgres();
assertEquals(String.format("SELECT COUNT(*) AS it FROM %s WHERE data->>'test' = :field0", TEST_TABLE), assertEquals(String.format("SELECT COUNT(*) AS it FROM %s WHERE data->>'test' = :field0", TEST_TABLE),
CountQuery.byFields(TEST_TABLE, List.of(Field.equal("test", "", ":field0"))), CountQuery.byFields(TEST_TABLE, List.of(Field.equal("test", "", ":field0"))),
@ -46,7 +46,7 @@ public class CountQueryTest {
@Test @Test
@DisplayName("byFields generates correctly | SQLite") @DisplayName("byFields generates correctly | SQLite")
public void byFieldsSQLite() { public void byFieldsSQLite() throws DocumentException {
ForceDialect.sqlite(); ForceDialect.sqlite();
assertEquals(String.format("SELECT COUNT(*) AS it FROM %s WHERE data->>'test' = :field0", TEST_TABLE), assertEquals(String.format("SELECT COUNT(*) AS it FROM %s WHERE data->>'test' = :field0", TEST_TABLE),
CountQuery.byFields(TEST_TABLE, List.of(Field.equal("test", "", ":field0"))), CountQuery.byFields(TEST_TABLE, List.of(Field.equal("test", "", ":field0"))),

View File

@ -19,7 +19,7 @@ import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
* Unit tests for the `Definition` object * Unit tests for the `Definition` object
*/ */
@DisplayName("JVM | Java | Query | DefinitionQuery") @DisplayName("JVM | Java | Query | DefinitionQuery")
public class DefinitionQueryTest { final public class DefinitionQueryTest {
/** /**
* Clear the connection string (resets Dialect) * Clear the connection string (resets Dialect)

View File

@ -0,0 +1,94 @@
package solutions.bitbadger.documents.java.query;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.DocumentException;
import solutions.bitbadger.documents.Field;
import solutions.bitbadger.documents.query.DeleteQuery;
import solutions.bitbadger.documents.support.ForceDialect;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
/**
* Unit tests for the `Delete` object
*/
@DisplayName("JVM | Java | Query | DeleteQuery")
final public class DeleteQueryTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
public void cleanUp() {
ForceDialect.none();
}
@Test
@DisplayName("byId generates correctly | PostgreSQL")
public void byIdPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("DELETE FROM %s WHERE data->>'id' = :id", TEST_TABLE), DeleteQuery.byId(TEST_TABLE),
"Delete query not constructed correctly");
}
@Test
@DisplayName("byId generates correctly | SQLite")
public void byIdSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format("DELETE FROM %s WHERE data->>'id' = :id", TEST_TABLE), DeleteQuery.byId(TEST_TABLE),
"Delete query not constructed correctly");
}
@Test
@DisplayName("byFields generates correctly | PostgreSQL")
public void byFieldsPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("DELETE FROM %s WHERE data->>'a' = :b", TEST_TABLE),
DeleteQuery.byFields(TEST_TABLE, List.of(Field.equal("a", "", ":b"))),
"Delete query not constructed correctly");
}
@Test
@DisplayName("byFields generates correctly | SQLite")
public void byFieldsSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format("DELETE FROM %s WHERE data->>'a' = :b", TEST_TABLE),
DeleteQuery.byFields(TEST_TABLE, List.of(Field.equal("a", "", ":b"))),
"Delete query not constructed correctly");
}
@Test
@DisplayName("byContains generates correctly | PostgreSQL")
public void byContainsPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("DELETE FROM %s WHERE data @> :criteria", TEST_TABLE),
DeleteQuery.byContains(TEST_TABLE), "Delete query not constructed correctly");
}
@Test
@DisplayName("byContains fails | SQLite")
public void byContainsSQLite() {
ForceDialect.sqlite();
assertThrows(DocumentException.class, () -> DeleteQuery.byContains(TEST_TABLE));
}
@Test
@DisplayName("byJsonPath generates correctly | PostgreSQL")
public void byJsonPathPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("DELETE FROM %s WHERE jsonb_path_exists(data, :path::jsonpath)", TEST_TABLE),
DeleteQuery.byJsonPath(TEST_TABLE), "Delete query not constructed correctly");
}
@Test
@DisplayName("byJsonPath fails | SQLite")
public void byJsonPathSQLite() {
ForceDialect.sqlite();
assertThrows(DocumentException.class, () -> DeleteQuery.byJsonPath(TEST_TABLE));
}
}

View File

@ -0,0 +1,134 @@
package solutions.bitbadger.documents.java.query;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.AutoId;
import solutions.bitbadger.documents.Configuration;
import solutions.bitbadger.documents.DocumentException;
import solutions.bitbadger.documents.query.DocumentQuery;
import solutions.bitbadger.documents.support.ForceDialect;
import static org.junit.jupiter.api.Assertions.*;
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
/**
* Unit tests for the `Document` object
*/
@DisplayName("JVM | Java | Query | DocumentQuery")
final public class DocumentQueryTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
public void cleanUp() {
ForceDialect.none();
}
@Test
@DisplayName("insert generates no auto ID | PostgreSQL")
public void insertNoAutoPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("INSERT INTO %s VALUES (:data)", TEST_TABLE), DocumentQuery.insert(TEST_TABLE));
}
@Test
@DisplayName("insert generates no auto ID | SQLite")
public void insertNoAutoSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format("INSERT INTO %s VALUES (:data)", TEST_TABLE), DocumentQuery.insert(TEST_TABLE));
}
@Test
@DisplayName("insert generates auto number | PostgreSQL")
public void insertAutoNumberPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("INSERT INTO %1$s VALUES (:data::jsonb || ('{\"id\":' "
+ "|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM %1$s) || '}')::jsonb)",
TEST_TABLE),
DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER));
}
@Test
@DisplayName("insert generates auto number | SQLite")
public void insertAutoNumberSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format("INSERT INTO %1$s VALUES (json_set(:data, '$.id', "
+ "(SELECT coalesce(max(data->>'id'), 0) + 1 FROM %1$s)))", TEST_TABLE),
DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER));
}
@Test
@DisplayName("insert generates auto UUID | PostgreSQL")
public void insertAutoUUIDPostgres() throws DocumentException {
ForceDialect.postgres();
final String query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID);
assertTrue(query.startsWith(String.format("INSERT INTO %s VALUES (:data::jsonb || '{\"id\":\"", TEST_TABLE)),
String.format("Query start not correct (actual: %s)", query));
assertTrue(query.endsWith("\"}')"), "Query end not correct");
}
@Test
@DisplayName("insert generates auto UUID | SQLite")
public void insertAutoUUIDSQLite() throws DocumentException {
ForceDialect.sqlite();
final String query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID);
assertTrue(query.startsWith(String.format("INSERT INTO %s VALUES (json_set(:data, '$.id', '", TEST_TABLE)),
String.format("Query start not correct (actual: %s)", query));
assertTrue(query.endsWith("'))"), "Query end not correct");
}
@Test
@DisplayName("insert generates auto random string | PostgreSQL")
public void insertAutoRandomPostgres() throws DocumentException {
try {
ForceDialect.postgres();
Configuration.idStringLength = 8;
final String query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING);
final String start = String.format("INSERT INTO %s VALUES (:data::jsonb || '{\"id\":\"", TEST_TABLE);
final String end = "\"}')";
assertTrue(query.startsWith(start), String.format("Query start not correct (actual: %s)", query));
assertTrue(query.endsWith(end), "Query end not correct");
assertEquals(8, query.replace(start, "").replace(end, "").length(), "Random string length incorrect");
} finally {
Configuration.idStringLength = 16;
}
}
@Test
@DisplayName("insert generates auto random string | SQLite")
public void insertAutoRandomSQLite() throws DocumentException {
ForceDialect.sqlite();
final String query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING);
final String start = String.format("INSERT INTO %s VALUES (json_set(:data, '$.id', '", TEST_TABLE);
final String end = "'))";
assertTrue(query.startsWith(start), String.format("Query start not correct (actual: %s)", query));
assertTrue(query.endsWith(end), "Query end not correct");
assertEquals(Configuration.idStringLength, query.replace(start, "").replace(end, "").length(),
"Random string length incorrect");
}
@Test
@DisplayName("insert fails when no dialect is set")
public void insertFailsUnknown() {
assertThrows(DocumentException.class, () -> DocumentQuery.insert(TEST_TABLE));
}
@Test
@DisplayName("save generates correctly")
public void save() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format(
"INSERT INTO %s VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data",
TEST_TABLE),
DocumentQuery.save(TEST_TABLE), "INSERT ON CONFLICT UPDATE statement not constructed correctly");
}
@Test
@DisplayName("update generates successfully")
public void update() {
assertEquals(String.format("UPDATE %s SET data = :data", TEST_TABLE), DocumentQuery.update(TEST_TABLE),
"Update query not constructed correctly");
}
}

View File

@ -0,0 +1,97 @@
package solutions.bitbadger.documents.java.query;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.DocumentException;
import solutions.bitbadger.documents.Field;
import solutions.bitbadger.documents.query.ExistsQuery;
import solutions.bitbadger.documents.support.ForceDialect;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
/**
* Unit tests for the `Exists` object
*/
@DisplayName("JVM | Java | Query | ExistsQuery")
final public class ExistsQueryTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
public void cleanUp() {
ForceDialect.none();
}
@Test
@DisplayName("byId generates correctly | PostgreSQL")
public void byIdPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("SELECT EXISTS (SELECT 1 FROM %s WHERE data->>'id' = :id) AS it", TEST_TABLE),
ExistsQuery.byId(TEST_TABLE), "Exists query not constructed correctly");
}
@Test
@DisplayName("byId generates correctly | SQLite")
public void byIdSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format("SELECT EXISTS (SELECT 1 FROM %s WHERE data->>'id' = :id) AS it", TEST_TABLE),
ExistsQuery.byId(TEST_TABLE), "Exists query not constructed correctly");
}
@Test
@DisplayName("byFields generates correctly | PostgreSQL")
public void byFieldsPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format(
"SELECT EXISTS (SELECT 1 FROM %s WHERE (data->>'it')::numeric = :test) AS it", TEST_TABLE),
ExistsQuery.byFields(TEST_TABLE, List.of(Field.equal("it", 7, ":test"))),
"Exists query not constructed correctly");
}
@Test
@DisplayName("byFields generates correctly | SQLite")
public void byFieldsSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format("SELECT EXISTS (SELECT 1 FROM %s WHERE data->>'it' = :test) AS it", TEST_TABLE),
ExistsQuery.byFields(TEST_TABLE, List.of(Field.equal("it", 7, ":test"))),
"Exists query not constructed correctly");
}
@Test
@DisplayName("byContains generates correctly | PostgreSQL")
public void byContainsPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("SELECT EXISTS (SELECT 1 FROM %s WHERE data @> :criteria) AS it", TEST_TABLE),
ExistsQuery.byContains(TEST_TABLE), "Exists query not constructed correctly");
}
@Test
@DisplayName("byContains fails | SQLite")
public void byContainsSQLite() {
ForceDialect.sqlite();
assertThrows(DocumentException.class, () -> ExistsQuery.byContains(TEST_TABLE));
}
@Test
@DisplayName("byJsonPath generates correctly | PostgreSQL")
public void byJsonPathPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format(
"SELECT EXISTS (SELECT 1 FROM %s WHERE jsonb_path_exists(data, :path::jsonpath)) AS it",
TEST_TABLE),
ExistsQuery.byJsonPath(TEST_TABLE), "Exists query not constructed correctly");
}
@Test
@DisplayName("byJsonPath fails | SQLite")
public void byJsonPathSQLite() {
ForceDialect.sqlite();
assertThrows(DocumentException.class, () -> ExistsQuery.byJsonPath(TEST_TABLE));
}
}

View File

@ -0,0 +1,102 @@
package solutions.bitbadger.documents.java.query;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.DocumentException;
import solutions.bitbadger.documents.Field;
import solutions.bitbadger.documents.query.FindQuery;
import solutions.bitbadger.documents.support.ForceDialect;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
/**
* Unit tests for the `Find` object
*/
@DisplayName("JVM | Java | Query | FindQuery")
final public class FindQueryTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
public void cleanUp() {
ForceDialect.none();
}
@Test
@DisplayName("all generates correctly")
public void all() {
assertEquals(String.format("SELECT data FROM %s", TEST_TABLE), FindQuery.all(TEST_TABLE),
"Find query not constructed correctly");
}
@Test
@DisplayName("byId generates correctly | PostgreSQL")
public void byIdPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("SELECT data FROM %s WHERE data->>'id' = :id", TEST_TABLE),
FindQuery.byId(TEST_TABLE), "Find query not constructed correctly");
}
@Test
@DisplayName("byId generates correctly | SQLite")
public void byIdSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format("SELECT data FROM %s WHERE data->>'id' = :id", TEST_TABLE),
FindQuery.byId(TEST_TABLE), "Find query not constructed correctly");
}
@Test
@DisplayName("byFields generates correctly | PostgreSQL")
public void byFieldsPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(
String.format("SELECT data FROM %s WHERE data->>'a' = :b AND (data->>'c')::numeric < :d", TEST_TABLE),
FindQuery.byFields(TEST_TABLE, List.of(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))),
"Find query not constructed correctly");
}
@Test
@DisplayName("byFields generates correctly | SQLite")
public void byFieldsSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format("SELECT data FROM %s WHERE data->>'a' = :b AND data->>'c' < :d", TEST_TABLE),
FindQuery.byFields(TEST_TABLE, List.of(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))),
"Find query not constructed correctly");
}
@Test
@DisplayName("byContains generates correctly | PostgreSQL")
public void byContainsPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("SELECT data FROM %s WHERE data @> :criteria", TEST_TABLE),
FindQuery.byContains(TEST_TABLE), "Find query not constructed correctly");
}
@Test
@DisplayName("byContains fails | SQLite")
public void byContainsSQLite() {
ForceDialect.sqlite();
assertThrows(DocumentException.class, () -> FindQuery.byContains(TEST_TABLE));
}
@Test
@DisplayName("byJsonPath generates correctly | PostgreSQL")
public void byJsonPathPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("SELECT data FROM %s WHERE jsonb_path_exists(data, :path::jsonpath)", TEST_TABLE),
FindQuery.byJsonPath(TEST_TABLE), "Find query not constructed correctly");
}
@Test
@DisplayName("byJsonPath fails | SQLite")
public void byJsonPathSQLite() {
ForceDialect.sqlite();
assertThrows(DocumentException.class, () -> FindQuery.byJsonPath(TEST_TABLE));
}
}

View File

@ -0,0 +1,97 @@
package solutions.bitbadger.documents.java.query;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.DocumentException;
import solutions.bitbadger.documents.Field;
import solutions.bitbadger.documents.query.PatchQuery;
import solutions.bitbadger.documents.support.ForceDialect;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
/**
* Unit tests for the `Patch` object
*/
@DisplayName("JVM | Java | Query | PatchQuery")
final public class PatchQueryTest {
/**
* Reset the dialect
*/
@AfterEach
public void cleanUp() {
ForceDialect.none();
}
@Test
@DisplayName("byId generates correctly | PostgreSQL")
public void byIdPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("UPDATE %s SET data = data || :data WHERE data->>'id' = :id", TEST_TABLE),
PatchQuery.byId(TEST_TABLE), "Patch query not constructed correctly");
}
@Test
@DisplayName("byId generates correctly | SQLite")
public void byIdSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format(
"UPDATE %s SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id", TEST_TABLE),
PatchQuery.byId(TEST_TABLE), "Patch query not constructed correctly");
}
@Test
@DisplayName("byFields generates correctly | PostgreSQL")
public void byFieldsPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("UPDATE %s SET data = data || :data WHERE data->>'z' = :y", TEST_TABLE),
PatchQuery.byFields(TEST_TABLE, List.of(Field.equal("z", "", ":y"))),
"Patch query not constructed correctly");
}
@Test
@DisplayName("byFields generates correctly | SQLite")
public void byFieldsSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format(
"UPDATE %s SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y", TEST_TABLE),
PatchQuery.byFields(TEST_TABLE, List.of(Field.equal("z", "", ":y"))),
"Patch query not constructed correctly");
}
@Test
@DisplayName("byContains generates correctly | PostgreSQL")
public void byContainsPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("UPDATE %s SET data = data || :data WHERE data @> :criteria", TEST_TABLE),
PatchQuery.byContains(TEST_TABLE), "Patch query not constructed correctly");
}
@Test
@DisplayName("byContains fails | SQLite")
public void byContainsSQLite() {
ForceDialect.sqlite();
assertThrows(DocumentException.class, () -> PatchQuery.byContains(TEST_TABLE));
}
@Test
@DisplayName("byJsonPath generates correctly | PostgreSQL")
public void byJsonPathPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("UPDATE %s SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)",
TEST_TABLE),
PatchQuery.byJsonPath(TEST_TABLE), "Patch query not constructed correctly");
}
@Test
@DisplayName("byJsonPath fails | SQLite")
public void byJsonPathSQLite() {
ForceDialect.sqlite();
assertThrows(DocumentException.class, () -> PatchQuery.byJsonPath(TEST_TABLE));
}
}

View File

@ -0,0 +1,166 @@
package solutions.bitbadger.documents.java.query;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.Dialect;
import solutions.bitbadger.documents.DocumentException;
import solutions.bitbadger.documents.Field;
import solutions.bitbadger.documents.FieldMatch;
import solutions.bitbadger.documents.query.QueryUtils;
import solutions.bitbadger.documents.support.ForceDialect;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Unit tests for the package-level query functions, presented as `QueryUtils` for Java-compatible use
*/
@DisplayName("JVM | Java | Query | QueryUtils")
final public class QueryUtilsTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
public void cleanUp() {
ForceDialect.none();
}
@Test
@DisplayName("statementWhere generates correctly")
public void statementWhere() {
assertEquals("x WHERE y", QueryUtils.statementWhere("x", "y"), "Statements not combined correctly");
}
@Test
@DisplayName("byId generates a numeric ID query | PostgreSQL")
public void byIdNumericPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("test WHERE (data->>'id')::numeric = :id", QueryUtils.byId("test", 9));
}
@Test
@DisplayName("byId generates an alphanumeric ID query | PostgreSQL")
public void byIdAlphaPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("unit WHERE data->>'id' = :id", QueryUtils.byId("unit", "18"));
}
@Test
@DisplayName("byId generates ID query | SQLite")
public void byIdSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("yo WHERE data->>'id' = :id", QueryUtils.byId("yo", 27));
}
@Test
@DisplayName("byFields generates default field query | PostgreSQL")
public void byFieldsMultipleDefaultPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("this WHERE data->>'a' = :the_a AND (data->>'b')::numeric = :b_value",
QueryUtils.byFields("this", List.of(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value"))));
}
@Test
@DisplayName("byFields generates default field query | SQLite")
public void byFieldsMultipleDefaultSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("this WHERE data->>'a' = :the_a AND data->>'b' = :b_value",
QueryUtils.byFields("this", List.of(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value"))));
}
@Test
@DisplayName("byFields generates ANY field query | PostgreSQL")
public void byFieldsMultipleAnyPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value",
QueryUtils.byFields("that", List.of(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")),
FieldMatch.ANY));
}
@Test
@DisplayName("byFields generates ANY field query | SQLite")
public void byFieldsMultipleAnySQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("that WHERE data->>'a' = :the_a OR data->>'b' = :b_value",
QueryUtils.byFields("that", List.of(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")),
FieldMatch.ANY));
}
@Test
@DisplayName("orderBy generates for no fields")
public void orderByNone() throws DocumentException {
assertEquals("", QueryUtils.orderBy(List.of(), Dialect.POSTGRESQL),
"ORDER BY should have been blank (PostgreSQL)");
assertEquals("", QueryUtils.orderBy(List.of(), Dialect.SQLITE), "ORDER BY should have been blank (SQLite)");
}
@Test
@DisplayName("orderBy generates single, no direction | PostgreSQL")
public void orderBySinglePostgres() throws DocumentException {
assertEquals(" ORDER BY data->>'TestField'",
QueryUtils.orderBy(List.of(Field.named("TestField")), Dialect.POSTGRESQL),
"ORDER BY not constructed correctly");
}
@Test
@DisplayName("orderBy generates single, no direction | SQLite")
public void orderBySingleSQLite() throws DocumentException {
assertEquals(" ORDER BY data->>'TestField'",
QueryUtils.orderBy(List.of(Field.named("TestField")), Dialect.SQLITE),
"ORDER BY not constructed correctly");
}
@Test
@DisplayName("orderBy generates multiple with direction | PostgreSQL")
public void orderByMultiplePostgres() throws DocumentException {
assertEquals(" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
QueryUtils.orderBy(List.of(
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 | SQLite")
public void orderByMultipleSQLite() throws DocumentException {
assertEquals(" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
QueryUtils.orderBy(List.of(
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")
public void orderByNumericPostgres() throws DocumentException {
assertEquals(" ORDER BY (data->>'Test')::numeric",
QueryUtils.orderBy(List.of(Field.named("n:Test")), Dialect.POSTGRESQL),
"ORDER BY not constructed correctly");
}
@Test
@DisplayName("orderBy generates numeric ordering | SQLite")
public void orderByNumericSQLite() throws DocumentException {
assertEquals(" ORDER BY data->>'Test'", QueryUtils.orderBy(List.of(Field.named("n:Test")), Dialect.SQLITE),
"ORDER BY not constructed correctly");
}
@Test
@DisplayName("orderBy generates case-insensitive ordering | PostgreSQL")
public void orderByCIPostgres() throws DocumentException {
assertEquals(" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST",
QueryUtils.orderBy(List.of(Field.named("i:Test.Field DESC NULLS FIRST")), Dialect.POSTGRESQL),
"ORDER BY not constructed correctly");
}
@Test
@DisplayName("orderBy generates case-insensitive ordering | SQLite")
public void orderByCISQLite() throws DocumentException {
assertEquals(" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST",
QueryUtils.orderBy(List.of(Field.named("i:Test.Field ASC NULLS LAST")), Dialect.SQLITE),
"ORDER BY not constructed correctly");
}
}

View File

@ -0,0 +1,110 @@
package solutions.bitbadger.documents.java.query;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import solutions.bitbadger.documents.DocumentException;
import solutions.bitbadger.documents.Field;
import solutions.bitbadger.documents.Parameter;
import solutions.bitbadger.documents.ParameterType;
import solutions.bitbadger.documents.query.RemoveFieldsQuery;
import solutions.bitbadger.documents.support.ForceDialect;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
/**
* Unit tests for the `RemoveFields` object
*/
@DisplayName("JVM | Java | Query | RemoveFieldsQuery")
final public class RemoveFieldsQueryTest {
/**
* Reset the dialect
*/
@AfterEach
public void cleanUp() {
ForceDialect.none();
}
@Test
@DisplayName("byId generates correctly | PostgreSQL")
public void byIdPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("UPDATE %s SET data = data - :name::text[] WHERE data->>'id' = :id", TEST_TABLE),
RemoveFieldsQuery.byId(TEST_TABLE, List.of(new Parameter<>(":name", ParameterType.STRING, "{a,z}"))),
"Remove Fields query not constructed correctly");
}
@Test
@DisplayName("byId generates correctly | SQLite")
public void byIdSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format("UPDATE %s SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id",
TEST_TABLE),
RemoveFieldsQuery.byId(TEST_TABLE, List.of(new Parameter<>(":name0", ParameterType.STRING, "a"),
new Parameter<>(":name1", ParameterType.STRING, "z"))),
"Remove Field query not constructed correctly");
}
@Test
@DisplayName("byFields generates correctly | PostgreSQL")
public void byFieldsPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("UPDATE %s SET data = data - :name::text[] WHERE data->>'f' > :g", TEST_TABLE),
RemoveFieldsQuery.byFields(TEST_TABLE, List.of(new Parameter<>(":name", ParameterType.STRING, "{b,c}")),
List.of(Field.greater("f", "", ":g"))),
"Remove Field query not constructed correctly");
}
@Test
@DisplayName("byFields generates correctly | SQLite")
public void byFieldsSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals(String.format("UPDATE %s SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g",
TEST_TABLE),
RemoveFieldsQuery.byFields(TEST_TABLE, List.of(new Parameter<>(":name0", ParameterType.STRING, "b"),
new Parameter<>(":name1", ParameterType.STRING, "c")),
List.of(Field.greater("f", "", ":g"))),
"Remove Field query not constructed correctly");
}
@Test
@DisplayName("byContains generates correctly | PostgreSQL")
public void byContainsPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format("UPDATE %s SET data = data - :name::text[] WHERE data @> :criteria", TEST_TABLE),
RemoveFieldsQuery.byContains(TEST_TABLE,
List.of(new Parameter<>(":name", ParameterType.STRING, "{m,n}"))),
"Remove Field query not constructed correctly");
}
@Test
@DisplayName("byContains fails | SQLite")
public void byContainsSQLite() {
ForceDialect.sqlite();
assertThrows(DocumentException.class, () -> RemoveFieldsQuery.byContains(TEST_TABLE, List.of()));
}
@Test
@DisplayName("byJsonPath generates correctly | PostgreSQL")
public void byJsonPathPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals(String.format(
"UPDATE %s SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)",
TEST_TABLE),
RemoveFieldsQuery.byJsonPath(TEST_TABLE,
List.of(new Parameter<>(":name", ParameterType.STRING, "{o,p}"))),
"Remove Field query not constructed correctly");
}
@Test
@DisplayName("byJsonPath fails | SQLite")
public void byJsonPathSQLite() {
ForceDialect.sqlite();
assertThrows(DocumentException.class, () -> RemoveFieldsQuery.byJsonPath(TEST_TABLE, List.of()));
}
}

View File

@ -4,10 +4,10 @@ import org.junit.jupiter.api.AfterEach
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 org.junit.jupiter.api.assertThrows 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.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
@ -16,87 +16,89 @@ import kotlin.test.assertEquals
@DisplayName("JVM | Kotlin | Query | DeleteQuery") @DisplayName("JVM | Kotlin | Query | DeleteQuery")
class DeleteQueryTest { class DeleteQueryTest {
/** Test table name */
private val tbl = "test_table"
/** /**
* Clear the connection string (resets Dialect) * Clear the connection string (resets Dialect)
*/ */
@AfterEach @AfterEach
fun cleanUp() { fun cleanUp() {
Configuration.dialectValue = null ForceDialect.none()
} }
@Test @Test
@DisplayName("byId generates correctly (PostgreSQL)") @DisplayName("byId generates correctly | PostgreSQL")
fun byIdPostgres() { fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE data->>'id' = :id", "DELETE FROM $TEST_TABLE WHERE data->>'id' = :id",
DeleteQuery.byId<String>(tbl), "Delete query not constructed correctly" DeleteQuery.byId<String>(TEST_TABLE), "Delete query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byId generates correctly (SQLite)") @DisplayName("byId generates correctly | SQLite")
fun byIdSQLite() { fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE data->>'id' = :id", "DELETE FROM $TEST_TABLE WHERE data->>'id' = :id",
DeleteQuery.byId<String>(tbl), "Delete query not constructed correctly" DeleteQuery.byId<String>(TEST_TABLE), "Delete query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byFields generates correctly (PostgreSQL)") @DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() { fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE data->>'a' = :b", DeleteQuery.byFields(tbl, listOf(Field.equal("a", "", ":b"))), "DELETE FROM $TEST_TABLE WHERE data->>'a' = :b",
DeleteQuery.byFields(TEST_TABLE, listOf(Field.equal("a", "", ":b"))),
"Delete query not constructed correctly" "Delete query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byFields generates correctly (SQLite)") @DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() { fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE data->>'a' = :b", DeleteQuery.byFields(tbl, listOf(Field.equal("a", "", ":b"))), "DELETE FROM $TEST_TABLE WHERE data->>'a' = :b",
DeleteQuery.byFields(TEST_TABLE, listOf(Field.equal("a", "", ":b"))),
"Delete query not constructed correctly" "Delete query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byContains generates correctly (PostgreSQL)") @DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"DELETE FROM $tbl WHERE data @> :criteria", DeleteQuery.byContains(tbl), "Delete query not constructed correctly" "DELETE FROM $TEST_TABLE WHERE data @> :criteria",
) DeleteQuery.byContains(TEST_TABLE),
}
@Test
@DisplayName("byContains fails (SQLite)")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { DeleteQuery.byContains(tbl) }
}
@Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)")
fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals(
"DELETE FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)", DeleteQuery.byJsonPath(tbl),
"Delete query not constructed correctly" "Delete query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byContains fails | SQLite")
fun byContainsSQLite() {
ForceDialect.sqlite()
assertThrows<DocumentException> { DeleteQuery.byContains(TEST_TABLE) }
}
@Test
@DisplayName("byJsonPath generates correctly | PostgreSQL")
fun byJsonPathPostgres() {
ForceDialect.postgres()
assertEquals(
"DELETE FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
DeleteQuery.byJsonPath(TEST_TABLE),
"Delete query not constructed correctly"
)
}
@Test
@DisplayName("byJsonPath fails | SQLite")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { DeleteQuery.byJsonPath(tbl) } assertThrows<DocumentException> { DeleteQuery.byJsonPath(TEST_TABLE) }
} }
} }

View File

@ -6,8 +6,9 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.AutoId import solutions.bitbadger.documents.AutoId
import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -17,92 +18,90 @@ import kotlin.test.assertTrue
@DisplayName("JVM | Kotlin | Query | DocumentQuery") @DisplayName("JVM | Kotlin | Query | DocumentQuery")
class DocumentQueryTest { class DocumentQueryTest {
/** Test table name */
private val tbl = "test_table"
/** /**
* Clear the connection string (resets Dialect) * Clear the connection string (resets Dialect)
*/ */
@AfterEach @AfterEach
fun cleanUp() { fun cleanUp() {
Configuration.dialectValue = null ForceDialect.none()
} }
@Test @Test
@DisplayName("insert generates no auto ID (PostgreSQL)") @DisplayName("insert generates no auto ID | PostgreSQL")
fun insertNoAutoPostgres() { fun insertNoAutoPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals("INSERT INTO $tbl VALUES (:data)", DocumentQuery.insert(tbl)) assertEquals("INSERT INTO $TEST_TABLE VALUES (:data)", DocumentQuery.insert(TEST_TABLE))
} }
@Test @Test
@DisplayName("insert generates no auto ID (SQLite)") @DisplayName("insert generates no auto ID | SQLite")
fun insertNoAutoSQLite() { fun insertNoAutoSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals("INSERT INTO $tbl VALUES (:data)", DocumentQuery.insert(tbl)) assertEquals("INSERT INTO $TEST_TABLE VALUES (:data)", DocumentQuery.insert(TEST_TABLE))
} }
@Test @Test
@DisplayName("insert generates auto number (PostgreSQL)") @DisplayName("insert generates auto number | PostgreSQL")
fun insertAutoNumberPostgres() { fun insertAutoNumberPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"INSERT INTO $tbl VALUES (:data::jsonb || ('{\"id\":' " + "INSERT INTO $TEST_TABLE VALUES (:data::jsonb || ('{\"id\":' " +
"|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $tbl) || '}')::jsonb)", "|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $TEST_TABLE) || '}')::jsonb)",
DocumentQuery.insert(tbl, AutoId.NUMBER) DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER)
) )
} }
@Test @Test
@DisplayName("insert generates auto number (SQLite)") @DisplayName("insert generates auto number | SQLite")
fun insertAutoNumberSQLite() { fun insertAutoNumberSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"INSERT INTO $tbl VALUES (json_set(:data, '$.id', " + "INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$.id', " +
"(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $tbl)))", "(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $TEST_TABLE)))",
DocumentQuery.insert(tbl, AutoId.NUMBER) DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER)
) )
} }
@Test @Test
@DisplayName("insert generates auto UUID (PostgreSQL)") @DisplayName("insert generates auto UUID | PostgreSQL")
fun insertAutoUUIDPostgres() { fun insertAutoUUIDPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
val query = DocumentQuery.insert(tbl, AutoId.UUID) val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
assertTrue( assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""), query.startsWith("INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\""),
"Query start not correct (actual: $query)" "Query start not correct (actual: $query)"
) )
assertTrue(query.endsWith("\"}')"), "Query end not correct") assertTrue(query.endsWith("\"}')"), "Query end not correct")
} }
@Test @Test
@DisplayName("insert generates auto UUID (SQLite)") @DisplayName("insert generates auto UUID | SQLite")
fun insertAutoUUIDSQLite() { fun insertAutoUUIDSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
val query = DocumentQuery.insert(tbl, AutoId.UUID) val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
assertTrue( assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"), query.startsWith("INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$.id', '"),
"Query start not correct (actual: $query)" "Query start not correct (actual: $query)"
) )
assertTrue(query.endsWith("'))"), "Query end not correct") assertTrue(query.endsWith("'))"), "Query end not correct")
} }
@Test @Test
@DisplayName("insert generates auto random string (PostgreSQL)") @DisplayName("insert generates auto random string | PostgreSQL")
fun insertAutoRandomPostgres() { fun insertAutoRandomPostgres() {
try { try {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
Configuration.idStringLength = 8 Configuration.idStringLength = 8
val query = DocumentQuery.insert(tbl, AutoId.RANDOM_STRING) val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
assertTrue( assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\""), query.startsWith("INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\""),
"Query start not correct (actual: $query)" "Query start not correct (actual: $query)"
) )
assertTrue(query.endsWith("\"}')"), "Query end not correct") assertTrue(query.endsWith("\"}')"), "Query end not correct")
assertEquals( assertEquals(
8, 8,
query.replace("INSERT INTO $tbl VALUES (:data::jsonb || '{\"id\":\"", "").replace("\"}')", "").length, query.replace("INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\"", "")
.replace("\"}')", "").length,
"Random string length incorrect" "Random string length incorrect"
) )
} finally { } finally {
@ -111,18 +110,18 @@ class DocumentQueryTest {
} }
@Test @Test
@DisplayName("insert generates auto random string (SQLite)") @DisplayName("insert generates auto random string | SQLite")
fun insertAutoRandomSQLite() { fun insertAutoRandomSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
val query = DocumentQuery.insert(tbl, AutoId.RANDOM_STRING) val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
assertTrue( assertTrue(
query.startsWith("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '"), query.startsWith("INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$.id', '"),
"Query start not correct (actual: $query)" "Query start not correct (actual: $query)"
) )
assertTrue(query.endsWith("'))"), "Query end not correct") assertTrue(query.endsWith("'))"), "Query end not correct")
assertEquals( assertEquals(
Configuration.idStringLength, Configuration.idStringLength,
query.replace("INSERT INTO $tbl VALUES (json_set(:data, '$.id', '", "").replace("'))", "").length, query.replace("INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$.id', '", "").replace("'))", "").length,
"Random string length incorrect" "Random string length incorrect"
) )
} }
@ -130,21 +129,25 @@ class DocumentQueryTest {
@Test @Test
@DisplayName("insert fails when no dialect is set") @DisplayName("insert fails when no dialect is set")
fun insertFailsUnknown() { fun insertFailsUnknown() {
assertThrows<DocumentException> { DocumentQuery.insert(tbl) } assertThrows<DocumentException> { DocumentQuery.insert(TEST_TABLE) }
} }
@Test @Test
@DisplayName("save generates correctly") @DisplayName("save generates correctly")
fun save() { fun save() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"INSERT INTO $tbl VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data", "INSERT INTO $TEST_TABLE VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data",
DocumentQuery.save(tbl), "INSERT ON CONFLICT UPDATE statement not constructed correctly" DocumentQuery.save(TEST_TABLE), "INSERT ON CONFLICT UPDATE statement not constructed correctly"
) )
} }
@Test @Test
@DisplayName("update generates successfully") @DisplayName("update generates successfully")
fun update() = fun update() =
assertEquals("UPDATE $tbl SET data = :data", DocumentQuery.update(tbl), "Update query not constructed correctly") assertEquals(
"UPDATE $TEST_TABLE SET data = :data",
DocumentQuery.update(TEST_TABLE),
"Update query not constructed correctly"
)
} }

View File

@ -8,98 +8,98 @@ import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
* Unit tests for the `Exists` object * Unit tests for the `Exists` object
*/ */
@DisplayName("Kotlin | Common | Query: Exists") @DisplayName("JVM | Kotlin | Query | ExistsQuery")
class ExistsQueryTest { class ExistsQueryTest {
/** Test table name */
private val tbl = "test_table"
/** /**
* Clear the connection string (resets Dialect) * Clear the connection string (resets Dialect)
*/ */
@AfterEach @AfterEach
fun cleanUp() { fun cleanUp() {
Configuration.dialectValue = null ForceDialect.none()
} }
@Test @Test
@DisplayName("byId generates correctly (PostgreSQL)") @DisplayName("byId generates correctly | PostgreSQL")
fun byIdPostgres() { fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'id' = :id) AS it", "SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it",
ExistsQuery.byId<String>(tbl), "Exists query not constructed correctly" ExistsQuery.byId<String>(TEST_TABLE), "Exists query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byId generates correctly (SQLite)") @DisplayName("byId generates correctly | SQLite")
fun byIdSQLite() { fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'id' = :id) AS it", "SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it",
ExistsQuery.byId<String>(tbl), "Exists query not constructed correctly" ExistsQuery.byId<String>(TEST_TABLE), "Exists query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byFields generates correctly (PostgreSQL)") @DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() { fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE (data->>'it')::numeric = :test) AS it", "SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE (data->>'it')::numeric = :test) AS it",
ExistsQuery.byFields(tbl, listOf(Field.equal("it", 7, ":test"))), ExistsQuery.byFields(TEST_TABLE, listOf(Field.equal("it", 7, ":test"))),
"Exists query not constructed correctly" "Exists query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byFields generates correctly (SQLite)") @DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() { fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'it' = :test) AS it", "SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'it' = :test) AS it",
ExistsQuery.byFields(tbl, listOf(Field.equal("it", 7, ":test"))), ExistsQuery.byFields(TEST_TABLE, listOf(Field.equal("it", 7, ":test"))),
"Exists query not constructed correctly" "Exists query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byContains generates correctly (PostgreSQL)") @DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data @> :criteria) AS it", ExistsQuery.byContains(tbl), "SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data @> :criteria) AS it",
ExistsQuery.byContains(TEST_TABLE),
"Exists query not constructed correctly" "Exists query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails | SQLite")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { ExistsQuery.byContains(tbl) } assertThrows<DocumentException> { ExistsQuery.byContains(TEST_TABLE) }
} }
@Test @Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)") @DisplayName("byJsonPath generates correctly | PostgreSQL")
fun byJsonPathPostgres() { fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)) AS it", "SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)) AS it",
ExistsQuery.byJsonPath(tbl), "Exists query not constructed correctly" ExistsQuery.byJsonPath(TEST_TABLE), "Exists query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails | SQLite")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { ExistsQuery.byJsonPath(tbl) } assertThrows<DocumentException> { ExistsQuery.byJsonPath(TEST_TABLE) }
} }
} }

View File

@ -4,10 +4,9 @@ import org.junit.jupiter.api.AfterEach
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 org.junit.jupiter.api.assertThrows 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.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -22,7 +21,7 @@ class FindQueryTest {
*/ */
@AfterEach @AfterEach
fun cleanUp() { fun cleanUp() {
Configuration.dialectValue = null ForceDialect.none()
} }
@Test @Test
@ -31,9 +30,9 @@ class FindQueryTest {
assertEquals("SELECT data FROM $TEST_TABLE", FindQuery.all(TEST_TABLE), "Find query not constructed correctly") assertEquals("SELECT data FROM $TEST_TABLE", FindQuery.all(TEST_TABLE), "Find query not constructed correctly")
@Test @Test
@DisplayName("byId generates correctly (PostgreSQL)") @DisplayName("byId generates correctly | PostgreSQL")
fun byIdPostgres() { fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id", "SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id",
FindQuery.byId<String>(TEST_TABLE), "Find query not constructed correctly" FindQuery.byId<String>(TEST_TABLE), "Find query not constructed correctly"
@ -41,9 +40,9 @@ class FindQueryTest {
} }
@Test @Test
@DisplayName("byId generates correctly (SQLite)") @DisplayName("byId generates correctly | SQLite")
fun byIdSQLite() { fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id", "SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id",
FindQuery.byId<String>(TEST_TABLE), "Find query not constructed correctly" FindQuery.byId<String>(TEST_TABLE), "Find query not constructed correctly"
@ -51,9 +50,9 @@ class FindQueryTest {
} }
@Test @Test
@DisplayName("byFields generates correctly (PostgreSQL)") @DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() { fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND (data->>'c')::numeric < :d", "SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND (data->>'c')::numeric < :d",
FindQuery.byFields(TEST_TABLE, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))), FindQuery.byFields(TEST_TABLE, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))),
@ -62,9 +61,9 @@ class FindQueryTest {
} }
@Test @Test
@DisplayName("byFields generates correctly (SQLite)") @DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() { fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND data->>'c' < :d", "SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND data->>'c' < :d",
FindQuery.byFields(TEST_TABLE, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))), FindQuery.byFields(TEST_TABLE, listOf(Field.equal("a", "", ":b"), Field.less("c", 14, ":d"))),
@ -73,9 +72,9 @@ class FindQueryTest {
} }
@Test @Test
@DisplayName("byContains generates correctly (PostgreSQL)") @DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT data FROM $TEST_TABLE WHERE data @> :criteria", FindQuery.byContains(TEST_TABLE), "SELECT data FROM $TEST_TABLE WHERE data @> :criteria", FindQuery.byContains(TEST_TABLE),
"Find query not constructed correctly" "Find query not constructed correctly"
@ -83,16 +82,16 @@ class FindQueryTest {
} }
@Test @Test
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails | SQLite")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { FindQuery.byContains(TEST_TABLE) } assertThrows<DocumentException> { FindQuery.byContains(TEST_TABLE) }
} }
@Test @Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)") @DisplayName("byJsonPath generates correctly | PostgreSQL")
fun byJsonPathPostgres() { fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT data FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)", "SELECT data FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
FindQuery.byJsonPath(TEST_TABLE), FindQuery.byJsonPath(TEST_TABLE),
@ -101,9 +100,9 @@ class FindQueryTest {
} }
@Test @Test
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails | SQLite")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { FindQuery.byJsonPath(TEST_TABLE) } assertThrows<DocumentException> { FindQuery.byJsonPath(TEST_TABLE) }
} }
} }

View File

@ -4,10 +4,10 @@ import org.junit.jupiter.api.AfterEach
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 org.junit.jupiter.api.assertThrows 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.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
@ -16,90 +16,87 @@ import kotlin.test.assertEquals
@DisplayName("JVM | Kotlin | Query | PatchQuery") @DisplayName("JVM | Kotlin | Query | PatchQuery")
class PatchQueryTest { class PatchQueryTest {
/** Test table name */
private val tbl = "test_table"
/** /**
* Reset the dialect * Reset the dialect
*/ */
@AfterEach @AfterEach
fun cleanUp() { fun cleanUp() {
Configuration.dialectValue = null ForceDialect.none()
} }
@Test @Test
@DisplayName("byId generates correctly (PostgreSQL)") @DisplayName("byId generates correctly | PostgreSQL")
fun byIdPostgres() { fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data->>'id' = :id", "UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'id' = :id",
PatchQuery.byId<String>(tbl), "Patch query not constructed correctly" PatchQuery.byId<String>(TEST_TABLE), "Patch query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byId generates correctly (SQLite)") @DisplayName("byId generates correctly | SQLite")
fun byIdSQLite() { fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id", "UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id",
PatchQuery.byId<String>(tbl), "Patch query not constructed correctly" PatchQuery.byId<String>(TEST_TABLE), "Patch query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byFields generates correctly (PostgreSQL)") @DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() { fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data->>'z' = :y", "UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'z' = :y",
PatchQuery.byFields(tbl, listOf(Field.equal("z", "", ":y"))), PatchQuery.byFields(TEST_TABLE, listOf(Field.equal("z", "", ":y"))),
"Patch query not constructed correctly" "Patch query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byFields generates correctly (SQLite)") @DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() { fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y", "UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y",
PatchQuery.byFields(tbl, listOf(Field.equal("z", "", ":y"))), PatchQuery.byFields(TEST_TABLE, listOf(Field.equal("z", "", ":y"))),
"Patch query not constructed correctly" "Patch query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byContains generates correctly (PostgreSQL)") @DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data @> :criteria", PatchQuery.byContains(tbl), "UPDATE $TEST_TABLE SET data = data || :data WHERE data @> :criteria", PatchQuery.byContains(TEST_TABLE),
"Patch query not constructed correctly" "Patch query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails | SQLite")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { PatchQuery.byContains(tbl) } assertThrows<DocumentException> { PatchQuery.byContains(TEST_TABLE) }
} }
@Test @Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)") @DisplayName("byJsonPath generates correctly | PostgreSQL")
fun byJsonPathPostgres() { fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"UPDATE $tbl SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)", "UPDATE $TEST_TABLE SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)",
PatchQuery.byJsonPath(tbl), "Patch query not constructed correctly" PatchQuery.byJsonPath(TEST_TABLE), "Patch query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails | SQLite")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { PatchQuery.byJsonPath(tbl) } assertThrows<DocumentException> { PatchQuery.byJsonPath(TEST_TABLE) }
} }
} }

View File

@ -3,10 +3,10 @@ package solutions.bitbadger.documents.query
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.Test import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.support.ForceDialect
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
@ -20,7 +20,7 @@ class QueryTest {
*/ */
@AfterEach @AfterEach
fun cleanUp() { fun cleanUp() {
Configuration.dialectValue = null ForceDialect.none()
} }
@Test @Test
@ -29,30 +29,30 @@ class QueryTest {
assertEquals("x WHERE y", statementWhere("x", "y"), "Statements not combined correctly") assertEquals("x WHERE y", statementWhere("x", "y"), "Statements not combined correctly")
@Test @Test
@DisplayName("byId generates a numeric ID query (PostgreSQL)") @DisplayName("byId generates a numeric ID query | PostgreSQL")
fun byIdNumericPostgres() { fun byIdNumericPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals("test WHERE (data->>'id')::numeric = :id", byId("test", 9)) assertEquals("test WHERE (data->>'id')::numeric = :id", byId("test", 9))
} }
@Test @Test
@DisplayName("byId generates an alphanumeric ID query (PostgreSQL)") @DisplayName("byId generates an alphanumeric ID query | PostgreSQL")
fun byIdAlphaPostgres() { fun byIdAlphaPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals("unit WHERE data->>'id' = :id", byId("unit", "18")) assertEquals("unit WHERE data->>'id' = :id", byId("unit", "18"))
} }
@Test @Test
@DisplayName("byId generates ID query (SQLite)") @DisplayName("byId generates ID query | SQLite")
fun byIdSQLite() { fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals("yo WHERE data->>'id' = :id", byId("yo", 27)) assertEquals("yo WHERE data->>'id' = :id", byId("yo", 27))
} }
@Test @Test
@DisplayName("byFields generates default field query (PostgreSQL)") @DisplayName("byFields generates default field query | PostgreSQL")
fun byFieldsMultipleDefaultPostgres() { fun byFieldsMultipleDefaultPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"this WHERE data->>'a' = :the_a AND (data->>'b')::numeric = :b_value", "this WHERE data->>'a' = :the_a AND (data->>'b')::numeric = :b_value",
byFields("this", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value"))) byFields("this", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")))
@ -60,9 +60,9 @@ class QueryTest {
} }
@Test @Test
@DisplayName("byFields generates default field query (SQLite)") @DisplayName("byFields generates default field query | SQLite")
fun byFieldsMultipleDefaultSQLite() { fun byFieldsMultipleDefaultSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"this WHERE data->>'a' = :the_a AND data->>'b' = :b_value", "this WHERE data->>'a' = :the_a AND data->>'b' = :b_value",
byFields("this", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value"))) byFields("this", listOf(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")))
@ -70,9 +70,9 @@ class QueryTest {
} }
@Test @Test
@DisplayName("byFields generates ANY field query (PostgreSQL)") @DisplayName("byFields generates ANY field query | PostgreSQL")
fun byFieldsMultipleAnyPostgres() { fun byFieldsMultipleAnyPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value", "that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value",
byFields( byFields(
@ -83,9 +83,9 @@ class QueryTest {
} }
@Test @Test
@DisplayName("byFields generates ANY field query (SQLite)") @DisplayName("byFields generates ANY field query | SQLite")
fun byFieldsMultipleAnySQLite() { fun byFieldsMultipleAnySQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"that WHERE data->>'a' = :the_a OR data->>'b' = :b_value", "that WHERE data->>'a' = :the_a OR data->>'b' = :b_value",
byFields( byFields(
@ -103,7 +103,7 @@ class QueryTest {
} }
@Test @Test
@DisplayName("orderBy generates single, no direction for PostgreSQL") @DisplayName("orderBy generates single, no direction | PostgreSQL")
fun orderBySinglePostgres() = fun orderBySinglePostgres() =
assertEquals( assertEquals(
" ORDER BY data->>'TestField'", " ORDER BY data->>'TestField'",
@ -111,7 +111,7 @@ class QueryTest {
) )
@Test @Test
@DisplayName("orderBy generates single, no direction for SQLite") @DisplayName("orderBy generates single, no direction | SQLite")
fun orderBySingleSQLite() = fun orderBySingleSQLite() =
assertEquals( assertEquals(
" ORDER BY data->>'TestField'", orderBy(listOf(Field.named("TestField")), Dialect.SQLITE), " ORDER BY data->>'TestField'", orderBy(listOf(Field.named("TestField")), Dialect.SQLITE),
@ -119,7 +119,7 @@ class QueryTest {
) )
@Test @Test
@DisplayName("orderBy generates multiple with direction for PostgreSQL") @DisplayName("orderBy generates multiple with direction | PostgreSQL")
fun orderByMultiplePostgres() = fun orderByMultiplePostgres() =
assertEquals( assertEquals(
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC", " ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
@ -131,7 +131,7 @@ class QueryTest {
) )
@Test @Test
@DisplayName("orderBy generates multiple with direction for SQLite") @DisplayName("orderBy generates multiple with direction | SQLite")
fun orderByMultipleSQLite() = fun orderByMultipleSQLite() =
assertEquals( assertEquals(
" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC", " ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
@ -143,7 +143,7 @@ class QueryTest {
) )
@Test @Test
@DisplayName("orderBy generates numeric ordering PostgreSQL") @DisplayName("orderBy generates numeric ordering | PostgreSQL")
fun orderByNumericPostgres() = fun orderByNumericPostgres() =
assertEquals( assertEquals(
" ORDER BY (data->>'Test')::numeric", " ORDER BY (data->>'Test')::numeric",
@ -151,7 +151,7 @@ class QueryTest {
) )
@Test @Test
@DisplayName("orderBy generates numeric ordering for SQLite") @DisplayName("orderBy generates numeric ordering | SQLite")
fun orderByNumericSQLite() = fun orderByNumericSQLite() =
assertEquals( assertEquals(
" ORDER BY data->>'Test'", orderBy(listOf(Field.named("n:Test")), Dialect.SQLITE), " ORDER BY data->>'Test'", orderBy(listOf(Field.named("n:Test")), Dialect.SQLITE),
@ -159,7 +159,7 @@ class QueryTest {
) )
@Test @Test
@DisplayName("orderBy generates case-insensitive ordering for PostgreSQL") @DisplayName("orderBy generates case-insensitive ordering | PostgreSQL")
fun orderByCIPostgres() = fun orderByCIPostgres() =
assertEquals( assertEquals(
" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST", " ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST",
@ -168,7 +168,7 @@ class QueryTest {
) )
@Test @Test
@DisplayName("orderBy generates case-insensitive ordering for SQLite") @DisplayName("orderBy generates case-insensitive ordering | SQLite")
fun orderByCISQLite() = fun orderByCISQLite() =
assertEquals( assertEquals(
" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST", " ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST",

View File

@ -5,6 +5,8 @@ import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.* import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
@ -13,36 +15,33 @@ import kotlin.test.assertEquals
@DisplayName("JVM | Kotlin | Query | RemoveFieldsQuery") @DisplayName("JVM | Kotlin | Query | RemoveFieldsQuery")
class RemoveFieldsQueryTest { class RemoveFieldsQueryTest {
/** Test table name */
private val tbl = "test_table"
/** /**
* Reset the dialect * Reset the dialect
*/ */
@AfterEach @AfterEach
fun cleanUp() { fun cleanUp() {
Configuration.dialectValue = null ForceDialect.none()
} }
@Test @Test
@DisplayName("byId generates correctly (PostgreSQL)") @DisplayName("byId generates correctly | PostgreSQL")
fun byIdPostgres() { fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data->>'id' = :id", "UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data->>'id' = :id",
RemoveFieldsQuery.byId<String>(tbl, listOf(Parameter(":name", ParameterType.STRING, "{a,z}"))), RemoveFieldsQuery.byId<String>(TEST_TABLE, listOf(Parameter(":name", ParameterType.STRING, "{a,z}"))),
"Remove Fields query not constructed correctly" "Remove Fields query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byId generates correctly (SQLite)") @DisplayName("byId generates correctly | SQLite")
fun byIdSQLite() { fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id", "UPDATE $TEST_TABLE SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id",
RemoveFieldsQuery.byId<String>( RemoveFieldsQuery.byId<String>(
tbl, TEST_TABLE,
listOf( listOf(
Parameter(":name0", ParameterType.STRING, "a"), Parameter(":name0", ParameterType.STRING, "a"),
Parameter(":name1", ParameterType.STRING, "z") Parameter(":name1", ParameterType.STRING, "z")
@ -53,13 +52,13 @@ class RemoveFieldsQueryTest {
} }
@Test @Test
@DisplayName("byFields generates correctly (PostgreSQL)") @DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() { fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data->>'f' > :g", "UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data->>'f' > :g",
RemoveFieldsQuery.byFields( RemoveFieldsQuery.byFields(
tbl, TEST_TABLE,
listOf(Parameter(":name", ParameterType.STRING, "{b,c}")), listOf(Parameter(":name", ParameterType.STRING, "{b,c}")),
listOf(Field.greater("f", "", ":g")) listOf(Field.greater("f", "", ":g"))
), ),
@ -68,13 +67,13 @@ class RemoveFieldsQueryTest {
} }
@Test @Test
@DisplayName("byFields generates correctly (SQLite)") @DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() { fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"UPDATE $tbl SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g", "UPDATE $TEST_TABLE SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g",
RemoveFieldsQuery.byFields( RemoveFieldsQuery.byFields(
tbl, TEST_TABLE,
listOf(Parameter(":name0", ParameterType.STRING, "b"), Parameter(":name1", ParameterType.STRING, "c")), listOf(Parameter(":name0", ParameterType.STRING, "b"), Parameter(":name1", ParameterType.STRING, "c")),
listOf(Field.greater("f", "", ":g")) listOf(Field.greater("f", "", ":g"))
), ),
@ -83,38 +82,38 @@ class RemoveFieldsQueryTest {
} }
@Test @Test
@DisplayName("byContains generates correctly (PostgreSQL)") @DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data @> :criteria", "UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data @> :criteria",
RemoveFieldsQuery.byContains(tbl, listOf(Parameter(":name", ParameterType.STRING, "{m,n}"))), RemoveFieldsQuery.byContains(TEST_TABLE, listOf(Parameter(":name", ParameterType.STRING, "{m,n}"))),
"Remove Field query not constructed correctly" "Remove Field query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails | SQLite")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { RemoveFieldsQuery.byContains(tbl, listOf()) } assertThrows<DocumentException> { RemoveFieldsQuery.byContains(TEST_TABLE, listOf()) }
} }
@Test @Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)") @DisplayName("byJsonPath generates correctly | PostgreSQL")
fun byJsonPathPostgres() { fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)", "UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)",
RemoveFieldsQuery.byJsonPath(tbl, listOf(Parameter(":name", ParameterType.STRING, "{o,p}"))), RemoveFieldsQuery.byJsonPath(TEST_TABLE, listOf(Parameter(":name", ParameterType.STRING, "{o,p}"))),
"Remove Field query not constructed correctly" "Remove Field query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails | SQLite")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { RemoveFieldsQuery.byJsonPath(tbl, listOf()) } assertThrows<DocumentException> { RemoveFieldsQuery.byJsonPath(TEST_TABLE, listOf()) }
} }
} }