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
import kotlin.jvm.Throws
/**
* 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)
* @return The path for the field
*/
@JvmOverloads
fun path(dialect: Dialect, format: FieldFormat = FieldFormat.SQL): String =
(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
* @throws DocumentException If the field has no parameter name or the database dialect has not been set
*/
@Throws(DocumentException::class)
fun toWhere(): String {
if (parameterName == null && !listOf(Op.EXISTS, Op.NOT_EXISTS).contains(comparison.op))
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 howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to count documents matching the given fields
* @throws DocumentException If the dialect has not been set
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
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 docId The ID of the document (optional, used for type checking)
* @return A query to delete a document by its ID
* @throws DocumentException If the dialect has not been set
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
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 howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to delete documents matching for the given fields
* @throws DocumentException If the dialect has not been set
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
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 docId The ID of the document (optional, used for type checking)
* @return A query to determine document existence by ID
* @throws DocumentException If the dialect has not been set
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun <TKey> byId(tableName: String, docId: TKey? = null) =
@ -39,7 +41,9 @@ object ExistsQuery {
* @param fields The field comparisons for the existence check
* @param howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to determine document existence for the given fields
* @throws DocumentException If the dialect has not been set
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
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 docId The ID of the document (optional, used for type checking)
* @return A query to retrieve a document by its ID
* @throws DocumentException If the dialect has not been set
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
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 howMatched How fields should be compared (optional, defaults to ALL)
* @return A query to retrieve documents matching the given fields
* @throws DocumentException If the dialect has not been set
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
fun byFields(tableName: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =

View File

@ -1,10 +1,8 @@
@file:JvmName("QueryUtils")
package solutions.bitbadger.documents.query
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.*
import kotlin.jvm.Throws
// ~~~ 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 docId The ID of the document targeted
* @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) =
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)
* @return A query addressing documents by field matching conditions
*/
@Throws(DocumentException::class)
@JvmOverloads
fun byFields(statement: String, fields: Collection<Field<*>>, howMatched: FieldMatch? = null) =
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
* @return An `ORDER BY` clause for the given fields
*/
@Throws(DocumentException::class)
@JvmOverloads
fun orderBy(fields: Collection<Field<*>>, dialect: Dialect? = null): String {
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 howMatched How the fields should be matched (optional, defaults to `ALL`)
* @return A `WHERE` clause fragment to match the given fields
* @throws DocumentException If the dialect has not been set
*/
@Throws(DocumentException::class)
@JvmStatic
@JvmOverloads
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 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
@JvmOverloads
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.Test;
import solutions.bitbadger.documents.*;
import solutions.bitbadger.documents.support.ForceDialect;
import java.util.Collection;
import java.util.List;
@ -22,7 +23,7 @@ final public class FieldTest {
*/
@AfterEach
public void cleanUp() {
Configuration.setConnectionString(null);
ForceDialect.none();
}
// ~~~ INSTANCE METHODS ~~~
@ -71,7 +72,7 @@ final public class FieldTest {
}
@Test
@DisplayName("path generates for simple unqualified PostgreSQL field")
@DisplayName("path generates for simple unqualified field | PostgreSQL")
public void pathPostgresSimpleUnqualified() {
assertEquals("data->>'SomethingCool'",
Field.greaterOrEqual("SomethingCool", 18).path(Dialect.POSTGRESQL, FieldFormat.SQL),
@ -79,7 +80,7 @@ final public class FieldTest {
}
@Test
@DisplayName("path generates for simple qualified PostgreSQL field")
@DisplayName("path generates for simple qualified field | PostgreSQL")
public void pathPostgresSimpleQualified() {
assertEquals("this.data->>'SomethingElse'",
Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.POSTGRESQL, FieldFormat.SQL),
@ -87,14 +88,14 @@ final public class FieldTest {
}
@Test
@DisplayName("path generates for nested unqualified PostgreSQL field")
@DisplayName("path generates for nested unqualified field | PostgreSQL")
public void pathPostgresNestedUnqualified() {
assertEquals("data#>>'{My,Nested,Field}'",
Field.equal("My.Nested.Field", "howdy").path(Dialect.POSTGRESQL, FieldFormat.SQL), "Path not correct");
}
@Test
@DisplayName("path generates for nested qualified PostgreSQL field")
@DisplayName("path generates for nested qualified field | PostgreSQL")
public void pathPostgresNestedQualified() {
assertEquals("bird.data#>>'{Nest,Away}'",
Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.POSTGRESQL, FieldFormat.SQL),
@ -102,14 +103,14 @@ final public class FieldTest {
}
@Test
@DisplayName("path generates for simple unqualified SQLite field")
@DisplayName("path generates for simple unqualified field | SQLite")
public void pathSQLiteSimpleUnqualified() {
assertEquals("data->>'SomethingCool'",
Field.greaterOrEqual("SomethingCool", 18).path(Dialect.SQLITE, FieldFormat.SQL), "Path not correct");
}
@Test
@DisplayName("path generates for simple qualified SQLite field")
@DisplayName("path generates for simple qualified field | SQLite")
public void pathSQLiteSimpleQualified() {
assertEquals("this.data->>'SomethingElse'",
Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.SQLITE, FieldFormat.SQL),
@ -117,14 +118,14 @@ final public class FieldTest {
}
@Test
@DisplayName("path generates for nested unqualified SQLite field")
@DisplayName("path generates for nested unqualified field | SQLite")
public void pathSQLiteNestedUnqualified() {
assertEquals("data->'My'->'Nested'->>'Field'",
Field.equal("My.Nested.Field", "howdy").path(Dialect.SQLITE, FieldFormat.SQL), "Path not correct");
}
@Test
@DisplayName("path generates for nested qualified SQLite field")
@DisplayName("path generates for nested qualified field | SQLite")
public void pathSQLiteNestedQualified() {
assertEquals("bird.data->'Nest'->>'Away'",
Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.SQLITE, FieldFormat.SQL),
@ -132,190 +133,196 @@ final public class FieldTest {
}
@Test
@DisplayName("toWhere generates for exists w/o qualifier (PostgreSQL)")
public void toWhereExistsNoQualPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates for exists w/o qualifier | PostgreSQL")
public void toWhereExistsNoQualPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("data->>'that_field' IS NOT NULL", Field.exists("that_field").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for exists w/o qualifier (SQLite)")
public void toWhereExistsNoQualSQLite() {
Configuration.setConnectionString(":sqlite:");
@DisplayName("toWhere generates for exists w/o qualifier | SQLite")
public void toWhereExistsNoQualSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("data->>'that_field' IS NOT NULL", Field.exists("that_field").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for not-exists w/o qualifier (PostgreSQL)")
public void toWhereNotExistsNoQualPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates for not-exists w/o qualifier | PostgreSQL")
public void toWhereNotExistsNoQualPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("data->>'a_field' IS NULL", Field.notExists("a_field").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for not-exists w/o qualifier (SQLite)")
public void toWhereNotExistsNoQualSQLite() {
Configuration.setConnectionString(":sqlite:");
@DisplayName("toWhere generates for not-exists w/o qualifier | SQLite")
public void toWhereNotExistsNoQualSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("data->>'a_field' IS NULL", Field.notExists("a_field").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for BETWEEN w/o qualifier, numeric range (PostgreSQL)")
public void toWhereBetweenNoQualNumericPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates for BETWEEN w/o qualifier, numeric range | PostgreSQL")
public void toWhereBetweenNoQualNumericPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("(data->>'age')::numeric BETWEEN @agemin AND @agemax",
Field.between("age", 13, 17, "@age").toWhere(), "Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for BETWEEN w/o qualifier, alphanumeric range (PostgreSQL)")
public void toWhereBetweenNoQualAlphaPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates for BETWEEN w/o qualifier, alphanumeric range | PostgreSQL")
public void toWhereBetweenNoQualAlphaPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("data->>'city' BETWEEN :citymin AND :citymax",
Field.between("city", "Atlanta", "Chicago", ":city").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for BETWEEN w/o qualifier (SQLite)")
public void toWhereBetweenNoQualSQLite() {
Configuration.setConnectionString(":sqlite:");
@DisplayName("toWhere generates for BETWEEN w/o qualifier | SQLite")
public void toWhereBetweenNoQualSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("data->>'age' BETWEEN @agemin AND @agemax", Field.between("age", 13, 17, "@age").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for BETWEEN w/ qualifier, numeric range (PostgreSQL)")
public void toWhereBetweenQualNumericPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates for BETWEEN w/ qualifier, numeric range | PostgreSQL")
public void toWhereBetweenQualNumericPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("(test.data->>'age')::numeric BETWEEN @agemin AND @agemax",
Field.between("age", 13, 17, "@age").withQualifier("test").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for BETWEEN w/ qualifier, alphanumeric range (PostgreSQL)")
public void toWhereBetweenQualAlphaPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates for BETWEEN w/ qualifier, alphanumeric range | PostgreSQL")
public void toWhereBetweenQualAlphaPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("unit.data->>'city' BETWEEN :citymin AND :citymax",
Field.between("city", "Atlanta", "Chicago", ":city").withQualifier("unit").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for BETWEEN w/ qualifier (SQLite)")
public void toWhereBetweenQualSQLite() {
Configuration.setConnectionString(":sqlite:");
@DisplayName("toWhere generates for BETWEEN w/ qualifier | SQLite")
public void toWhereBetweenQualSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("my.data->>'age' BETWEEN @agemin AND @agemax",
Field.between("age", 13, 17, "@age").withQualifier("my").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for IN/any, numeric values (PostgreSQL)")
public void toWhereAnyNumericPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates for IN/any, numeric values | PostgreSQL")
public void toWhereAnyNumericPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)",
Field.any("even", List.of(2, 4, 6), ":nbr").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for IN/any, alphanumeric values (PostgreSQL)")
public void toWhereAnyAlphaPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates for IN/any, alphanumeric values | PostgreSQL")
public void toWhereAnyAlphaPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("data->>'test' IN (:city_0, :city_1)",
Field.any("test", List.of("Atlanta", "Chicago"), ":city").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for IN/any (SQLite)")
public void toWhereAnySQLite() {
Configuration.setConnectionString(":sqlite:");
@DisplayName("toWhere generates for IN/any | SQLite")
public void toWhereAnySQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("data->>'test' IN (:city_0, :city_1)",
Field.any("test", List.of("Atlanta", "Chicago"), ":city").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for inArray (PostgreSQL)")
public void toWhereInArrayPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates for inArray | PostgreSQL")
public void toWhereInArrayPostgres() throws DocumentException {
ForceDialect.postgres();
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 WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for inArray (SQLite)")
public void toWhereInArraySQLite() {
Configuration.setConnectionString(":sqlite:");
@DisplayName("toWhere generates for inArray | SQLite")
public void toWhereInArraySQLite() throws DocumentException {
ForceDialect.sqlite();
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 WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for others w/o qualifier (PostgreSQL)")
public void toWhereOtherNoQualPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates for others w/o qualifier | PostgreSQL")
public void toWhereOtherNoQualPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("data->>'some_field' = :value", Field.equal("some_field", "", ":value").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates for others w/o qualifier (SQLite)")
public void toWhereOtherNoQualSQLite() {
Configuration.setConnectionString(":sqlite:");
@DisplayName("toWhere generates for others w/o qualifier | SQLite")
public void toWhereOtherNoQualSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("data->>'some_field' = :value", Field.equal("some_field", "", ":value").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates no-parameter w/ qualifier (PostgreSQL)")
public void toWhereNoParamWithQualPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates no-parameter w/ qualifier | PostgreSQL")
public void toWhereNoParamWithQualPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("test.data->>'no_field' IS NOT NULL", Field.exists("no_field").withQualifier("test").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates no-parameter w/ qualifier (SQLite)")
public void toWhereNoParamWithQualSQLite() {
Configuration.setConnectionString(":sqlite:");
@DisplayName("toWhere generates no-parameter w/ qualifier | SQLite")
public void toWhereNoParamWithQualSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("test.data->>'no_field' IS NOT NULL", Field.exists("no_field").withQualifier("test").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates parameter w/ qualifier (PostgreSQL)")
public void toWhereParamWithQualPostgres() {
Configuration.setConnectionString(":postgresql:");
@DisplayName("toWhere generates parameter w/ qualifier | PostgreSQL")
public void toWhereParamWithQualPostgres() throws DocumentException {
ForceDialect.postgres();
assertEquals("(q.data->>'le_field')::numeric <= :it",
Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere(),
"Field WHERE clause not generated correctly");
}
@Test
@DisplayName("toWhere generates parameter w/ qualifier (SQLite)")
public void toWhereParamWithQualSQLite() {
Configuration.setConnectionString(":sqlite:");
@DisplayName("toWhere generates parameter w/ qualifier | SQLite")
public void toWhereParamWithQualSQLite() throws DocumentException {
ForceDialect.sqlite();
assertEquals("q.data->>'le_field' <= :it",
Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere(),
"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 ~~~
@Test
@DisplayName("equal constructs a field w/o parameter name")
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(Op.EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(14, field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -326,7 +333,7 @@ final public class FieldTest {
@Test
@DisplayName("equal constructs a field w/ parameter name")
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(Op.EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(14, field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -337,7 +344,7 @@ final public class FieldTest {
@Test
@DisplayName("greater constructs a field w/o parameter name")
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(Op.GREATER, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("night", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -348,7 +355,7 @@ final public class FieldTest {
@Test
@DisplayName("greater constructs a field w/ parameter name")
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(Op.GREATER, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("night", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -359,7 +366,7 @@ final public class FieldTest {
@Test
@DisplayName("greaterOrEqual constructs a field w/o parameter name")
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(Op.GREATER_OR_EQUAL, field.getComparison().getOp(),
"Field comparison operation not filled correctly");
@ -371,7 +378,7 @@ final public class FieldTest {
@Test
@DisplayName("greaterOrEqual constructs a field w/ parameter name")
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(Op.GREATER_OR_EQUAL, field.getComparison().getOp(),
"Field comparison operation not filled correctly");
@ -383,7 +390,7 @@ final public class FieldTest {
@Test
@DisplayName("less constructs a field w/o parameter name")
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(Op.LESS, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("seven", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -394,7 +401,7 @@ final public class FieldTest {
@Test
@DisplayName("less constructs a field w/ parameter name")
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(Op.LESS, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("seven", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -405,7 +412,7 @@ final public class FieldTest {
@Test
@DisplayName("lessOrEqual constructs a field w/o parameter name")
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(Op.LESS_OR_EQUAL, field.getComparison().getOp(),
"Field comparison operation not filled correctly");
@ -417,7 +424,7 @@ final public class FieldTest {
@Test
@DisplayName("lessOrEqual constructs a field w/ parameter name")
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(Op.LESS_OR_EQUAL, field.getComparison().getOp(),
"Field comparison operation not filled correctly");
@ -429,7 +436,7 @@ final public class FieldTest {
@Test
@DisplayName("notEqual constructs a field w/o parameter name")
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(Op.NOT_EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("here", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -440,7 +447,7 @@ final public class FieldTest {
@Test
@DisplayName("notEqual constructs a field w/ parameter name")
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(Op.NOT_EQUAL, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("here", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -451,7 +458,7 @@ final public class FieldTest {
@Test
@DisplayName("between constructs a field w/o parameter name")
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(Op.BETWEEN, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(18, field.getComparison().getValue().getFirst(),
@ -465,7 +472,7 @@ final public class FieldTest {
@Test
@DisplayName("between constructs a field w/ parameter name")
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(Op.BETWEEN, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(18, field.getComparison().getValue().getFirst(),
@ -479,7 +486,7 @@ final public class FieldTest {
@Test
@DisplayName("any constructs a field w/o parameter name")
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(Op.IN, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(List.of(8, 16, 32), field.getComparison().getValue(),
@ -491,7 +498,7 @@ final public class FieldTest {
@Test
@DisplayName("any constructs a field w/ parameter name")
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(Op.IN, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals(List.of(8, 16, 32), field.getComparison().getValue(),
@ -503,7 +510,7 @@ final public class FieldTest {
@Test
@DisplayName("inArray constructs a field w/o parameter name")
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(Op.IN_ARRAY, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("table", field.getComparison().getValue().getFirst(),
@ -517,7 +524,7 @@ final public class FieldTest {
@Test
@DisplayName("inArray constructs a field w/ parameter name")
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(Op.IN_ARRAY, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("table", field.getComparison().getValue().getFirst(),
@ -531,7 +538,7 @@ final public class FieldTest {
@Test
@DisplayName("exists constructs a field")
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(Op.EXISTS, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -542,7 +549,7 @@ final public class FieldTest {
@Test
@DisplayName("notExists constructs a field")
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(Op.NOT_EXISTS, field.getComparison().getOp(), "Field comparison operation not filled correctly");
assertEquals("", field.getComparison().getValue(), "Field comparison value not filled correctly");
@ -553,7 +560,7 @@ final public class FieldTest {
@Test
@DisplayName("named constructs a field")
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(Op.EQUAL, field.getComparison().getOp(), "Field comparison operation 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
*/
@DisplayName("JVM | Java | Query | CountQuery")
public class CountQueryTest {
final public class CountQueryTest {
/**
* Clear the connection string (resets Dialect)
@ -37,7 +37,7 @@ public class CountQueryTest {
@Test
@DisplayName("byFields generates correctly | PostgreSQL")
public void byFieldsPostgres() {
public void byFieldsPostgres() throws DocumentException {
ForceDialect.postgres();
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"))),
@ -46,7 +46,7 @@ public class CountQueryTest {
@Test
@DisplayName("byFields generates correctly | SQLite")
public void byFieldsSQLite() {
public void byFieldsSQLite() throws DocumentException {
ForceDialect.sqlite();
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"))),

View File

@ -19,7 +19,7 @@ import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE;
* Unit tests for the `Definition` object
*/
@DisplayName("JVM | Java | Query | DefinitionQuery")
public class DefinitionQueryTest {
final public class DefinitionQueryTest {
/**
* 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.Test
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals
/**
@ -16,87 +16,89 @@ import kotlin.test.assertEquals
@DisplayName("JVM | Kotlin | Query | DeleteQuery")
class DeleteQueryTest {
/** Test table name */
private val tbl = "test_table"
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
fun cleanUp() {
Configuration.dialectValue = null
ForceDialect.none()
}
@Test
@DisplayName("byId generates correctly (PostgreSQL)")
@DisplayName("byId generates correctly | PostgreSQL")
fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"DELETE FROM $tbl WHERE data->>'id' = :id",
DeleteQuery.byId<String>(tbl), "Delete query not constructed correctly"
"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id",
DeleteQuery.byId<String>(TEST_TABLE), "Delete query not constructed correctly"
)
}
@Test
@DisplayName("byId generates correctly (SQLite)")
@DisplayName("byId generates correctly | SQLite")
fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals(
"DELETE FROM $tbl WHERE data->>'id' = :id",
DeleteQuery.byId<String>(tbl), "Delete query not constructed correctly"
"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id",
DeleteQuery.byId<String>(TEST_TABLE), "Delete query not constructed correctly"
)
}
@Test
@DisplayName("byFields generates correctly (PostgreSQL)")
@DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
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"
)
}
@Test
@DisplayName("byFields generates correctly (SQLite)")
@DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
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"
)
}
@Test
@DisplayName("byContains generates correctly (PostgreSQL)")
@DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"DELETE FROM $tbl WHERE data @> :criteria", DeleteQuery.byContains(tbl), "Delete query not constructed correctly"
)
}
@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 FROM $TEST_TABLE WHERE data @> :criteria",
DeleteQuery.byContains(TEST_TABLE),
"Delete query not constructed correctly"
)
}
@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() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { DeleteQuery.byJsonPath(tbl) }
ForceDialect.sqlite()
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 solutions.bitbadger.documents.AutoId
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
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.assertTrue
@ -17,92 +18,90 @@ import kotlin.test.assertTrue
@DisplayName("JVM | Kotlin | Query | DocumentQuery")
class DocumentQueryTest {
/** Test table name */
private val tbl = "test_table"
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
fun cleanUp() {
Configuration.dialectValue = null
ForceDialect.none()
}
@Test
@DisplayName("insert generates no auto ID (PostgreSQL)")
@DisplayName("insert generates no auto ID | PostgreSQL")
fun insertNoAutoPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
assertEquals("INSERT INTO $tbl VALUES (:data)", DocumentQuery.insert(tbl))
ForceDialect.postgres()
assertEquals("INSERT INTO $TEST_TABLE VALUES (:data)", DocumentQuery.insert(TEST_TABLE))
}
@Test
@DisplayName("insert generates no auto ID (SQLite)")
@DisplayName("insert generates no auto ID | SQLite")
fun insertNoAutoSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertEquals("INSERT INTO $tbl VALUES (:data)", DocumentQuery.insert(tbl))
ForceDialect.sqlite()
assertEquals("INSERT INTO $TEST_TABLE VALUES (:data)", DocumentQuery.insert(TEST_TABLE))
}
@Test
@DisplayName("insert generates auto number (PostgreSQL)")
@DisplayName("insert generates auto number | PostgreSQL")
fun insertAutoNumberPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"INSERT INTO $tbl VALUES (:data::jsonb || ('{\"id\":' " +
"|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $tbl) || '}')::jsonb)",
DocumentQuery.insert(tbl, AutoId.NUMBER)
"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || ('{\"id\":' " +
"|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $TEST_TABLE) || '}')::jsonb)",
DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER)
)
}
@Test
@DisplayName("insert generates auto number (SQLite)")
@DisplayName("insert generates auto number | SQLite")
fun insertAutoNumberSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals(
"INSERT INTO $tbl VALUES (json_set(:data, '$.id', " +
"(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $tbl)))",
DocumentQuery.insert(tbl, AutoId.NUMBER)
"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$.id', " +
"(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $TEST_TABLE)))",
DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER)
)
}
@Test
@DisplayName("insert generates auto UUID (PostgreSQL)")
@DisplayName("insert generates auto UUID | PostgreSQL")
fun insertAutoUUIDPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
val query = DocumentQuery.insert(tbl, AutoId.UUID)
ForceDialect.postgres()
val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
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)"
)
assertTrue(query.endsWith("\"}')"), "Query end not correct")
}
@Test
@DisplayName("insert generates auto UUID (SQLite)")
@DisplayName("insert generates auto UUID | SQLite")
fun insertAutoUUIDSQLite() {
Configuration.dialectValue = Dialect.SQLITE
val query = DocumentQuery.insert(tbl, AutoId.UUID)
ForceDialect.sqlite()
val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
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)"
)
assertTrue(query.endsWith("'))"), "Query end not correct")
}
@Test
@DisplayName("insert generates auto random string (PostgreSQL)")
@DisplayName("insert generates auto random string | PostgreSQL")
fun insertAutoRandomPostgres() {
try {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
Configuration.idStringLength = 8
val query = DocumentQuery.insert(tbl, AutoId.RANDOM_STRING)
val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
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)"
)
assertTrue(query.endsWith("\"}')"), "Query end not correct")
assertEquals(
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"
)
} finally {
@ -111,18 +110,18 @@ class DocumentQueryTest {
}
@Test
@DisplayName("insert generates auto random string (SQLite)")
@DisplayName("insert generates auto random string | SQLite")
fun insertAutoRandomSQLite() {
Configuration.dialectValue = Dialect.SQLITE
val query = DocumentQuery.insert(tbl, AutoId.RANDOM_STRING)
ForceDialect.sqlite()
val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
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)"
)
assertTrue(query.endsWith("'))"), "Query end not correct")
assertEquals(
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"
)
}
@ -130,21 +129,25 @@ class DocumentQueryTest {
@Test
@DisplayName("insert fails when no dialect is set")
fun insertFailsUnknown() {
assertThrows<DocumentException> { DocumentQuery.insert(tbl) }
assertThrows<DocumentException> { DocumentQuery.insert(TEST_TABLE) }
}
@Test
@DisplayName("save generates correctly")
fun save() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"INSERT INTO $tbl VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data",
DocumentQuery.save(tbl), "INSERT ON CONFLICT UPDATE statement not constructed correctly"
"INSERT INTO $TEST_TABLE VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data",
DocumentQuery.save(TEST_TABLE), "INSERT ON CONFLICT UPDATE statement not constructed correctly"
)
}
@Test
@DisplayName("update generates successfully")
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.DocumentException
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals
/**
* Unit tests for the `Exists` object
*/
@DisplayName("Kotlin | Common | Query: Exists")
@DisplayName("JVM | Kotlin | Query | ExistsQuery")
class ExistsQueryTest {
/** Test table name */
private val tbl = "test_table"
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
fun cleanUp() {
Configuration.dialectValue = null
ForceDialect.none()
}
@Test
@DisplayName("byId generates correctly (PostgreSQL)")
@DisplayName("byId generates correctly | PostgreSQL")
fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'id' = :id) AS it",
ExistsQuery.byId<String>(tbl), "Exists query not constructed correctly"
"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it",
ExistsQuery.byId<String>(TEST_TABLE), "Exists query not constructed correctly"
)
}
@Test
@DisplayName("byId generates correctly (SQLite)")
@DisplayName("byId generates correctly | SQLite")
fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'id' = :id) AS it",
ExistsQuery.byId<String>(tbl), "Exists query not constructed correctly"
"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it",
ExistsQuery.byId<String>(TEST_TABLE), "Exists query not constructed correctly"
)
}
@Test
@DisplayName("byFields generates correctly (PostgreSQL)")
@DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE (data->>'it')::numeric = :test) AS it",
ExistsQuery.byFields(tbl, listOf(Field.equal("it", 7, ":test"))),
"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE (data->>'it')::numeric = :test) AS it",
ExistsQuery.byFields(TEST_TABLE, listOf(Field.equal("it", 7, ":test"))),
"Exists query not constructed correctly"
)
}
@Test
@DisplayName("byFields generates correctly (SQLite)")
@DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE data->>'it' = :test) AS it",
ExistsQuery.byFields(tbl, listOf(Field.equal("it", 7, ":test"))),
"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'it' = :test) AS it",
ExistsQuery.byFields(TEST_TABLE, listOf(Field.equal("it", 7, ":test"))),
"Exists query not constructed correctly"
)
}
@Test
@DisplayName("byContains generates correctly (PostgreSQL)")
@DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
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"
)
}
@Test
@DisplayName("byContains fails (SQLite)")
@DisplayName("byContains fails | SQLite")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { ExistsQuery.byContains(tbl) }
ForceDialect.sqlite()
assertThrows<DocumentException> { ExistsQuery.byContains(TEST_TABLE) }
}
@Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)")
@DisplayName("byJsonPath generates correctly | PostgreSQL")
fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"SELECT EXISTS (SELECT 1 FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)) AS it",
ExistsQuery.byJsonPath(tbl), "Exists query not constructed correctly"
"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)) AS it",
ExistsQuery.byJsonPath(TEST_TABLE), "Exists query not constructed correctly"
)
}
@Test
@DisplayName("byJsonPath fails (SQLite)")
@DisplayName("byJsonPath fails | SQLite")
fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { ExistsQuery.byJsonPath(tbl) }
ForceDialect.sqlite()
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.Test
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals
@ -22,7 +21,7 @@ class FindQueryTest {
*/
@AfterEach
fun cleanUp() {
Configuration.dialectValue = null
ForceDialect.none()
}
@Test
@ -31,9 +30,9 @@ class FindQueryTest {
assertEquals("SELECT data FROM $TEST_TABLE", FindQuery.all(TEST_TABLE), "Find query not constructed correctly")
@Test
@DisplayName("byId generates correctly (PostgreSQL)")
@DisplayName("byId generates correctly | PostgreSQL")
fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id",
FindQuery.byId<String>(TEST_TABLE), "Find query not constructed correctly"
@ -41,9 +40,9 @@ class FindQueryTest {
}
@Test
@DisplayName("byId generates correctly (SQLite)")
@DisplayName("byId generates correctly | SQLite")
fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals(
"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id",
FindQuery.byId<String>(TEST_TABLE), "Find query not constructed correctly"
@ -51,9 +50,9 @@ class FindQueryTest {
}
@Test
@DisplayName("byFields generates correctly (PostgreSQL)")
@DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"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"))),
@ -62,9 +61,9 @@ class FindQueryTest {
}
@Test
@DisplayName("byFields generates correctly (SQLite)")
@DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals(
"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"))),
@ -73,9 +72,9 @@ class FindQueryTest {
}
@Test
@DisplayName("byContains generates correctly (PostgreSQL)")
@DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"SELECT data FROM $TEST_TABLE WHERE data @> :criteria", FindQuery.byContains(TEST_TABLE),
"Find query not constructed correctly"
@ -83,16 +82,16 @@ class FindQueryTest {
}
@Test
@DisplayName("byContains fails (SQLite)")
@DisplayName("byContains fails | SQLite")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertThrows<DocumentException> { FindQuery.byContains(TEST_TABLE) }
}
@Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)")
@DisplayName("byJsonPath generates correctly | PostgreSQL")
fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"SELECT data FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
FindQuery.byJsonPath(TEST_TABLE),
@ -101,9 +100,9 @@ class FindQueryTest {
}
@Test
@DisplayName("byJsonPath fails (SQLite)")
@DisplayName("byJsonPath fails | SQLite")
fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
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.Test
import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals
/**
@ -16,90 +16,87 @@ import kotlin.test.assertEquals
@DisplayName("JVM | Kotlin | Query | PatchQuery")
class PatchQueryTest {
/** Test table name */
private val tbl = "test_table"
/**
* Reset the dialect
*/
@AfterEach
fun cleanUp() {
Configuration.dialectValue = null
ForceDialect.none()
}
@Test
@DisplayName("byId generates correctly (PostgreSQL)")
@DisplayName("byId generates correctly | PostgreSQL")
fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data->>'id' = :id",
PatchQuery.byId<String>(tbl), "Patch query not constructed correctly"
"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'id' = :id",
PatchQuery.byId<String>(TEST_TABLE), "Patch query not constructed correctly"
)
}
@Test
@DisplayName("byId generates correctly (SQLite)")
@DisplayName("byId generates correctly | SQLite")
fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals(
"UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id",
PatchQuery.byId<String>(tbl), "Patch query not constructed correctly"
"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id",
PatchQuery.byId<String>(TEST_TABLE), "Patch query not constructed correctly"
)
}
@Test
@DisplayName("byFields generates correctly (PostgreSQL)")
@DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"UPDATE $tbl SET data = data || :data WHERE data->>'z' = :y",
PatchQuery.byFields(tbl, listOf(Field.equal("z", "", ":y"))),
"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'z' = :y",
PatchQuery.byFields(TEST_TABLE, listOf(Field.equal("z", "", ":y"))),
"Patch query not constructed correctly"
)
}
@Test
@DisplayName("byFields generates correctly (SQLite)")
@DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals(
"UPDATE $tbl SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y",
PatchQuery.byFields(tbl, listOf(Field.equal("z", "", ":y"))),
"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y",
PatchQuery.byFields(TEST_TABLE, listOf(Field.equal("z", "", ":y"))),
"Patch query not constructed correctly"
)
}
@Test
@DisplayName("byContains generates correctly (PostgreSQL)")
@DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
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"
)
}
@Test
@DisplayName("byContains fails (SQLite)")
@DisplayName("byContains fails | SQLite")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { PatchQuery.byContains(tbl) }
ForceDialect.sqlite()
assertThrows<DocumentException> { PatchQuery.byContains(TEST_TABLE) }
}
@Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)")
@DisplayName("byJsonPath generates correctly | PostgreSQL")
fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"UPDATE $tbl SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)",
PatchQuery.byJsonPath(tbl), "Patch query not constructed correctly"
"UPDATE $TEST_TABLE SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)",
PatchQuery.byJsonPath(TEST_TABLE), "Patch query not constructed correctly"
)
}
@Test
@DisplayName("byJsonPath fails (SQLite)")
@DisplayName("byJsonPath fails | SQLite")
fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { PatchQuery.byJsonPath(tbl) }
ForceDialect.sqlite()
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.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.support.ForceDialect
import kotlin.test.assertEquals
/**
@ -20,7 +20,7 @@ class QueryTest {
*/
@AfterEach
fun cleanUp() {
Configuration.dialectValue = null
ForceDialect.none()
}
@Test
@ -29,30 +29,30 @@ class QueryTest {
assertEquals("x WHERE y", statementWhere("x", "y"), "Statements not combined correctly")
@Test
@DisplayName("byId generates a numeric ID query (PostgreSQL)")
@DisplayName("byId generates a numeric ID query | PostgreSQL")
fun byIdNumericPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals("test WHERE (data->>'id')::numeric = :id", byId("test", 9))
}
@Test
@DisplayName("byId generates an alphanumeric ID query (PostgreSQL)")
@DisplayName("byId generates an alphanumeric ID query | PostgreSQL")
fun byIdAlphaPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals("unit WHERE data->>'id' = :id", byId("unit", "18"))
}
@Test
@DisplayName("byId generates ID query (SQLite)")
@DisplayName("byId generates ID query | SQLite")
fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals("yo WHERE data->>'id' = :id", byId("yo", 27))
}
@Test
@DisplayName("byFields generates default field query (PostgreSQL)")
@DisplayName("byFields generates default field query | PostgreSQL")
fun byFieldsMultipleDefaultPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"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")))
@ -60,9 +60,9 @@ class QueryTest {
}
@Test
@DisplayName("byFields generates default field query (SQLite)")
@DisplayName("byFields generates default field query | SQLite")
fun byFieldsMultipleDefaultSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals(
"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")))
@ -70,9 +70,9 @@ class QueryTest {
}
@Test
@DisplayName("byFields generates ANY field query (PostgreSQL)")
@DisplayName("byFields generates ANY field query | PostgreSQL")
fun byFieldsMultipleAnyPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value",
byFields(
@ -83,9 +83,9 @@ class QueryTest {
}
@Test
@DisplayName("byFields generates ANY field query (SQLite)")
@DisplayName("byFields generates ANY field query | SQLite")
fun byFieldsMultipleAnySQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
assertEquals(
"that WHERE data->>'a' = :the_a OR data->>'b' = :b_value",
byFields(
@ -103,7 +103,7 @@ class QueryTest {
}
@Test
@DisplayName("orderBy generates single, no direction for PostgreSQL")
@DisplayName("orderBy generates single, no direction | PostgreSQL")
fun orderBySinglePostgres() =
assertEquals(
" ORDER BY data->>'TestField'",
@ -111,7 +111,7 @@ class QueryTest {
)
@Test
@DisplayName("orderBy generates single, no direction for SQLite")
@DisplayName("orderBy generates single, no direction | SQLite")
fun orderBySingleSQLite() =
assertEquals(
" ORDER BY data->>'TestField'", orderBy(listOf(Field.named("TestField")), Dialect.SQLITE),
@ -119,7 +119,7 @@ class QueryTest {
)
@Test
@DisplayName("orderBy generates multiple with direction for PostgreSQL")
@DisplayName("orderBy generates multiple with direction | PostgreSQL")
fun orderByMultiplePostgres() =
assertEquals(
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
@ -131,7 +131,7 @@ class QueryTest {
)
@Test
@DisplayName("orderBy generates multiple with direction for SQLite")
@DisplayName("orderBy generates multiple with direction | SQLite")
fun orderByMultipleSQLite() =
assertEquals(
" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
@ -143,7 +143,7 @@ class QueryTest {
)
@Test
@DisplayName("orderBy generates numeric ordering PostgreSQL")
@DisplayName("orderBy generates numeric ordering | PostgreSQL")
fun orderByNumericPostgres() =
assertEquals(
" ORDER BY (data->>'Test')::numeric",
@ -151,7 +151,7 @@ class QueryTest {
)
@Test
@DisplayName("orderBy generates numeric ordering for SQLite")
@DisplayName("orderBy generates numeric ordering | SQLite")
fun orderByNumericSQLite() =
assertEquals(
" ORDER BY data->>'Test'", orderBy(listOf(Field.named("n:Test")), Dialect.SQLITE),
@ -159,7 +159,7 @@ class QueryTest {
)
@Test
@DisplayName("orderBy generates case-insensitive ordering for PostgreSQL")
@DisplayName("orderBy generates case-insensitive ordering | PostgreSQL")
fun orderByCIPostgres() =
assertEquals(
" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST",
@ -168,7 +168,7 @@ class QueryTest {
)
@Test
@DisplayName("orderBy generates case-insensitive ordering for SQLite")
@DisplayName("orderBy generates case-insensitive ordering | SQLite")
fun orderByCISQLite() =
assertEquals(
" 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.assertThrows
import solutions.bitbadger.documents.*
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals
/**
@ -13,36 +15,33 @@ import kotlin.test.assertEquals
@DisplayName("JVM | Kotlin | Query | RemoveFieldsQuery")
class RemoveFieldsQueryTest {
/** Test table name */
private val tbl = "test_table"
/**
* Reset the dialect
*/
@AfterEach
fun cleanUp() {
Configuration.dialectValue = null
ForceDialect.none()
}
@Test
@DisplayName("byId generates correctly (PostgreSQL)")
@DisplayName("byId generates correctly | PostgreSQL")
fun byIdPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data->>'id' = :id",
RemoveFieldsQuery.byId<String>(tbl, listOf(Parameter(":name", ParameterType.STRING, "{a,z}"))),
"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data->>'id' = :id",
RemoveFieldsQuery.byId<String>(TEST_TABLE, listOf(Parameter(":name", ParameterType.STRING, "{a,z}"))),
"Remove Fields query not constructed correctly"
)
}
@Test
@DisplayName("byId generates correctly (SQLite)")
@DisplayName("byId generates correctly | SQLite")
fun byIdSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
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>(
tbl,
TEST_TABLE,
listOf(
Parameter(":name0", ParameterType.STRING, "a"),
Parameter(":name1", ParameterType.STRING, "z")
@ -53,13 +52,13 @@ class RemoveFieldsQueryTest {
}
@Test
@DisplayName("byFields generates correctly (PostgreSQL)")
@DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
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(
tbl,
TEST_TABLE,
listOf(Parameter(":name", ParameterType.STRING, "{b,c}")),
listOf(Field.greater("f", "", ":g"))
),
@ -68,13 +67,13 @@ class RemoveFieldsQueryTest {
}
@Test
@DisplayName("byFields generates correctly (SQLite)")
@DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
ForceDialect.sqlite()
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(
tbl,
TEST_TABLE,
listOf(Parameter(":name0", ParameterType.STRING, "b"), Parameter(":name1", ParameterType.STRING, "c")),
listOf(Field.greater("f", "", ":g"))
),
@ -83,38 +82,38 @@ class RemoveFieldsQueryTest {
}
@Test
@DisplayName("byContains generates correctly (PostgreSQL)")
@DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE data @> :criteria",
RemoveFieldsQuery.byContains(tbl, listOf(Parameter(":name", ParameterType.STRING, "{m,n}"))),
"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data @> :criteria",
RemoveFieldsQuery.byContains(TEST_TABLE, listOf(Parameter(":name", ParameterType.STRING, "{m,n}"))),
"Remove Field query not constructed correctly"
)
}
@Test
@DisplayName("byContains fails (SQLite)")
@DisplayName("byContains fails | SQLite")
fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { RemoveFieldsQuery.byContains(tbl, listOf()) }
ForceDialect.sqlite()
assertThrows<DocumentException> { RemoveFieldsQuery.byContains(TEST_TABLE, listOf()) }
}
@Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)")
@DisplayName("byJsonPath generates correctly | PostgreSQL")
fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL
ForceDialect.postgres()
assertEquals(
"UPDATE $tbl SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)",
RemoveFieldsQuery.byJsonPath(tbl, listOf(Parameter(":name", ParameterType.STRING, "{o,p}"))),
"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)",
RemoveFieldsQuery.byJsonPath(TEST_TABLE, listOf(Parameter(":name", ParameterType.STRING, "{o,p}"))),
"Remove Field query not constructed correctly"
)
}
@Test
@DisplayName("byJsonPath fails (SQLite)")
@DisplayName("byJsonPath fails | SQLite")
fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE
assertThrows<DocumentException> { RemoveFieldsQuery.byJsonPath(tbl, listOf()) }
ForceDialect.sqlite()
assertThrows<DocumentException> { RemoveFieldsQuery.byJsonPath(TEST_TABLE, listOf()) }
}
}