diff --git a/.idea/compiler.xml b/.idea/compiler.xml index afb5348..ea58415 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -6,13 +6,20 @@ - - + + + + - + + + + + + diff --git a/.idea/encodings.xml b/.idea/encodings.xml index 3a6db4a..66a447d 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -1,13 +1,30 @@ + + + + + + + + + + + + + + + + + diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index c22b6fa..d1e0db8 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,16 @@ + + + + + + - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 46a3f3f..a3d569e 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,9 @@ - + + + \ No newline at end of file diff --git a/.idea/scala_compiler.xml b/.idea/scala_compiler.xml index 6cdd90a..96c87c5 100644 --- a/.idea/scala_compiler.xml +++ b/.idea/scala_compiler.xml @@ -2,6 +2,6 @@ \ No newline at end of file diff --git a/java.iml b/java.iml new file mode 100644 index 0000000..0ae9cfd --- /dev/null +++ b/java.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index b32d087..7dfbc5a 100644 --- a/pom.xml +++ b/pom.xml @@ -38,13 +38,57 @@ UTF-8 official - 11 - 2.1.0 + 17 + ${java.version} + 2.1.20 1.8.0 + 3.5.2 + 4.0.26 + 3.5.2 + 3.5.2 + 2.18.2 + 3.46.1.2 + 42.7.5 - src + ./src/core + ./src/groovy + ./src/kotlinx + ./src/scala + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + org.jetbrains.kotlin + kotlin-test-junit5 + ${kotlin.version} + test + + + org.xerial + sqlite-jdbc + ${sqlite.version} + test + + + org.slf4j + slf4j-simple + 2.0.16 + test + + + org.postgresql + postgresql + ${postgresql.version} + test + + + \ No newline at end of file diff --git a/src/core/core.iml b/src/core/core.iml new file mode 100644 index 0000000..2feb230 --- /dev/null +++ b/src/core/core.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/jvm/pom.xml b/src/core/pom.xml similarity index 54% rename from src/jvm/pom.xml rename to src/core/pom.xml index 287370a..fd79794 100644 --- a/src/jvm/pom.xml +++ b/src/core/pom.xml @@ -3,61 +3,27 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - - solutions.bitbadger.documents - jvm - 4.0.0-alpha1-SNAPSHOT - jar - solutions.bitbadger documents 4.0.0-alpha1-SNAPSHOT + ../../pom.xml + solutions.bitbadger.documents + core + ${project.groupId}:${project.artifactId} - Expose a document store interface for PostgreSQL and SQLite (Standard JVM Library) + Expose a document store interface for PostgreSQL and SQLite (Core Library) https://bitbadger.solutions/open-source/relational-documents/jvm/ - - scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git - scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git - https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents - - - 2.18.2 + UTF-8 + official + 1.8 - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - test - - - org.scala-lang - scala3-library_3 - ${scala.version} - test - - - org.apache.groovy - groovy-test - ${groovy.version} - test - - - org.apache.groovy - groovy-test-junit5 - ${groovy.version} - test - - - - src/main/kotlin org.jetbrains.kotlin @@ -72,6 +38,7 @@ + ${project.basedir}/src/main/java ${project.basedir}/src/main/kotlin @@ -84,62 +51,20 @@ - ${project.basedir}/src/test/kotlin ${project.basedir}/src/test/java - ${project.basedir}/src/test/scala + ${project.basedir}/src/test/kotlin - - net.alchim31.maven - scala-maven-plugin - 4.9.2 - - - - testCompile - - - - - ${java.version} - ${java.version} - - - - org.codehaus.gmaven - gmaven-plugin - 1.5 - - ${java.version} - - - - - 2.0 - - - generateTestStubs - testCompile - - - - maven-surefire-plugin - 2.22.2 - - - --add-opens=java.base/java.lang=ALL-UNNAMED - --add-opens=java.base/java.util=ALL-UNNAMED - - + ${surefire.version} maven-failsafe-plugin - 2.22.2 + ${failsafe.version} @@ -160,4 +85,24 @@ - + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + + + + \ No newline at end of file diff --git a/src/core/src/main/java/module-info.java b/src/core/src/main/java/module-info.java new file mode 100644 index 0000000..63cd2b9 --- /dev/null +++ b/src/core/src/main/java/module-info.java @@ -0,0 +1,9 @@ +module solutions.bitbadger.documents.core { + requires kotlin.stdlib; + requires kotlin.reflect; + requires java.sql; + exports solutions.bitbadger.documents; + exports solutions.bitbadger.documents.java; + exports solutions.bitbadger.documents.java.extensions; + exports solutions.bitbadger.documents.query; +} diff --git a/src/jvm/src/main/kotlin/AutoId.kt b/src/core/src/main/kotlin/AutoId.kt similarity index 100% rename from src/jvm/src/main/kotlin/AutoId.kt rename to src/core/src/main/kotlin/AutoId.kt diff --git a/src/jvm/src/main/kotlin/Comparison.kt b/src/core/src/main/kotlin/Comparison.kt similarity index 100% rename from src/jvm/src/main/kotlin/Comparison.kt rename to src/core/src/main/kotlin/Comparison.kt diff --git a/src/jvm/src/main/kotlin/Configuration.kt b/src/core/src/main/kotlin/Configuration.kt similarity index 100% rename from src/jvm/src/main/kotlin/Configuration.kt rename to src/core/src/main/kotlin/Configuration.kt diff --git a/src/jvm/src/main/kotlin/Dialect.kt b/src/core/src/main/kotlin/Dialect.kt similarity index 100% rename from src/jvm/src/main/kotlin/Dialect.kt rename to src/core/src/main/kotlin/Dialect.kt diff --git a/src/jvm/src/main/kotlin/DocumentException.kt b/src/core/src/main/kotlin/DocumentException.kt similarity index 100% rename from src/jvm/src/main/kotlin/DocumentException.kt rename to src/core/src/main/kotlin/DocumentException.kt diff --git a/src/jvm/src/main/kotlin/DocumentIndex.kt b/src/core/src/main/kotlin/DocumentIndex.kt similarity index 100% rename from src/jvm/src/main/kotlin/DocumentIndex.kt rename to src/core/src/main/kotlin/DocumentIndex.kt diff --git a/src/jvm/src/main/kotlin/DocumentSerializer.kt b/src/core/src/main/kotlin/DocumentSerializer.kt similarity index 100% rename from src/jvm/src/main/kotlin/DocumentSerializer.kt rename to src/core/src/main/kotlin/DocumentSerializer.kt diff --git a/src/jvm/src/main/kotlin/Field.kt b/src/core/src/main/kotlin/Field.kt similarity index 100% rename from src/jvm/src/main/kotlin/Field.kt rename to src/core/src/main/kotlin/Field.kt diff --git a/src/jvm/src/main/kotlin/FieldFormat.kt b/src/core/src/main/kotlin/FieldFormat.kt similarity index 100% rename from src/jvm/src/main/kotlin/FieldFormat.kt rename to src/core/src/main/kotlin/FieldFormat.kt diff --git a/src/jvm/src/main/kotlin/FieldMatch.kt b/src/core/src/main/kotlin/FieldMatch.kt similarity index 100% rename from src/jvm/src/main/kotlin/FieldMatch.kt rename to src/core/src/main/kotlin/FieldMatch.kt diff --git a/src/jvm/src/main/kotlin/Op.kt b/src/core/src/main/kotlin/Op.kt similarity index 100% rename from src/jvm/src/main/kotlin/Op.kt rename to src/core/src/main/kotlin/Op.kt diff --git a/src/jvm/src/main/kotlin/Parameter.kt b/src/core/src/main/kotlin/Parameter.kt similarity index 100% rename from src/jvm/src/main/kotlin/Parameter.kt rename to src/core/src/main/kotlin/Parameter.kt diff --git a/src/jvm/src/main/kotlin/ParameterName.kt b/src/core/src/main/kotlin/ParameterName.kt similarity index 100% rename from src/jvm/src/main/kotlin/ParameterName.kt rename to src/core/src/main/kotlin/ParameterName.kt diff --git a/src/jvm/src/main/kotlin/ParameterType.kt b/src/core/src/main/kotlin/ParameterType.kt similarity index 100% rename from src/jvm/src/main/kotlin/ParameterType.kt rename to src/core/src/main/kotlin/ParameterType.kt diff --git a/src/jvm/src/main/kotlin/jvm/Count.kt b/src/core/src/main/kotlin/java/Count.kt similarity index 94% rename from src/jvm/src/main/kotlin/jvm/Count.kt rename to src/core/src/main/kotlin/java/Count.kt index 25f87e3..bd599e9 100644 --- a/src/jvm/src/main/kotlin/jvm/Count.kt +++ b/src/core/src/main/kotlin/java/Count.kt @@ -1,8 +1,7 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.* import solutions.bitbadger.documents.query.CountQuery -import solutions.bitbadger.documents.extensions.customScalar import java.sql.Connection import kotlin.jvm.Throws @@ -22,7 +21,7 @@ object Count { @Throws(DocumentException::class) @JvmStatic fun all(tableName: String, conn: Connection) = - conn.customScalar(CountQuery.all(tableName), listOf(), Long::class.java, Results::toCount) + Custom.scalar(CountQuery.all(tableName), listOf(), Long::class.java, conn, Results::toCount) /** * Count all documents in the table @@ -56,10 +55,11 @@ object Count { conn: Connection ): Long { val named = Parameters.nameFields(fields) - return conn.customScalar( + return Custom.scalar( CountQuery.byFields(tableName, named, howMatched), Parameters.addFields(named), Long::class.java, + conn, Results::toCount ) } @@ -91,10 +91,11 @@ object Count { @Throws(DocumentException::class) @JvmStatic fun byContains(tableName: String, criteria: TContains, conn: Connection) = - conn.customScalar( + Custom.scalar( CountQuery.byContains(tableName), listOf(Parameters.json(":criteria", criteria)), Long::class.java, + conn, Results::toCount ) @@ -123,10 +124,11 @@ object Count { @Throws(DocumentException::class) @JvmStatic fun byJsonPath(tableName: String, path: String, conn: Connection) = - conn.customScalar( + Custom.scalar( CountQuery.byJsonPath(tableName), listOf(Parameter(":path", ParameterType.STRING, path)), Long::class.java, + conn, Results::toCount ) diff --git a/src/jvm/src/main/kotlin/jvm/Custom.kt b/src/core/src/main/kotlin/java/Custom.kt similarity index 85% rename from src/jvm/src/main/kotlin/jvm/Custom.kt rename to src/core/src/main/kotlin/java/Custom.kt index d438b64..f341153 100644 --- a/src/jvm/src/main/kotlin/jvm/Custom.kt +++ b/src/core/src/main/kotlin/java/Custom.kt @@ -1,10 +1,12 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Parameter import java.sql.Connection import java.sql.ResultSet +import java.sql.SQLException +import java.util.* import kotlin.jvm.Throws object Custom { @@ -57,7 +59,7 @@ object Custom { * @param clazz The class of the document to be returned * @param conn The connection over which the query should be executed * @param mapFunc The mapping function between the document and the domain item - * @return The document if one matches the query, `null` otherwise + * @return An `Optional` value, with the document if one matches the query * @throws DocumentException If parameters are invalid */ @Throws(DocumentException::class) @@ -68,7 +70,7 @@ object Custom { clazz: Class, conn: Connection, mapFunc: (ResultSet, Class) -> TDoc - ) = list("$query LIMIT 1", parameters, clazz, conn, mapFunc).singleOrNull() + ) = Optional.ofNullable(list("$query LIMIT 1", parameters, clazz, conn, mapFunc).singleOrNull()) /** * Execute a query that returns one or no results @@ -95,12 +97,16 @@ object Custom { * @param query The query to retrieve the results * @param conn The connection over which the query should be executed * @param parameters Parameters to use for the query - * @throws DocumentException If parameters are invalid + * @throws DocumentException If parameters are invalid or if the query fails */ @Throws(DocumentException::class) @JvmStatic fun nonQuery(query: String, parameters: Collection> = listOf(), conn: Connection) { - Parameters.apply(conn, query, parameters).use { it.executeUpdate() } + try { + Parameters.apply(conn, query, parameters).use { it.executeUpdate() } + } catch (ex: SQLException) { + throw DocumentException("Unable to execute non-query: ${ex.message}", ex) + } } /** @@ -108,7 +114,7 @@ object Custom { * * @param query The query to retrieve the results * @param parameters Parameters to use for the query - * @throws DocumentException If no connection string has been set, or if parameters are invalid + * @throws DocumentException If no connection string has been set, if parameters are invalid, or if the query fails */ @Throws(DocumentException::class) @JvmStatic @@ -124,7 +130,7 @@ object Custom { * @param conn The connection over which the query should be executed * @param mapFunc The mapping function between the document and the domain item * @return The scalar value from the query - * @throws DocumentException If parameters are invalid + * @throws DocumentException If parameters are invalid or if the query fails */ @Throws(DocumentException::class) @JvmStatic @@ -135,9 +141,13 @@ object Custom { conn: Connection, mapFunc: (ResultSet, Class) -> T ) = Parameters.apply(conn, query, parameters).use { stmt -> - stmt.executeQuery().use { rs -> - rs.next() - mapFunc(rs, clazz) + try { + stmt.executeQuery().use { rs -> + rs.next() + mapFunc(rs, clazz) + } + } catch (ex: SQLException) { + throw DocumentException("Unable to retrieve scalar value: ${ex.message}", ex) } } diff --git a/src/jvm/src/main/kotlin/jvm/Definition.kt b/src/core/src/main/kotlin/java/Definition.kt similarity index 88% rename from src/jvm/src/main/kotlin/jvm/Definition.kt rename to src/core/src/main/kotlin/java/Definition.kt index 585e7d9..baf7b9b 100644 --- a/src/jvm/src/main/kotlin/jvm/Definition.kt +++ b/src/core/src/main/kotlin/java/Definition.kt @@ -1,9 +1,8 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentIndex -import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.query.DefinitionQuery import java.sql.Connection import kotlin.jvm.Throws @@ -24,8 +23,8 @@ object Definition { @JvmStatic fun ensureTable(tableName: String, conn: Connection) = Configuration.dialect("ensure $tableName exists").let { - conn.customNonQuery(DefinitionQuery.ensureTable(tableName, it)) - conn.customNonQuery(DefinitionQuery.ensureKey(tableName, it)) + Custom.nonQuery(DefinitionQuery.ensureTable(tableName, it), conn = conn) + Custom.nonQuery(DefinitionQuery.ensureKey(tableName, it), conn = conn) } /** @@ -51,7 +50,7 @@ object Definition { @Throws(DocumentException::class) @JvmStatic fun ensureFieldIndex(tableName: String, indexName: String, fields: Collection, conn: Connection) = - conn.customNonQuery(DefinitionQuery.ensureIndexOn(tableName, indexName, fields)) + Custom.nonQuery(DefinitionQuery.ensureIndexOn(tableName, indexName, fields), conn = conn) /** * Create an index on field(s) within documents in the specified table if necessary @@ -77,7 +76,7 @@ object Definition { @Throws(DocumentException::class) @JvmStatic fun ensureDocumentIndex(tableName: String, indexType: DocumentIndex, conn: Connection) = - conn.customNonQuery(DefinitionQuery.ensureDocumentIndexOn(tableName, indexType)) + Custom.nonQuery(DefinitionQuery.ensureDocumentIndexOn(tableName, indexType), conn = conn) /** * Create a document index on a table (PostgreSQL only) diff --git a/src/jvm/src/main/kotlin/jvm/Delete.kt b/src/core/src/main/kotlin/java/Delete.kt similarity index 90% rename from src/jvm/src/main/kotlin/jvm/Delete.kt rename to src/core/src/main/kotlin/java/Delete.kt index 7cba82a..969ac5e 100644 --- a/src/jvm/src/main/kotlin/jvm/Delete.kt +++ b/src/core/src/main/kotlin/java/Delete.kt @@ -1,7 +1,6 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.query.DeleteQuery import java.sql.Connection import kotlin.jvm.Throws @@ -22,9 +21,10 @@ object Delete { @Throws(DocumentException::class) @JvmStatic fun byId(tableName: String, docId: TKey, conn: Connection) = - conn.customNonQuery( + Custom.nonQuery( DeleteQuery.byId(tableName, docId), - Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))) + Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))), + conn ) /** @@ -53,7 +53,7 @@ object Delete { @JvmOverloads fun byFields(tableName: String, fields: Collection>, howMatched: FieldMatch? = null, conn: Connection) { val named = Parameters.nameFields(fields) - conn.customNonQuery(DeleteQuery.byFields(tableName, named, howMatched), Parameters.addFields(named)) + Custom.nonQuery(DeleteQuery.byFields(tableName, named, howMatched), Parameters.addFields(named), conn) } /** @@ -81,7 +81,7 @@ object Delete { @Throws(DocumentException::class) @JvmStatic fun byContains(tableName: String, criteria: TContains, conn: Connection) = - conn.customNonQuery(DeleteQuery.byContains(tableName), listOf(Parameters.json(":criteria", criteria))) + Custom.nonQuery(DeleteQuery.byContains(tableName), listOf(Parameters.json(":criteria", criteria)), conn) /** * Delete documents using a JSON containment query (PostgreSQL only) @@ -106,7 +106,7 @@ object Delete { @Throws(DocumentException::class) @JvmStatic fun byJsonPath(tableName: String, path: String, conn: Connection) = - conn.customNonQuery(DeleteQuery.byJsonPath(tableName), listOf(Parameter(":path", ParameterType.STRING, path))) + Custom.nonQuery(DeleteQuery.byJsonPath(tableName), listOf(Parameter(":path", ParameterType.STRING, path)), conn) /** * Delete documents using a JSON Path match query (PostgreSQL only) diff --git a/src/jvm/src/main/kotlin/jvm/Document.kt b/src/core/src/main/kotlin/java/Document.kt similarity index 92% rename from src/jvm/src/main/kotlin/jvm/Document.kt rename to src/core/src/main/kotlin/java/Document.kt index 69c4e64..018b49c 100644 --- a/src/jvm/src/main/kotlin/jvm/Document.kt +++ b/src/core/src/main/kotlin/java/Document.kt @@ -1,10 +1,9 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.AutoId import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.query.DocumentQuery import solutions.bitbadger.documents.query.Where import solutions.bitbadger.documents.query.statementWhere @@ -33,7 +32,7 @@ object Document { } else { DocumentQuery.insert(tableName, strategy) } - conn.customNonQuery(query, listOf(Parameters.json(":data", document))) + Custom.nonQuery(query, listOf(Parameters.json(":data", document)), conn) } /** @@ -60,7 +59,7 @@ object Document { @Throws(DocumentException::class) @JvmStatic fun save(tableName: String, document: TDoc, conn: Connection) = - conn.customNonQuery(DocumentQuery.save(tableName), listOf(Parameters.json(":data", document))) + Custom.nonQuery(DocumentQuery.save(tableName), listOf(Parameters.json(":data", document)), conn) /** * Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert") @@ -86,12 +85,13 @@ object Document { @Throws(DocumentException::class) @JvmStatic fun update(tableName: String, docId: TKey, document: TDoc, conn: Connection) = - conn.customNonQuery( + Custom.nonQuery( statementWhere(DocumentQuery.update(tableName), Where.byId(":id", docId)), Parameters.addFields( listOf(Field.equal(Configuration.idField, docId, ":id")), mutableListOf(Parameters.json(":data", document)) - ) + ), + conn ) /** diff --git a/src/jvm/src/main/kotlin/jvm/DocumentConfig.kt b/src/core/src/main/kotlin/java/DocumentConfig.kt similarity index 86% rename from src/jvm/src/main/kotlin/jvm/DocumentConfig.kt rename to src/core/src/main/kotlin/java/DocumentConfig.kt index d8b39fd..f817b4b 100644 --- a/src/jvm/src/main/kotlin/jvm/DocumentConfig.kt +++ b/src/core/src/main/kotlin/java/DocumentConfig.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.DocumentSerializer diff --git a/src/jvm/src/main/kotlin/jvm/Exists.kt b/src/core/src/main/kotlin/java/Exists.kt similarity index 96% rename from src/jvm/src/main/kotlin/jvm/Exists.kt rename to src/core/src/main/kotlin/java/Exists.kt index 990129e..bbfa151 100644 --- a/src/jvm/src/main/kotlin/jvm/Exists.kt +++ b/src/core/src/main/kotlin/java/Exists.kt @@ -1,7 +1,6 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.extensions.customScalar import solutions.bitbadger.documents.query.ExistsQuery import java.sql.Connection import kotlin.jvm.Throws @@ -23,10 +22,11 @@ object Exists { @Throws(DocumentException::class) @JvmStatic fun byId(tableName: String, docId: TKey, conn: Connection) = - conn.customScalar( + Custom.scalar( ExistsQuery.byId(tableName, docId), Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))), Boolean::class.java, + conn, Results::toExists ) @@ -63,10 +63,11 @@ object Exists { conn: Connection ): Boolean { val named = Parameters.nameFields(fields) - return conn.customScalar( + return Custom.scalar( ExistsQuery.byFields(tableName, named, howMatched), Parameters.addFields(named), Boolean::class.java, + conn, Results::toExists ) } @@ -98,10 +99,11 @@ object Exists { @Throws(DocumentException::class) @JvmStatic fun byContains(tableName: String, criteria: TContains, conn: Connection) = - conn.customScalar( + Custom.scalar( ExistsQuery.byContains(tableName), listOf(Parameters.json(":criteria", criteria)), Boolean::class.java, + conn, Results::toExists ) @@ -130,10 +132,11 @@ object Exists { @Throws(DocumentException::class) @JvmStatic fun byJsonPath(tableName: String, path: String, conn: Connection) = - conn.customScalar( + Custom.scalar( ExistsQuery.byJsonPath(tableName), listOf(Parameter(":path", ParameterType.STRING, path)), Boolean::class.java, + conn, Results::toExists ) diff --git a/src/jvm/src/main/kotlin/jvm/Find.kt b/src/core/src/main/kotlin/java/Find.kt similarity index 92% rename from src/jvm/src/main/kotlin/jvm/Find.kt rename to src/core/src/main/kotlin/java/Find.kt index b63d52f..09d8bef 100644 --- a/src/jvm/src/main/kotlin/jvm/Find.kt +++ b/src/core/src/main/kotlin/java/Find.kt @@ -1,11 +1,10 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.extensions.customList -import solutions.bitbadger.documents.extensions.customSingle import solutions.bitbadger.documents.query.FindQuery import solutions.bitbadger.documents.query.orderBy import java.sql.Connection +import java.util.Optional import kotlin.jvm.Throws /** @@ -26,7 +25,7 @@ object Find { @Throws(DocumentException::class) @JvmStatic fun all(tableName: String, clazz: Class, orderBy: Collection>? = null, conn: Connection) = - conn.customList(FindQuery.all(tableName) + (orderBy?.let(::orderBy) ?: ""), listOf(), clazz, Results::fromData) + Custom.list(FindQuery.all(tableName) + (orderBy?.let(::orderBy) ?: ""), listOf(), clazz, conn, Results::fromData) /** * Retrieve all documents in the given table @@ -64,16 +63,17 @@ object Find { * @param docId The ID of the document to retrieve * @param clazz The class of the document to be returned * @param conn The connection over which documents should be retrieved - * @return The document if it is found, `null` otherwise + * @return An `Optional` item with the document if it is found * @throws DocumentException If no dialect has been configured */ @Throws(DocumentException::class) @JvmStatic fun byId(tableName: String, docId: TKey, clazz: Class, conn: Connection) = - conn.customSingle( + Custom.single( FindQuery.byId(tableName, docId), Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id"))), clazz, + conn, Results::fromData ) @@ -83,7 +83,7 @@ object Find { * @param tableName The table from which the document should be retrieved * @param docId The ID of the document to retrieve * @param clazz The class of the document to be returned - * @return The document if it is found, `null` otherwise + * @return An `Optional` item with the document if it is found * @throws DocumentException If no connection string has been set */ @Throws(DocumentException::class) @@ -114,10 +114,11 @@ object Find { conn: Connection ): List { val named = Parameters.nameFields(fields) - return conn.customList( + return Custom.list( FindQuery.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""), Parameters.addFields(named), clazz, + conn, Results::fromData ) } @@ -187,10 +188,11 @@ object Find { orderBy: Collection>? = null, conn: Connection ) = - conn.customList( + Custom.list( FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""), listOf(Parameters.json(":criteria", criteria)), clazz, + conn, Results::fromData ) @@ -250,10 +252,11 @@ object Find { orderBy: Collection>? = null, conn: Connection ) = - conn.customList( + Custom.list( FindQuery.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""), listOf(Parameter(":path", ParameterType.STRING, path)), clazz, + conn, Results::fromData ) @@ -297,7 +300,7 @@ object Find { * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`) * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) * @param conn The connection over which documents should be retrieved - * @return The first document matching the field comparison, or `null` if no matches are found + * @return An `Optional` item, with the first document matching the field comparison if found * @throws DocumentException If no dialect has been configured, or if parameters are invalid */ @Throws(DocumentException::class) @@ -309,12 +312,13 @@ object Find { howMatched: FieldMatch? = null, orderBy: Collection>? = null, conn: Connection - ): TDoc? { + ): Optional { val named = Parameters.nameFields(fields) - return conn.customSingle( + return Custom.single( FindQuery.byFields(tableName, named, howMatched) + (orderBy?.let(::orderBy) ?: ""), Parameters.addFields(named), clazz, + conn, Results::fromData ) } @@ -327,7 +331,7 @@ object Find { * @param clazz The class of the document to be returned * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`) * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) - * @return The first document matching the field comparison, or `null` if no matches are found + * @return An `Optional` item, with the first document matching the field comparison if found * @throws DocumentException If no connection string has been set, or if parameters are invalid */ @Throws(DocumentException::class) @@ -350,7 +354,7 @@ object Find { * @param clazz The class of the document to be returned * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`) * @param conn The connection over which documents should be retrieved - * @return The first document matching the field comparison, or `null` if no matches are found + * @return An `Optional` item, with the first document matching the field comparison if found * @throws DocumentException If no dialect has been configured, or if parameters are invalid */ @Throws(DocumentException::class) @@ -371,7 +375,7 @@ object Find { * @param criteria The object for which JSON containment should be checked * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) * @param conn The connection over which documents should be retrieved - * @return The first document matching the JSON containment query, or `null` if no matches are found + * @return An `Optional` item, with the first document matching the JSON containment query if found * @throws DocumentException If called on a SQLite connection */ @Throws(DocumentException::class) @@ -383,10 +387,11 @@ object Find { orderBy: Collection>? = null, conn: Connection ) = - conn.customSingle( + Custom.single( FindQuery.byContains(tableName) + (orderBy?.let(::orderBy) ?: ""), listOf(Parameters.json(":criteria", criteria)), clazz, + conn, Results::fromData ) @@ -397,7 +402,7 @@ object Find { * @param criteria The object for which JSON containment should be checked * @param clazz The class of the document to be returned * @param conn The connection over which documents should be retrieved - * @return The first document matching the JSON containment query, or `null` if no matches are found + * @return An `Optional` item, with the first document matching the JSON containment query if found * @throws DocumentException If called on a SQLite connection */ @Throws(DocumentException::class) @@ -417,7 +422,7 @@ object Find { * @param criteria The object for which JSON containment should be checked * @param clazz The class of the document to be returned * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) - * @return The first document matching the JSON containment query, or `null` if no matches are found + * @return An `Optional` item, with the first document matching the JSON containment query if found * @throws DocumentException If no connection string has been set, or if called on a SQLite connection */ @Throws(DocumentException::class) @@ -439,7 +444,7 @@ object Find { * @param clazz The class of the document to be returned * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) * @param conn The connection over which documents should be retrieved - * @return The first document matching the JSON Path match query, or `null` if no matches are found + * @return An `Optional` item, with the first document matching the JSON Path match query if found * @throws DocumentException If called on a SQLite connection */ @Throws(DocumentException::class) @@ -451,10 +456,11 @@ object Find { orderBy: Collection>? = null, conn: Connection ) = - conn.customSingle( + Custom.single( FindQuery.byJsonPath(tableName) + (orderBy?.let(::orderBy) ?: ""), listOf(Parameter(":path", ParameterType.STRING, path)), clazz, + conn, Results::fromData ) @@ -465,7 +471,7 @@ object Find { * @param path The JSON path comparison to match * @param clazz The class of the document to be returned * @param conn The connection over which documents should be retrieved - * @return The first document matching the JSON Path match query, or `null` if no matches are found + * @return An `Optional` item, with the first document matching the JSON Path match query if found * @throws DocumentException If called on a SQLite connection */ @Throws(DocumentException::class) @@ -480,7 +486,7 @@ object Find { * @param path The JSON path comparison to match * @param clazz The class of the document to be returned * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) - * @return The first document matching the JSON Path match query, or `null` if no matches are found + * @return An `Optional` item, with the first document matching the JSON Path match query if found * @throws DocumentException If no connection string has been set, or if called on a SQLite connection */ @Throws(DocumentException::class) diff --git a/src/jvm/src/main/kotlin/jvm/NullDocumentSerializer.kt b/src/core/src/main/kotlin/java/NullDocumentSerializer.kt similarity index 94% rename from src/jvm/src/main/kotlin/jvm/NullDocumentSerializer.kt rename to src/core/src/main/kotlin/java/NullDocumentSerializer.kt index 41a7b63..233a6ce 100644 --- a/src/jvm/src/main/kotlin/jvm/NullDocumentSerializer.kt +++ b/src/core/src/main/kotlin/java/NullDocumentSerializer.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.DocumentSerializer diff --git a/src/jvm/src/main/kotlin/jvm/Parameters.kt b/src/core/src/main/kotlin/java/Parameters.kt similarity index 99% rename from src/jvm/src/main/kotlin/jvm/Parameters.kt rename to src/core/src/main/kotlin/java/Parameters.kt index a614480..b45b64a 100644 --- a/src/jvm/src/main/kotlin/jvm/Parameters.kt +++ b/src/core/src/main/kotlin/java/Parameters.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.* import solutions.bitbadger.documents.ParameterName diff --git a/src/jvm/src/main/kotlin/jvm/Patch.kt b/src/core/src/main/kotlin/java/Patch.kt similarity index 92% rename from src/jvm/src/main/kotlin/jvm/Patch.kt rename to src/core/src/main/kotlin/java/Patch.kt index 1097d9c..1a03d00 100644 --- a/src/jvm/src/main/kotlin/jvm/Patch.kt +++ b/src/core/src/main/kotlin/java/Patch.kt @@ -1,7 +1,6 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.query.PatchQuery import java.sql.Connection import kotlin.jvm.Throws @@ -23,12 +22,13 @@ object Patch { @Throws(DocumentException::class) @JvmStatic fun byId(tableName: String, docId: TKey, patch: TPatch, conn: Connection) = - conn.customNonQuery( + Custom.nonQuery( PatchQuery.byId(tableName, docId), Parameters.addFields( listOf(Field.equal(Configuration.idField, docId, ":id")), mutableListOf(Parameters.json(":data", patch)) - ) + ), + conn ) /** @@ -64,11 +64,10 @@ object Patch { conn: Connection ) { val named = Parameters.nameFields(fields) - conn.customNonQuery( - PatchQuery.byFields(tableName, named, howMatched), Parameters.addFields( - named, - mutableListOf(Parameters.json(":data", patch)) - ) + Custom.nonQuery( + PatchQuery.byFields(tableName, named, howMatched), + Parameters.addFields(named, mutableListOf(Parameters.json(":data", patch))), + conn ) } @@ -104,9 +103,10 @@ object Patch { @Throws(DocumentException::class) @JvmStatic fun byContains(tableName: String, criteria: TContains, patch: TPatch, conn: Connection) = - conn.customNonQuery( + Custom.nonQuery( PatchQuery.byContains(tableName), - listOf(Parameters.json(":criteria", criteria), Parameters.json(":data", patch)) + listOf(Parameters.json(":criteria", criteria), Parameters.json(":data", patch)), + conn ) /** @@ -134,9 +134,10 @@ object Patch { @Throws(DocumentException::class) @JvmStatic fun byJsonPath(tableName: String, path: String, patch: TPatch, conn: Connection) = - conn.customNonQuery( + Custom.nonQuery( PatchQuery.byJsonPath(tableName), - listOf(Parameter(":path", ParameterType.STRING, path), Parameters.json(":data", patch)) + listOf(Parameter(":path", ParameterType.STRING, path), Parameters.json(":data", patch)), + conn ) /** diff --git a/src/jvm/src/main/kotlin/jvm/RemoveFields.kt b/src/core/src/main/kotlin/java/RemoveFields.kt similarity index 94% rename from src/jvm/src/main/kotlin/jvm/RemoveFields.kt rename to src/core/src/main/kotlin/java/RemoveFields.kt index b69fbf1..540d504 100644 --- a/src/jvm/src/main/kotlin/jvm/RemoveFields.kt +++ b/src/core/src/main/kotlin/java/RemoveFields.kt @@ -1,7 +1,6 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.extensions.customNonQuery import solutions.bitbadger.documents.query.RemoveFieldsQuery import java.sql.Connection import kotlin.jvm.Throws @@ -38,12 +37,10 @@ object RemoveFields { @JvmStatic fun byId(tableName: String, docId: TKey, toRemove: Collection, conn: Connection) { val nameParams = Parameters.fieldNames(toRemove) - conn.customNonQuery( + Custom.nonQuery( RemoveFieldsQuery.byId(tableName, nameParams, docId), - Parameters.addFields( - listOf(Field.equal(Configuration.idField, docId, ":id")), - translatePath(nameParams) - ) + Parameters.addFields(listOf(Field.equal(Configuration.idField, docId, ":id")), translatePath(nameParams)), + conn ) } @@ -81,9 +78,10 @@ object RemoveFields { ) { val named = Parameters.nameFields(fields) val nameParams = Parameters.fieldNames(toRemove) - conn.customNonQuery( + Custom.nonQuery( RemoveFieldsQuery.byFields(tableName, nameParams, named, howMatched), - Parameters.addFields(named, translatePath(nameParams)) + Parameters.addFields(named, translatePath(nameParams)), + conn ) } @@ -125,9 +123,10 @@ object RemoveFields { conn: Connection ) { val nameParams = Parameters.fieldNames(toRemove) - conn.customNonQuery( + Custom.nonQuery( RemoveFieldsQuery.byContains(tableName, nameParams), - listOf(Parameters.json(":criteria", criteria), *nameParams.toTypedArray()) + listOf(Parameters.json(":criteria", criteria), *nameParams.toTypedArray()), + conn ) } @@ -157,9 +156,10 @@ object RemoveFields { @JvmStatic fun byJsonPath(tableName: String, path: String, toRemove: Collection, conn: Connection) { val nameParams = Parameters.fieldNames(toRemove) - conn.customNonQuery( + Custom.nonQuery( RemoveFieldsQuery.byJsonPath(tableName, nameParams), - listOf(Parameter(":path", ParameterType.STRING, path), *nameParams.toTypedArray()) + listOf(Parameter(":path", ParameterType.STRING, path), *nameParams.toTypedArray()), + conn ) } diff --git a/src/jvm/src/main/kotlin/jvm/Results.kt b/src/core/src/main/kotlin/java/Results.kt similarity index 98% rename from src/jvm/src/main/kotlin/jvm/Results.kt rename to src/core/src/main/kotlin/java/Results.kt index 0bea109..d79b533 100644 --- a/src/jvm/src/main/kotlin/jvm/Results.kt +++ b/src/core/src/main/kotlin/java/Results.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.java import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.Dialect @@ -6,7 +6,6 @@ import solutions.bitbadger.documents.DocumentException import java.sql.PreparedStatement import java.sql.ResultSet import java.sql.SQLException -import kotlin.jvm.Throws object Results { diff --git a/src/jvm/src/main/kotlin/extensions/Connection.kt b/src/core/src/main/kotlin/java/extensions/Connection.kt similarity index 99% rename from src/jvm/src/main/kotlin/extensions/Connection.kt rename to src/core/src/main/kotlin/java/extensions/Connection.kt index e539c22..f28a103 100644 --- a/src/jvm/src/main/kotlin/extensions/Connection.kt +++ b/src/core/src/main/kotlin/java/extensions/Connection.kt @@ -1,9 +1,9 @@ @file:JvmName("ConnExt") -package solutions.bitbadger.documents.extensions +package solutions.bitbadger.documents.java.extensions import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.jvm.* +import solutions.bitbadger.documents.java.* import java.sql.Connection import java.sql.ResultSet import kotlin.jvm.Throws @@ -430,6 +430,7 @@ fun Connection.patchById(tableName: String, docId: TKey, patch: T * @throws DocumentException If no dialect has been configured, or if parameters are invalid */ @Throws(DocumentException::class) +@JvmOverloads fun Connection.patchByFields( tableName: String, fields: Collection>, @@ -490,6 +491,7 @@ fun Connection.removeFieldsById(tableName: String, docId: TKey, toRemove: * @throws DocumentException If no dialect has been configured, or if parameters are invalid */ @Throws(DocumentException::class) +@JvmOverloads fun Connection.removeFieldsByFields( tableName: String, fields: Collection>, diff --git a/src/jvm/src/main/kotlin/query/CountQuery.kt b/src/core/src/main/kotlin/query/CountQuery.kt similarity index 100% rename from src/jvm/src/main/kotlin/query/CountQuery.kt rename to src/core/src/main/kotlin/query/CountQuery.kt diff --git a/src/jvm/src/main/kotlin/query/DefinitionQuery.kt b/src/core/src/main/kotlin/query/DefinitionQuery.kt similarity index 100% rename from src/jvm/src/main/kotlin/query/DefinitionQuery.kt rename to src/core/src/main/kotlin/query/DefinitionQuery.kt diff --git a/src/jvm/src/main/kotlin/query/DeleteQuery.kt b/src/core/src/main/kotlin/query/DeleteQuery.kt similarity index 100% rename from src/jvm/src/main/kotlin/query/DeleteQuery.kt rename to src/core/src/main/kotlin/query/DeleteQuery.kt diff --git a/src/jvm/src/main/kotlin/query/DocumentQuery.kt b/src/core/src/main/kotlin/query/DocumentQuery.kt similarity index 100% rename from src/jvm/src/main/kotlin/query/DocumentQuery.kt rename to src/core/src/main/kotlin/query/DocumentQuery.kt diff --git a/src/jvm/src/main/kotlin/query/ExistsQuery.kt b/src/core/src/main/kotlin/query/ExistsQuery.kt similarity index 100% rename from src/jvm/src/main/kotlin/query/ExistsQuery.kt rename to src/core/src/main/kotlin/query/ExistsQuery.kt diff --git a/src/jvm/src/main/kotlin/query/FindQuery.kt b/src/core/src/main/kotlin/query/FindQuery.kt similarity index 100% rename from src/jvm/src/main/kotlin/query/FindQuery.kt rename to src/core/src/main/kotlin/query/FindQuery.kt diff --git a/src/jvm/src/main/kotlin/query/PatchQuery.kt b/src/core/src/main/kotlin/query/PatchQuery.kt similarity index 100% rename from src/jvm/src/main/kotlin/query/PatchQuery.kt rename to src/core/src/main/kotlin/query/PatchQuery.kt diff --git a/src/jvm/src/main/kotlin/query/Query.kt b/src/core/src/main/kotlin/query/Query.kt similarity index 100% rename from src/jvm/src/main/kotlin/query/Query.kt rename to src/core/src/main/kotlin/query/Query.kt diff --git a/src/jvm/src/main/kotlin/query/RemoveFieldsQuery.kt b/src/core/src/main/kotlin/query/RemoveFieldsQuery.kt similarity index 100% rename from src/jvm/src/main/kotlin/query/RemoveFieldsQuery.kt rename to src/core/src/main/kotlin/query/RemoveFieldsQuery.kt diff --git a/src/jvm/src/main/kotlin/query/Where.kt b/src/core/src/main/kotlin/query/Where.kt similarity index 100% rename from src/jvm/src/main/kotlin/query/Where.kt rename to src/core/src/main/kotlin/query/Where.kt diff --git a/src/core/src/test/java/module-info.java b/src/core/src/test/java/module-info.java new file mode 100644 index 0000000..5fafbfc --- /dev/null +++ b/src/core/src/test/java/module-info.java @@ -0,0 +1,20 @@ +module solutions.bitbadger.documents.core.tests { + requires solutions.bitbadger.documents.core; + requires com.fasterxml.jackson.databind; + requires java.sql; + requires kotlin.stdlib; + requires kotlin.test.junit5; + requires org.junit.jupiter.api; + requires org.slf4j; + requires annotations; + //requires org.checkerframework.checker.qual; + + exports solutions.bitbadger.documents.core.tests; + exports solutions.bitbadger.documents.core.tests.integration; + exports solutions.bitbadger.documents.core.tests.java; + exports solutions.bitbadger.documents.core.tests.java.integration; + + opens solutions.bitbadger.documents.core.tests; + opens solutions.bitbadger.documents.core.tests.java; + opens solutions.bitbadger.documents.core.tests.java.integration; +} diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/AutoIdTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/AutoIdTest.java similarity index 98% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/AutoIdTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/AutoIdTest.java index b157359..8f6c0f8 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/AutoIdTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/AutoIdTest.java @@ -1,17 +1,16 @@ -package solutions.bitbadger.documents.java; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import solutions.bitbadger.documents.AutoId; import solutions.bitbadger.documents.DocumentException; -import solutions.bitbadger.documents.java.support.*; import static org.junit.jupiter.api.Assertions.*; /** * Unit tests for the `AutoId` enum */ -@DisplayName("JVM | Java | AutoId") +@DisplayName("Core | Java | AutoId") final public class AutoIdTest { @Test diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/ByteIdClass.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ByteIdClass.java similarity index 80% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/support/ByteIdClass.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ByteIdClass.java index bf2be5d..72e872f 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/ByteIdClass.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ByteIdClass.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.support; +package solutions.bitbadger.documents.core.tests.java; public class ByteIdClass { diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/ConfigurationTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ConfigurationTest.java similarity index 94% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/ConfigurationTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ConfigurationTest.java index 2a7ef00..d198c6c 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/ConfigurationTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ConfigurationTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -13,7 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; /** * Unit tests for the `Configuration` object */ -@DisplayName("JVM | Java | Configuration") +@DisplayName("Core | Java | Configuration") final public class ConfigurationTest { @Test diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/CountQueryTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/CountQueryTest.java similarity index 92% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/query/CountQueryTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/CountQueryTest.java index 02b916f..71f3c74 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/CountQueryTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/CountQueryTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.query; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -6,18 +6,18 @@ import org.junit.jupiter.api.Test; import solutions.bitbadger.documents.DocumentException; import solutions.bitbadger.documents.Field; import solutions.bitbadger.documents.query.CountQuery; -import solutions.bitbadger.documents.support.ForceDialect; +import solutions.bitbadger.documents.core.tests.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; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; /** * Unit tests for the `Count` object */ -@DisplayName("JVM | Java | Query | CountQuery") +@DisplayName("Core | Java | Query | CountQuery") final public class CountQueryTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DefinitionQueryTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DefinitionQueryTest.java similarity index 95% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DefinitionQueryTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DefinitionQueryTest.java index 3558285..219c31f 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DefinitionQueryTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DefinitionQueryTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.query; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -7,18 +7,18 @@ import solutions.bitbadger.documents.Dialect; import solutions.bitbadger.documents.DocumentException; import solutions.bitbadger.documents.DocumentIndex; import solutions.bitbadger.documents.query.DefinitionQuery; -import solutions.bitbadger.documents.support.ForceDialect; +import solutions.bitbadger.documents.core.tests.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; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; /** * Unit tests for the `Definition` object */ -@DisplayName("JVM | Java | Query | DefinitionQuery") +@DisplayName("Core | Java | Query | DefinitionQuery") final public class DefinitionQueryTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DeleteQueryTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DeleteQueryTest.java similarity index 93% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DeleteQueryTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DeleteQueryTest.java index 4922f66..3128652 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DeleteQueryTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DeleteQueryTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.query; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -6,18 +6,18 @@ 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 solutions.bitbadger.documents.core.tests.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; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; /** * Unit tests for the `Delete` object */ -@DisplayName("JVM | Java | Query | DeleteQuery") +@DisplayName("Core | Java | Query | DeleteQuery") final public class DeleteQueryTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/DialectTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DialectTest.java similarity index 94% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/DialectTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DialectTest.java index 91fbb2f..543986d 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/DialectTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DialectTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.*; /** * Unit tests for the `Dialect` enum */ -@DisplayName("JVM | Java | Dialect") +@DisplayName("Core | Java | Dialect") final public class DialectTest { @Test diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/DocumentIndexTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DocumentIndexTest.java similarity index 87% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/DocumentIndexTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DocumentIndexTest.java index 72b3d79..7354ebb 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/DocumentIndexTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DocumentIndexTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** * Unit tests for the `DocumentIndex` enum */ -@DisplayName("JVM | Java | DocumentIndex") +@DisplayName("Core | Java | DocumentIndex") final public class DocumentIndexTest { @Test diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DocumentQueryTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DocumentQueryTest.java similarity index 95% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DocumentQueryTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DocumentQueryTest.java index 26307f5..ada52b3 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DocumentQueryTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/DocumentQueryTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.query; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -7,15 +7,15 @@ 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 solutions.bitbadger.documents.core.tests.ForceDialect; import static org.junit.jupiter.api.Assertions.*; -import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; /** * Unit tests for the `Document` object */ -@DisplayName("JVM | Java | Query | DocumentQuery") +@DisplayName("Core | Java | Query | DocumentQuery") final public class DocumentQueryTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/ExistsQueryTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ExistsQueryTest.java similarity index 93% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/query/ExistsQueryTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ExistsQueryTest.java index 82a6c52..d4b2e57 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/ExistsQueryTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ExistsQueryTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.query; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -6,18 +6,18 @@ 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 solutions.bitbadger.documents.core.tests.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; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; /** * Unit tests for the `Exists` object */ -@DisplayName("JVM | Java | Query | ExistsQuery") +@DisplayName("Core | Java | Query | ExistsQuery") final public class ExistsQueryTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/FieldMatchTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/FieldMatchTest.java similarity index 85% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/FieldMatchTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/FieldMatchTest.java index f9dab5f..54a9096 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/FieldMatchTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/FieldMatchTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** * Unit tests for the `FieldMatch` enum */ -@DisplayName("JVM | Java | FieldMatch") +@DisplayName("Core | Java | FieldMatch") final public class FieldMatchTest { @Test diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/FieldTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/FieldTest.java similarity index 99% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/FieldTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/FieldTest.java index 0c2f668..193869a 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/FieldTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/FieldTest.java @@ -1,11 +1,11 @@ -package solutions.bitbadger.documents.java; +package solutions.bitbadger.documents.core.tests.java; import kotlin.Pair; 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 solutions.bitbadger.documents.core.tests.ForceDialect; import java.util.Collection; import java.util.List; @@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.*; /** * Unit tests for the `Field` class */ -@DisplayName("JVM | Java | Field") +@DisplayName("Core | Java | Field") final public class FieldTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/FindQueryTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/FindQueryTest.java similarity index 93% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/query/FindQueryTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/FindQueryTest.java index bcc717e..900c96b 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/FindQueryTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/FindQueryTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.query; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -6,18 +6,18 @@ 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 solutions.bitbadger.documents.core.tests.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; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; /** * Unit tests for the `Find` object */ -@DisplayName("JVM | Java | Query | FindQuery") +@DisplayName("Core | Java | Query | FindQuery") final public class FindQueryTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/IntIdClass.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/IntIdClass.java similarity index 80% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/support/IntIdClass.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/IntIdClass.java index 2db8732..2acdef6 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/IntIdClass.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/IntIdClass.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.support; +package solutions.bitbadger.documents.core.tests.java; public class IntIdClass { diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/LongIdClass.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/LongIdClass.java similarity index 80% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/support/LongIdClass.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/LongIdClass.java index 665e2c1..0ea90a9 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/LongIdClass.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/LongIdClass.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.support; +package solutions.bitbadger.documents.core.tests.java; public class LongIdClass { diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/OpTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/OpTest.java similarity index 96% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/OpTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/OpTest.java index f216ee4..5ecfd48 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/OpTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/OpTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** * Unit tests for the `Op` enum */ -@DisplayName("JVM | Java | Op") +@DisplayName("Core | Java | Op") final public class OpTest { @Test diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/ParameterNameTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ParameterNameTest.java similarity index 92% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/ParameterNameTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ParameterNameTest.java index 1c18044..32d88e7 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/ParameterNameTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ParameterNameTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** * Unit tests for the `ParameterName` class */ -@DisplayName("JVM | Java | ParameterName") +@DisplayName("Core | Java | ParameterName") final public class ParameterNameTest { @Test diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/ParameterTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ParameterTest.java similarity index 93% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/ParameterTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ParameterTest.java index 9ca4432..abe2ae3 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/ParameterTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ParameterTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.*; /** * Unit tests for the `Parameter` class */ -@DisplayName("JVM | Java | Parameter") +@DisplayName("Core | Java | Parameter") final public class ParameterTest { @Test diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/ParametersTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ParametersTest.java similarity index 97% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/ParametersTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ParametersTest.java index 4c5f39f..c74fac6 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/ParametersTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ParametersTest.java @@ -1,10 +1,10 @@ -package solutions.bitbadger.documents.java.jvm; +package solutions.bitbadger.documents.core.tests.java; 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.jvm.Parameters; +import solutions.bitbadger.documents.java.Parameters; import java.util.List; @@ -13,7 +13,7 @@ import static org.junit.jupiter.api.Assertions.*; /** * Unit tests for the `Parameters` object */ -@DisplayName("JVM | Java | Parameters") +@DisplayName("Core | Java | Parameters") final public class ParametersTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/PatchQueryTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/PatchQueryTest.java similarity index 93% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/query/PatchQueryTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/PatchQueryTest.java index 56afbdc..f0f6b7a 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/PatchQueryTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/PatchQueryTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.query; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -6,18 +6,18 @@ 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 solutions.bitbadger.documents.core.tests.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; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; /** * Unit tests for the `Patch` object */ -@DisplayName("JVM | Java | Query | PatchQuery") +@DisplayName("Core | Java | Query | PatchQuery") final public class PatchQueryTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/QueryUtilsTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/QueryUtilsTest.java similarity index 97% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/query/QueryUtilsTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/QueryUtilsTest.java index f815db0..48d8ffb 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/QueryUtilsTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/QueryUtilsTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.query; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -8,7 +8,7 @@ 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 solutions.bitbadger.documents.core.tests.ForceDialect; import java.util.List; @@ -17,7 +17,7 @@ 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") +@DisplayName("Core | Java | Query | QueryUtils") final public class QueryUtilsTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/RemoveFieldsQueryTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/RemoveFieldsQueryTest.java similarity index 94% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/query/RemoveFieldsQueryTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/RemoveFieldsQueryTest.java index 027352e..10d9de0 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/RemoveFieldsQueryTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/RemoveFieldsQueryTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.query; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -8,18 +8,18 @@ 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 solutions.bitbadger.documents.core.tests.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; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; /** * Unit tests for the `RemoveFields` object */ -@DisplayName("JVM | Java | Query | RemoveFieldsQuery") +@DisplayName("Core | Java | Query | RemoveFieldsQuery") final public class RemoveFieldsQueryTest { /** diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/ShortIdClass.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ShortIdClass.java similarity index 81% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/support/ShortIdClass.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ShortIdClass.java index 6608387..f064677 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/ShortIdClass.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/ShortIdClass.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.support; +package solutions.bitbadger.documents.core.tests.java; public class ShortIdClass { diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/StringIdClass.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/StringIdClass.java similarity index 81% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/support/StringIdClass.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/StringIdClass.java index 8264d2b..b47d34d 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/StringIdClass.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/StringIdClass.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.support; +package solutions.bitbadger.documents.core.tests.java; public class StringIdClass { diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/WhereTest.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/WhereTest.java similarity index 97% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/query/WhereTest.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/WhereTest.java index 13de7d9..575effa 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/WhereTest.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/WhereTest.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.query; +package solutions.bitbadger.documents.core.tests.java; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; @@ -7,7 +7,7 @@ import solutions.bitbadger.documents.DocumentException; import solutions.bitbadger.documents.Field; import solutions.bitbadger.documents.FieldMatch; import solutions.bitbadger.documents.query.Where; -import solutions.bitbadger.documents.support.ForceDialect; +import solutions.bitbadger.documents.core.tests.ForceDialect; import java.util.List; @@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; /** * Unit tests for the `Where` object */ -@DisplayName("JVM | Java | Query | Where") +@DisplayName("Core | Java | Query | Where") final public class WhereTest { /** diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/ArrayDocument.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/ArrayDocument.java new file mode 100644 index 0000000..482160b --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/ArrayDocument.java @@ -0,0 +1,41 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import java.util.List; + +public class ArrayDocument { + + private String id; + private List values; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + + public ArrayDocument(String id, List values) { + this.id = id; + this.values = values; + } + + public ArrayDocument() { + this("", List.of()); + } + + public static List testDocuments = + List.of( + new ArrayDocument("first", List.of("a", "b", "c")), + new ArrayDocument("second", List.of("c", "d", "e")), + new ArrayDocument("third", List.of("x", "y", "z"))); + +} diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/common/CountFunctions.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/CountFunctions.java similarity index 87% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/common/CountFunctions.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/CountFunctions.java index a502241..b851ce0 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/common/CountFunctions.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/CountFunctions.java @@ -1,16 +1,15 @@ -package solutions.bitbadger.documents.java.jvm.integration.common; +package solutions.bitbadger.documents.core.tests.java.integration; import solutions.bitbadger.documents.DocumentException; import solutions.bitbadger.documents.Field; -import solutions.bitbadger.documents.java.support.JsonDocument; -import solutions.bitbadger.documents.support.ThrowawayDatabase; +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; -import static solutions.bitbadger.documents.extensions.ConnExt.*; -import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.java.extensions.ConnExt.*; /** * Integration tests for the `Count` object diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/common/CustomFunctions.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/CustomFunctions.java similarity index 83% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/common/CustomFunctions.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/CustomFunctions.java index a1bdfe9..7c6ee1b 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/common/CustomFunctions.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/CustomFunctions.java @@ -1,19 +1,18 @@ -package solutions.bitbadger.documents.java.jvm.integration.common; +package solutions.bitbadger.documents.core.tests.java.integration; import solutions.bitbadger.documents.*; -import solutions.bitbadger.documents.java.support.JsonDocument; -import solutions.bitbadger.documents.jvm.Results; +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase; +import solutions.bitbadger.documents.java.Results; import solutions.bitbadger.documents.query.CountQuery; import solutions.bitbadger.documents.query.DeleteQuery; import solutions.bitbadger.documents.query.FindQuery; -import solutions.bitbadger.documents.support.ThrowawayDatabase; import java.util.Collection; import java.util.List; import static org.junit.jupiter.api.Assertions.*; -import static solutions.bitbadger.documents.extensions.ConnExt.*; -import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.java.extensions.ConnExt.*; final public class CustomFunctions { @@ -33,16 +32,18 @@ final public class CustomFunctions { } public static void singleNone(ThrowawayDatabase db) throws DocumentException { - assertNull( - customSingle(db.getConn(), FindQuery.all(TEST_TABLE), List.of(), JsonDocument.class, Results::fromData), + assertFalse( + customSingle(db.getConn(), FindQuery.all(TEST_TABLE), List.of(), JsonDocument.class, Results::fromData) + .isPresent(), "There should not have been a document returned"); } public static void singleOne(ThrowawayDatabase db) throws DocumentException { JsonDocument.load(db); - assertNotNull( - customSingle(db.getConn(), FindQuery.all(TEST_TABLE), List.of(), JsonDocument.class, Results::fromData), - "There should not have been a document returned"); + assertTrue( + customSingle(db.getConn(), FindQuery.all(TEST_TABLE), List.of(), JsonDocument.class, Results::fromData) + .isPresent(), + "There should have been a document returned"); } public static void nonQueryChanges(ThrowawayDatabase db) throws DocumentException { diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/DefinitionFunctions.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/DefinitionFunctions.java new file mode 100644 index 0000000..13d2336 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/DefinitionFunctions.java @@ -0,0 +1,47 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.DocumentIndex; +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.java.extensions.ConnExt.*; + +final public class DefinitionFunctions { + + public static void ensuresATable(ThrowawayDatabase db) throws DocumentException { + assertFalse(db.dbObjectExists("ensured"), "The 'ensured' table should not exist"); + assertFalse(db.dbObjectExists("idx_ensured_key"), "The PK index for the 'ensured' table should not exist"); + ensureTable(db.getConn(), "ensured"); + assertTrue(db.dbObjectExists("ensured"), "The 'ensured' table should exist"); + assertTrue(db.dbObjectExists("idx_ensured_key"), "The PK index for the 'ensured' table should now exist"); + } + + public static void ensuresAFieldIndex(ThrowawayDatabase db) throws DocumentException { + assertFalse(db.dbObjectExists(String.format("idx_%s_test", TEST_TABLE)), "The test index should not exist"); + ensureFieldIndex(db.getConn(), TEST_TABLE, "test", List.of("id", "category")); + assertTrue(db.dbObjectExists(String.format("idx_%s_test", TEST_TABLE)), "The test index should now exist"); + } + + public static void ensureDocumentIndexFull(ThrowawayDatabase db) throws DocumentException { + assertFalse(db.dbObjectExists("doc_table"), "The 'doc_table' table should not exist"); + ensureTable(db.getConn(), "doc_table"); + assertTrue(db.dbObjectExists("doc_table"), "The 'doc_table' table should exist"); + assertFalse(db.dbObjectExists("idx_doc_table_document"), "The document index should not exist"); + ensureDocumentIndex(db.getConn(), "doc_table", DocumentIndex.FULL); + assertTrue(db.dbObjectExists("idx_doc_table_document"), "The document index should exist"); + } + + public static void ensureDocumentIndexOptimized(ThrowawayDatabase db) throws DocumentException { + assertFalse(db.dbObjectExists("doc_table"), "The 'doc_table' table should not exist"); + ensureTable(db.getConn(), "doc_table"); + assertTrue(db.dbObjectExists("doc_table"), "The 'doc_table' table should exist"); + assertFalse(db.dbObjectExists("idx_doc_table_document"), "The document index should not exist"); + ensureDocumentIndex(db.getConn(), "doc_table", DocumentIndex.OPTIMIZED); + assertTrue(db.dbObjectExists("idx_doc_table_document"), "The document index should exist"); + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/DeleteFunctions.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/DeleteFunctions.java new file mode 100644 index 0000000..89c02ee --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/DeleteFunctions.java @@ -0,0 +1,71 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.Field; +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.java.extensions.ConnExt.*; + +final public class DeleteFunctions { + + public static void byIdMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should be 5 documents in the table"); + deleteById(db.getConn(), TEST_TABLE, "four"); + assertEquals(4L, countAll(db.getConn(), TEST_TABLE), "There should now be 4 documents in the table"); + } + + public static void byIdNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should be 5 documents in the table"); + deleteById(db.getConn(), TEST_TABLE, "negative four"); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should still be 5 documents in the table"); + } + + public static void byFieldsMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should be 5 documents in the table"); + deleteByFields( db.getConn(), TEST_TABLE, List.of(Field.notEqual("value", "purple"))); + assertEquals(2L, countAll(db.getConn(), TEST_TABLE), "There should now be 2 documents in the table"); + } + + public static void byFieldsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should be 5 documents in the table"); + deleteByFields(db.getConn(), TEST_TABLE, List.of(Field.equal("value", "crimson"))); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should still be 5 documents in the table"); + } + + public static void byContainsMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should be 5 documents in the table"); + deleteByContains(db.getConn(), TEST_TABLE, Map.of("value", "purple")); + assertEquals(3L, countAll(db.getConn(), TEST_TABLE), "There should now be 3 documents in the table"); + } + + public static void byContainsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should be 5 documents in the table"); + deleteByContains(db.getConn(), TEST_TABLE, Map.of("target", "acquired")); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should still be 5 documents in the table"); + } + + public static void byJsonPathMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should be 5 documents in the table"); + deleteByJsonPath(db.getConn(), TEST_TABLE, "$.value ? (@ == \"purple\")"); + assertEquals(3L, countAll(db.getConn(), TEST_TABLE), "There should now be 3 documents in the table"); + } + + public static void byJsonPathNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should be 5 documents in the table"); + deleteByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ > 100)"); + assertEquals(5L, countAll(db.getConn(), TEST_TABLE), "There should still be 5 documents in the table"); + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/DocumentFunctions.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/DocumentFunctions.java new file mode 100644 index 0000000..6bec3a8 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/DocumentFunctions.java @@ -0,0 +1,138 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import solutions.bitbadger.documents.AutoId; +import solutions.bitbadger.documents.Configuration; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.Field; +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase; + +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.java.extensions.ConnExt.*; + +final public class DocumentFunctions { + + public static void insertDefault(ThrowawayDatabase db) throws DocumentException { + assertEquals(0L, countAll(db.getConn(), TEST_TABLE), "There should be no documents in the table"); + insert(db.getConn(), TEST_TABLE, new JsonDocument("turkey", "", 0, new SubDocument("gobble", "gobble!"))); + final List after = findAll(db.getConn(), TEST_TABLE, JsonDocument.class); + assertEquals(1, after.size(), "There should be one document in the table"); + final JsonDocument doc = after.get(0); + assertEquals("turkey", doc.getId(), "The inserted document's ID is incorrect"); + assertEquals("", doc.getValue(), "The inserted document's value is incorrect"); + assertEquals(0, doc.getNumValue(), "The document's numeric value is incorrect"); + assertNotNull(doc.getSub(), "The inserted document's subdocument was not created"); + assertEquals("gobble", doc.getSub().getFoo(), "The subdocument's \"foo\" property is incorrect"); + assertEquals("gobble!", doc.getSub().getBar(), "The subdocument's \"bar\" property is incorrect"); + } + + public static void insertDupe(ThrowawayDatabase db) throws DocumentException { + insert(db.getConn(), TEST_TABLE, new JsonDocument("a", "", 0)); + assertThrows(DocumentException.class, () -> insert(db.getConn(), TEST_TABLE, new JsonDocument("a", "b", 22)), + "Inserting a document with a duplicate key should have thrown an exception"); + } + + public static void insertNumAutoId(ThrowawayDatabase db) throws DocumentException { + try { + Configuration.autoIdStrategy = AutoId.NUMBER; + Configuration.idField = "key"; + assertEquals(0L, countAll(db.getConn(), TEST_TABLE), "There should be no documents in the table"); + + insert(db.getConn(), TEST_TABLE, new NumIdDocument(0, "one")); + insert(db.getConn(), TEST_TABLE, new NumIdDocument(0, "two")); + insert(db.getConn(), TEST_TABLE, new NumIdDocument(77, "three")); + insert(db.getConn(), TEST_TABLE, new NumIdDocument(0, "four")); + + final List after = findAll(db.getConn(), TEST_TABLE, NumIdDocument.class, + List.of(Field.named("key"))); + assertEquals(4, after.size(), "There should have been 4 documents returned"); + assertEquals("1|2|77|78", + after.stream().map(doc -> String.valueOf(doc.getKey())) + .reduce((acc, item) -> String.format("%s|%s", acc, item)).get(), + "The IDs were not generated correctly"); + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED; + Configuration.idField = "id"; + } + } + + public static void insertUUIDAutoId(ThrowawayDatabase db) throws DocumentException { + try { + Configuration.autoIdStrategy = AutoId.UUID; + assertEquals(0L, countAll(db.getConn(), TEST_TABLE), "There should be no documents in the table"); + + insert(db.getConn(), TEST_TABLE, new JsonDocument("")); + + final List after = findAll(db.getConn(), TEST_TABLE, JsonDocument.class); + assertEquals(1, after.size(), "There should have been 1 document returned"); + assertEquals(32, after.get(0).getId().length(), "The ID was not generated correctly"); + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED; + } + } + + public static void insertStringAutoId(ThrowawayDatabase db) throws DocumentException { + try { + Configuration.autoIdStrategy = AutoId.RANDOM_STRING; + assertEquals(0L, countAll(db.getConn(), TEST_TABLE), "There should be no documents in the table"); + + insert(db.getConn(), TEST_TABLE, new JsonDocument("")); + + Configuration.idStringLength = 21; + insert(db.getConn(), TEST_TABLE, new JsonDocument("")); + + final List after = findAll(db.getConn(), TEST_TABLE, JsonDocument.class); + assertEquals(2, after.size(), "There should have been 2 documents returned"); + assertEquals(16, after.get(0).getId().length(), "The first document's ID was not generated correctly"); + assertEquals(21, after.get(1).getId().length(), "The second document's ID was not generated correctly"); + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED; + Configuration.idStringLength = 16; + } + } + + public static void saveMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + save(db.getConn(), TEST_TABLE, new JsonDocument("two", "", 44)); + final Optional tryDoc = findById(db.getConn(), TEST_TABLE, "two", JsonDocument.class); + assertTrue(tryDoc.isPresent(), "There should have been a document returned"); + final JsonDocument doc = tryDoc.get(); + assertEquals("two", doc.getId(), "An incorrect document was returned"); + assertEquals("", doc.getValue(), "The \"value\" field was not updated"); + assertEquals(44, doc.getNumValue(), "The \"numValue\" field was not updated"); + assertNull(doc.getSub(), "The \"sub\" field was not updated"); + } + + public static void saveNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final JsonDocument toSave = new JsonDocument("test"); + toSave.setSub(new SubDocument("a", "b")); + save(db.getConn(), TEST_TABLE, toSave); + assertTrue(findById(db.getConn(), TEST_TABLE, "test", JsonDocument.class).isPresent(), + "The test document should have been saved"); + } + + public static void updateMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + update(db.getConn(), TEST_TABLE, "one", new JsonDocument("one", "howdy", 8, new SubDocument("y", "z"))); + final Optional tryDoc = findById(db.getConn(), TEST_TABLE, "one", JsonDocument.class); + assertTrue(tryDoc.isPresent(), "There should have been a document returned"); + final JsonDocument doc = tryDoc.get(); + assertEquals("one", doc.getId(), "An incorrect document was returned"); + assertEquals("howdy", doc.getValue(), "The \"value\" field was not updated"); + assertEquals(8, doc.getNumValue(), "The \"numValue\" field was not updated"); + assertNotNull(doc.getSub(), "The sub-document should not be null"); + assertEquals("y", doc.getSub().getFoo(), "The sub-document \"foo\" field was not updated"); + assertEquals("z", doc.getSub().getBar(), "The sub-document \"bar\" field was not updated"); + } + + public static void updateNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsById(db.getConn(), TEST_TABLE, "two-hundred")); + update(db.getConn(), TEST_TABLE, "two-hundred", new JsonDocument("two-hundred", "", 200)); + assertFalse(existsById(db.getConn(), TEST_TABLE, "two-hundred")); + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/ExistsFunctions.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/ExistsFunctions.java new file mode 100644 index 0000000..48464e7 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/ExistsFunctions.java @@ -0,0 +1,62 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.Field; +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.java.extensions.ConnExt.*; + +final public class ExistsFunctions { + + public static void byIdMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertTrue(existsById(db.getConn(), TEST_TABLE, "three"), "The document with ID \"three\" should exist"); + } + + public static void byIdNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsById(db.getConn(), TEST_TABLE, "seven"), "The document with ID \"seven\" should not exist"); + } + + public static void byFieldsMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertTrue(existsByFields(db.getConn(), TEST_TABLE, List.of(Field.equal("numValue", 10))), + "Matching documents should have been found"); + } + + public static void byFieldsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsByFields(db.getConn(), TEST_TABLE, List.of(Field.equal("nothing", "none"))), + "No matching documents should have been found"); + } + + public static void byContainsMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertTrue(existsByContains(db.getConn(), TEST_TABLE, Map.of("value", "purple")), + "Matching documents should have been found"); + } + + public static void byContainsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsByContains(db.getConn(), TEST_TABLE, Map.of("value", "violet")), + "Matching documents should not have been found"); + } + + public static void byJsonPathMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertTrue(existsByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ == 10)"), + "Matching documents should have been found"); + } + + public static void byJsonPathNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ == 10.1)"), + "Matching documents should not have been found"); + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/FindFunctions.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/FindFunctions.java new file mode 100644 index 0000000..5dbe009 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/FindFunctions.java @@ -0,0 +1,279 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import solutions.bitbadger.documents.Configuration; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.Field; +import solutions.bitbadger.documents.FieldMatch; +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.java.extensions.ConnExt.*; + +final public class FindFunctions { + + private static String docIds(List docs) { + return docs.stream().map(JsonDocument::getId).reduce((acc, docId) -> String.format("%s|%s", acc, docId)).get(); + } + + public static void allDefault(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(5, findAll(db.getConn(), TEST_TABLE, JsonDocument.class).size(), + "There should have been 5 documents returned"); + } + + public static void allAscending(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List docs = findAll(db.getConn(), TEST_TABLE, JsonDocument.class, + List.of(Field.named("id"))); + assertEquals(5, docs.size(), "There should have been 5 documents returned"); + assertEquals("five|four|one|three|two", docIds(docs), "The documents were not ordered correctly"); + } + + public static void allDescending(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List docs = findAll(db.getConn(), TEST_TABLE, JsonDocument.class, + List.of(Field.named("id DESC"))); + assertEquals(5, docs.size(), "There should have been 5 documents returned"); + assertEquals("two|three|one|four|five", docIds(docs), "The documents were not ordered correctly"); + } + + public static void allNumOrder(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List docs = findAll(db.getConn(), TEST_TABLE, JsonDocument.class, + List.of(Field.named("sub.foo NULLS LAST"), Field.named("n:numValue"))); + assertEquals(5, docs.size(), "There should have been 5 documents returned"); + assertEquals("two|four|one|three|five", docIds(docs), "The documents were not ordered correctly"); + } + + public static void allEmpty(ThrowawayDatabase db) throws DocumentException { + assertEquals(0, findAll(db.getConn(), TEST_TABLE, JsonDocument.class).size(), + "There should have been no documents returned"); + } + + public static void byIdString(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Optional doc = findById(db.getConn(), TEST_TABLE, "two", JsonDocument.class); + assertTrue(doc.isPresent(), "The document should have been returned"); + assertEquals("two", doc.get().getId(), "An incorrect document was returned"); + } + + public static void byIdNumber(ThrowawayDatabase db) throws DocumentException { + Configuration.idField = "key"; + try { + insert(db.getConn(), TEST_TABLE, new NumIdDocument(18, "howdy")); + final Optional doc = findById(db.getConn(), TEST_TABLE, 18, NumIdDocument.class); + assertTrue(doc.isPresent(), "The document should have been returned"); + } finally { + Configuration.idField = "id"; + } + } + + public static void byIdNotFound(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(findById(db.getConn(), TEST_TABLE, "x", JsonDocument.class).isPresent(), + "There should have been no document returned"); + } + + public static void byFieldsMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List docs = findByFields(db.getConn(), TEST_TABLE, + List.of(Field.any("value", List.of("blue", "purple")), Field.exists("sub")), JsonDocument.class, + FieldMatch.ALL); + assertEquals(1, docs.size(), "There should have been a document returned"); + assertEquals("four", docs.get(0).getId(), "The incorrect document was returned"); + } + + public static void byFieldsMatchOrdered(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List docs = findByFields(db.getConn(), TEST_TABLE, List.of(Field.equal("value", "purple")), + JsonDocument.class, null, List.of(Field.named("id"))); + assertEquals(2, docs.size(), "There should have been 2 documents returned"); + assertEquals("five|four", docIds(docs), "The documents were not ordered correctly"); + } + + public static void byFieldsMatchNumIn(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List docs = findByFields(db.getConn(), TEST_TABLE, + List.of(Field.any("numValue", List.of(2, 4, 6, 8))), JsonDocument.class); + assertEquals(1, docs.size(), "There should have been a document returned"); + assertEquals("three", docs.get(0).getId(), "The incorrect document was returned"); + } + + public static void byFieldsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(0, + findByFields(db.getConn(), TEST_TABLE, List.of(Field.greater("numValue", 100)), JsonDocument.class) + .size(), + "There should have been no documents returned"); + } + + public static void byFieldsMatchInArray(ThrowawayDatabase db) throws DocumentException { + for (final ArrayDocument doc : ArrayDocument.testDocuments) { insert(db.getConn(), TEST_TABLE, doc); } + final List docs = findByFields(db.getConn(), TEST_TABLE, + List.of(Field.inArray("values", TEST_TABLE, List.of("c"))), ArrayDocument.class); + assertEquals(2, docs.size(), "There should have been two documents returned"); + assertTrue(List.of("first", "second").contains(docs.get(0).getId()), + String.format("An incorrect document was returned (%s)", docs.get(0).getId())); + assertTrue(List.of("first", "second").contains(docs.get(1).getId()), + String.format("An incorrect document was returned (%s)", docs.get(1).getId())); + } + + public static void byFieldsNoMatchInArray(ThrowawayDatabase db) throws DocumentException { + for (final ArrayDocument doc : ArrayDocument.testDocuments) { insert(db.getConn(), TEST_TABLE, doc); } + assertEquals(0, + findByFields(db.getConn(), TEST_TABLE, List.of(Field.inArray("values", TEST_TABLE, List.of("j"))), + ArrayDocument.class).size(), + "There should have been no documents returned"); + } + + public static void byContainsMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List docs = findByContains(db.getConn(), TEST_TABLE, Map.of("value", "purple"), + JsonDocument.class); + assertEquals(2, docs.size(), "There should have been 2 documents returned"); + assertTrue(List.of("four", "five").contains(docs.get(0).getId()), + String.format("An incorrect document was returned (%s)", docs.get(0).getId())); + assertTrue(List.of("four", "five").contains(docs.get(1).getId()), + String.format("An incorrect document was returned (%s)", docs.get(1).getId())); + } + + public static void byContainsMatchOrdered(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List docs = findByContains(db.getConn(), TEST_TABLE, Map.of("sub", Map.of("foo", "green")), + JsonDocument.class, List.of(Field.named("value"))); + assertEquals(2, docs.size(), "There should have been 2 documents returned"); + assertEquals("two|four", docIds(docs), "The documents were not ordered correctly"); + } + + public static void byContainsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(0, findByContains(db.getConn(), TEST_TABLE, Map.of("value", "indigo"), JsonDocument.class).size(), + "There should have been no documents returned"); + } + + public static void byJsonPathMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List docs = findByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ > 10)", + JsonDocument.class); + assertEquals(2, docs.size(), "There should have been 2 documents returned"); + assertTrue(List.of("four", "five").contains(docs.get(0).getId()), + String.format("An incorrect document was returned (%s)", docs.get(0).getId())); + assertTrue(List.of("four", "five").contains(docs.get(1).getId()), + String.format("An incorrect document was returned (%s)", docs.get(1).getId())); } + + public static void byJsonPathMatchOrdered(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List docs = findByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ > 10)", + JsonDocument.class, List.of(Field.named("id"))); + assertEquals(2, docs.size(), "There should have been 2 documents returned"); + assertEquals("five|four", docIds(docs), "The documents were not ordered correctly"); + } + + public static void byJsonPathNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertEquals(0, findByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ > 100)", JsonDocument.class).size(), + "There should have been no documents returned"); + } + + public static void firstByFieldsMatchOne(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Optional doc = findFirstByFields(db.getConn(), TEST_TABLE, + List.of(Field.equal("value", "another")), JsonDocument.class); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertEquals("two", doc.get().getId(), "The incorrect document was returned"); + } + + public static void firstByFieldsMatchMany(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Optional doc = findFirstByFields(db.getConn(), TEST_TABLE, + List.of(Field.equal("sub.foo", "green")), JsonDocument.class); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertTrue(List.of("two", "four").contains(doc.get().getId()), + String.format("An incorrect document was returned (%s)", doc.get().getId())); + } + + public static void firstByFieldsMatchOrdered(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Optional doc = findFirstByFields(db.getConn(), TEST_TABLE, + List.of(Field.equal("sub.foo", "green")), JsonDocument.class, null, + List.of(Field.named("n:numValue DESC"))); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertEquals("four", doc.get().getId(), "An incorrect document was returned"); + } + + public static void firstByFieldsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(findFirstByFields(db.getConn(), TEST_TABLE, List.of(Field.equal("value", "absent")), + JsonDocument.class).isPresent(), + "There should have been no document returned"); + } + + public static void firstByContainsMatchOne(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Optional doc = findFirstByContains(db.getConn(), TEST_TABLE, Map.of("value", "FIRST!"), + JsonDocument.class); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertEquals("one", doc.get().getId(), "An incorrect document was returned"); + } + + public static void firstByContainsMatchMany(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Optional doc = findFirstByContains(db.getConn(), TEST_TABLE, Map.of("value", "purple"), + JsonDocument.class); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertTrue(List.of("four", "five").contains(doc.get().getId()), + String.format("An incorrect document was returned (%s)", doc.get().getId())); + } + + public static void firstByContainsMatchOrdered(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Optional doc = findFirstByContains(db.getConn(), TEST_TABLE, Map.of("value", "purple"), + JsonDocument.class, List.of(Field.named("sub.bar NULLS FIRST"))); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertEquals("five", doc.get().getId(), "An incorrect document was returned"); + } + + public static void firstByContainsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(findFirstByContains(db.getConn(), TEST_TABLE, Map.of("value", "indigo"), JsonDocument.class) + .isPresent(), + "There should have been no document returned"); + } + + public static void firstByJsonPathMatchOne(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Optional doc = findFirstByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ == 10)", + JsonDocument.class); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertEquals("two", doc.get().getId(), "An incorrect document was returned"); + } + + public static void firstByJsonPathMatchMany(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Optional doc = findFirstByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ > 10)", + JsonDocument.class); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertTrue(List.of("four", "five").contains(doc.get().getId()), + String.format("An incorrect document was returned (%s)", doc.get().getId())); + } + + public static void firstByJsonPathMatchOrdered(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Optional doc = findFirstByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ > 10)", + JsonDocument.class, List.of(Field.named("id DESC"))); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertEquals("four", doc.get().getId(), "An incorrect document was returned"); + } + + public static void firstByJsonPathNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(findFirstByJsonPath(db.getConn(), TEST_TABLE, "$.numValue ? (@ > 100)", JsonDocument.class) + .isPresent(), + "There should have been no document returned"); + } +} diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/JsonDocument.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/JsonDocument.java similarity index 88% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/support/JsonDocument.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/JsonDocument.java index ec6e639..cb85902 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/JsonDocument.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/JsonDocument.java @@ -1,13 +1,13 @@ -package solutions.bitbadger.documents.java.support; +package solutions.bitbadger.documents.core.tests.java.integration; import solutions.bitbadger.documents.DocumentException; -import solutions.bitbadger.documents.jvm.Document; -import solutions.bitbadger.documents.support.ThrowawayDatabase; +import solutions.bitbadger.documents.java.Document; +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase; import java.util.List; import static org.junit.jupiter.api.Assertions.fail; -import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; public class JsonDocument { diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/NumIdDocument.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/NumIdDocument.java new file mode 100644 index 0000000..f980c09 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/NumIdDocument.java @@ -0,0 +1,32 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +public class NumIdDocument { + + private int key; + private String value; + + public int getKey() { + return key; + } + + public void setKey(int key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public NumIdDocument(int key, String value) { + this.key = key; + this.value = value; + } + + public NumIdDocument() { + this(0, ""); + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PatchFunctions.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PatchFunctions.java new file mode 100644 index 0000000..324d2c6 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PatchFunctions.java @@ -0,0 +1,85 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.Field; +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.java.extensions.ConnExt.*; + +final public class PatchFunctions { + + public static void byIdMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + patchById(db.getConn(), TEST_TABLE, "one", Map.of("numValue", 44)); + final Optional doc = findById(db.getConn(), TEST_TABLE, "one", JsonDocument.class); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertEquals("one", doc.get().getId(), "An incorrect document was returned"); + assertEquals(44, doc.get().getNumValue(), "The document was not patched"); + } + + public static void byIdNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsById(db.getConn(), TEST_TABLE, "forty-seven"), + "Document with ID \"forty-seven\" should not exist"); + patchById(db.getConn(), TEST_TABLE, "forty-seven", Map.of("foo", "green")); // no exception = pass + } + + public static void byFieldsMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + patchByFields(db.getConn(), TEST_TABLE, List.of(Field.equal("value", "purple")), Map.of("numValue", 77)); + assertEquals(2L, countByFields(db.getConn(), TEST_TABLE, List.of(Field.equal("numValue", 77))), + "There should have been 2 documents with numeric value 77"); + } + + public static void byFieldsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List> fields = List.of(Field.equal("value", "burgundy")); + assertFalse(existsByFields(db.getConn(), TEST_TABLE, fields), + "There should be no documents with value of \"burgundy\""); + patchByFields(db.getConn(), TEST_TABLE, fields, Map.of("foo", "green")); // no exception = pass + } + + public static void byContainsMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Map contains = Map.of("value", "another"); + patchByContains(db.getConn(), TEST_TABLE, contains, Map.of("numValue", 12)); + final Optional doc = findFirstByContains(db.getConn(), TEST_TABLE, contains, JsonDocument.class); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertEquals("two", doc.get().getId(), "The incorrect document was returned"); + assertEquals(12, doc.get().getNumValue(), "The document was not updated"); + } + + public static void byContainsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Map contains = Map.of("value", "updated"); + assertFalse(existsByContains(db.getConn(), TEST_TABLE, contains), "There should be no matching documents"); + patchByContains(db.getConn(), TEST_TABLE, contains, Map.of("sub.foo", "green")); // no exception = pass + } + + public static void byJsonPathMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final String path = "$.numValue ? (@ > 10)"; + patchByJsonPath(db.getConn(), TEST_TABLE, path, Map.of("value", "blue")); + final List docs = findByJsonPath(db.getConn(), TEST_TABLE, path, JsonDocument.class); + assertEquals(2, docs.size(), "There should have been two documents returned"); + for (final JsonDocument doc : docs) { + assertTrue(List.of("four", "five").contains(doc.getId()), + String.format("An incorrect document was returned (%s)", doc.getId())); + assertEquals("blue", doc.getValue(), String.format("The value for ID %s was incorrect", doc.getId())); + } + } + + public static void byJsonPathNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final String path = "$.numValue ? (@ > 100)"; + assertFalse(existsByJsonPath(db.getConn(), TEST_TABLE, path), + "There should be no documents with numeric values over 100"); + patchByJsonPath(db.getConn(), TEST_TABLE, path, Map.of("value", "blue")); // no exception = pass + } +} diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/postgresql/CountIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLCountIT.java similarity index 86% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/postgresql/CountIT.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLCountIT.java index def860b..2d4c829 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/postgresql/CountIT.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLCountIT.java @@ -1,16 +1,15 @@ -package solutions.bitbadger.documents.java.jvm.integration.postgresql; +package solutions.bitbadger.documents.core.tests.java.integration; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import solutions.bitbadger.documents.DocumentException; -import solutions.bitbadger.documents.java.jvm.integration.common.CountFunctions; -import solutions.bitbadger.documents.jvm.integration.postgresql.PgDB; +import solutions.bitbadger.documents.core.tests.integration.PgDB; /** * PostgreSQL integration tests for the `Count` object / `count*` connection extension functions */ -@DisplayName("JVM | Java | PostgreSQL: Count") -public class CountIT { +@DisplayName("Core | Java | PostgreSQL: Count") +public class PostgreSQLCountIT { @Test @DisplayName("all counts all documents") diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/postgresql/CustomIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLCustomIT.java similarity index 85% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/postgresql/CustomIT.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLCustomIT.java index 3396255..d2c2b01 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/postgresql/CustomIT.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLCustomIT.java @@ -1,16 +1,15 @@ -package solutions.bitbadger.documents.java.jvm.integration.postgresql; +package solutions.bitbadger.documents.core.tests.java.integration; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import solutions.bitbadger.documents.DocumentException; -import solutions.bitbadger.documents.jvm.integration.postgresql.PgDB; -import solutions.bitbadger.documents.java.jvm.integration.common.CustomFunctions; +import solutions.bitbadger.documents.core.tests.integration.PgDB; /** * PostgreSQL integration tests for the `Custom` object / `custom*` connection extension functions */ -@DisplayName("JVM | Java | PostgreSQL: Custom") -final public class CustomIT { +@DisplayName("Core | Java | PostgreSQL: Custom") +final public class PostgreSQLCustomIT { @Test @DisplayName("list succeeds with empty list") diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLDefinitionIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLDefinitionIT.java new file mode 100644 index 0000000..6ba239d --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLDefinitionIT.java @@ -0,0 +1,45 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.PgDB; + +/** + * PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions + */ +@DisplayName("Core | Java | PostgreSQL: Definition") +final public class PostgreSQLDefinitionIT { + + @Test + @DisplayName("ensureTable creates table and index") + public void ensureTable() throws DocumentException { + try (PgDB db = new PgDB()) { + DefinitionFunctions.ensuresATable(db); + } + } + + @Test + @DisplayName("ensureFieldIndex creates an index") + public void ensureFieldIndex() throws DocumentException { + try (PgDB db = new PgDB()) { + DefinitionFunctions.ensuresAFieldIndex(db); + } + } + + @Test + @DisplayName("ensureDocumentIndex creates a full index") + public void ensureDocumentIndexFull() throws DocumentException { + try (PgDB db = new PgDB()) { + DefinitionFunctions.ensureDocumentIndexFull(db); + } + } + + @Test + @DisplayName("ensureDocumentIndex creates an optimized index") + public void ensureDocumentIndexOptimized() throws DocumentException { + try (PgDB db = new PgDB()) { + DefinitionFunctions.ensureDocumentIndexOptimized(db); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLDeleteIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLDeleteIT.java new file mode 100644 index 0000000..1ed918d --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLDeleteIT.java @@ -0,0 +1,77 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.PgDB; + +/** + * PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions + */ +@DisplayName("Core | Java | PostgreSQL: Delete") +final public class PostgreSQLDeleteIT { + + @Test + @DisplayName("byId deletes a matching ID") + public void byIdMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DeleteFunctions.byIdMatch(db); + } + } + + @Test + @DisplayName("byId succeeds when no ID matches") + public void byIdNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DeleteFunctions.byIdNoMatch(db); + } + } + + @Test + @DisplayName("byFields deletes matching documents") + public void byFieldsMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DeleteFunctions.byFieldsMatch(db); + } + } + + @Test + @DisplayName("byFields succeeds when no documents match") + public void byFieldsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DeleteFunctions.byFieldsNoMatch(db); + } + } + + @Test + @DisplayName("byContains deletes matching documents") + public void byContainsMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DeleteFunctions.byContainsMatch(db); + } + } + + @Test + @DisplayName("byContains succeeds when no documents match") + public void byContainsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DeleteFunctions.byContainsNoMatch(db); + } + } + + @Test + @DisplayName("byJsonPath deletes matching documents") + public void byJsonPathMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DeleteFunctions.byJsonPathMatch(db); + } + } + + @Test + @DisplayName("byJsonPath succeeds when no documents match") + public void byJsonPathNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DeleteFunctions.byJsonPathNoMatch(db); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLDocumentIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLDocumentIT.java new file mode 100644 index 0000000..a3fd24c --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLDocumentIT.java @@ -0,0 +1,85 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.PgDB; + +/** + * PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions + */ +@DisplayName("Core | Java | PostgreSQL: Document") +final public class PostgreSQLDocumentIT { + + @Test + @DisplayName("insert works with default values") + public void insertDefault() throws DocumentException { + try (PgDB db = new PgDB()) { + DocumentFunctions.insertDefault(db); + } + } + + @Test + @DisplayName("insert fails with duplicate key") + public void insertDupe() throws DocumentException { + try (PgDB db = new PgDB()) { + DocumentFunctions.insertDupe(db); + } + } + + @Test + @DisplayName("insert succeeds with numeric auto IDs") + public void insertNumAutoId() throws DocumentException { + try (PgDB db = new PgDB()) { + DocumentFunctions.insertNumAutoId(db); + } + } + + @Test + @DisplayName("insert succeeds with UUID auto ID") + public void insertUUIDAutoId() throws DocumentException { + try (PgDB db = new PgDB()) { + DocumentFunctions.insertUUIDAutoId(db); + } + } + + @Test + @DisplayName("insert succeeds with random string auto ID") + public void insertStringAutoId() throws DocumentException { + try (PgDB db = new PgDB()) { + DocumentFunctions.insertStringAutoId(db); + } + } + + @Test + @DisplayName("save updates an existing document") + public void saveMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DocumentFunctions.saveMatch(db); + } + } + + @Test + @DisplayName("save inserts a new document") + public void saveNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DocumentFunctions.saveNoMatch(db); + } + } + + @Test + @DisplayName("update replaces an existing document") + public void updateMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DocumentFunctions.updateMatch(db); + } + } + + @Test + @DisplayName("update succeeds when no document exists") + public void updateNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + DocumentFunctions.updateNoMatch(db); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLExistsIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLExistsIT.java new file mode 100644 index 0000000..e8b6437 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLExistsIT.java @@ -0,0 +1,77 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.PgDB; + +/** + * PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions + */ +@DisplayName("Core | Java | PostgreSQL: Exists") +final public class PostgreSQLExistsIT { + + @Test + @DisplayName("byId returns true when a document matches the ID") + public void byIdMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + ExistsFunctions.byIdMatch(db); + } + } + + @Test + @DisplayName("byId returns false when no document matches the ID") + public void byIdNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + ExistsFunctions.byIdNoMatch(db); + } + } + + @Test + @DisplayName("byFields returns true when documents match") + public void byFieldsMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + ExistsFunctions.byFieldsMatch(db); + } + } + + @Test + @DisplayName("byFields returns false when no documents match") + public void byFieldsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + ExistsFunctions.byFieldsNoMatch(db); + } + } + + @Test + @DisplayName("byContains returns true when documents match") + public void byContainsMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + ExistsFunctions.byContainsMatch(db); + } + } + + @Test + @DisplayName("byContains returns false when no documents match") + public void byContainsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + ExistsFunctions.byContainsNoMatch(db); + } + } + + @Test + @DisplayName("byJsonPath returns true when documents match") + public void byJsonPathMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + ExistsFunctions.byJsonPathMatch(db); + } + } + + @Test + @DisplayName("byJsonPath returns false when no documents match") + public void byJsonPathNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + ExistsFunctions.byJsonPathNoMatch(db); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLFindIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLFindIT.java new file mode 100644 index 0000000..920d34a --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLFindIT.java @@ -0,0 +1,269 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.PgDB; + +/** + * PostgreSQL integration tests for the `Find` object / `find*` connection extension functions + */ +@DisplayName("Core | Java | PostgreSQL: Find") +final public class PostgreSQLFindIT { + + @Test + @DisplayName("all retrieves all documents") + public void allDefault() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.allDefault(db); + } + } + + @Test + @DisplayName("all sorts data ascending") + public void allAscending() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.allAscending(db); + } + } + + @Test + @DisplayName("all sorts data descending") + public void allDescending() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.allDescending(db); + } + } + + @Test + @DisplayName("all sorts data numerically") + public void allNumOrder() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.allNumOrder(db); + } + } + + @Test + @DisplayName("all succeeds with an empty table") + public void allEmpty() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.allEmpty(db); + } + } + + @Test + @DisplayName("byId retrieves a document via a string ID") + public void byIdString() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byIdString(db); + } + } + + @Test + @DisplayName("byId retrieves a document via a numeric ID") + public void byIdNumber() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byIdNumber(db); + } + } + + @Test + @DisplayName("byId returns null when a matching ID is not found") + public void byIdNotFound() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byIdNotFound(db); + } + } + + @Test + @DisplayName("byFields retrieves matching documents") + public void byFieldsMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byFieldsMatch(db); + } + } + + @Test + @DisplayName("byFields retrieves ordered matching documents") + public void byFieldsMatchOrdered() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byFieldsMatchOrdered(db); + } + } + + @Test + @DisplayName("byFields retrieves matching documents with a numeric IN clause") + public void byFieldsMatchNumIn() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byFieldsMatchNumIn(db); + } + } + + @Test + @DisplayName("byFields succeeds when no documents match") + public void byFieldsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byFieldsNoMatch(db); + } + } + + @Test + @DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison") + public void byFieldsMatchInArray() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byFieldsMatchInArray(db); + } + } + + @Test + @DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison") + public void byFieldsNoMatchInArray() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byFieldsNoMatchInArray(db); + } + } + + @Test + @DisplayName("byContains retrieves matching documents") + public void byContainsMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byContainsMatch(db); + } + } + + @Test + @DisplayName("byContains retrieves ordered matching documents") + public void byContainsMatchOrdered() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byContainsMatchOrdered(db); + } + } + + @Test + @DisplayName("byContains succeeds when no documents match") + public void byContainsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byContainsNoMatch(db); + } + } + + @Test + @DisplayName("byJsonPath retrieves matching documents") + public void byJsonPathMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byJsonPathMatch(db); + } + } + + @Test + @DisplayName("byJsonPath retrieves ordered matching documents") + public void byJsonPathMatchOrdered() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byJsonPathMatchOrdered(db); + } + } + + @Test + @DisplayName("byJsonPath succeeds when no documents match") + public void byJsonPathNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.byJsonPathNoMatch(db); + } + } + + @Test + @DisplayName("firstByFields retrieves a matching document") + public void firstByFieldsMatchOne() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByFieldsMatchOne(db); + } + } + + @Test + @DisplayName("firstByFields retrieves a matching document among many") + public void firstByFieldsMatchMany() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByFieldsMatchMany(db); + } + } + + @Test + @DisplayName("firstByFields retrieves a matching document among many (ordered)") + public void firstByFieldsMatchOrdered() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByFieldsMatchOrdered(db); + } + } + + @Test + @DisplayName("firstByFields returns null when no document matches") + public void firstByFieldsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByFieldsNoMatch(db); + } + } + + @Test + @DisplayName("firstByContains retrieves a matching document") + public void firstByContainsMatchOne() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByContainsMatchOne(db); + } + } + + @Test + @DisplayName("firstByContains retrieves a matching document among many") + public void firstByContainsMatchMany() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByContainsMatchMany(db); + } + } + + @Test + @DisplayName("firstByContains retrieves a matching document among many (ordered)") + public void firstByContainsMatchOrdered() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByContainsMatchOrdered(db); + } + } + + @Test + @DisplayName("firstByContains returns null when no document matches") + public void firstByContainsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByContainsNoMatch(db); + } + } + + @Test + @DisplayName("firstByJsonPath retrieves a matching document") + public void firstByJsonPathMatchOne() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByJsonPathMatchOne(db); + } + } + + @Test + @DisplayName("firstByJsonPath retrieves a matching document among many") + public void firstByJsonPathMatchMany() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByJsonPathMatchMany(db); + } + } + + @Test + @DisplayName("firstByJsonPath retrieves a matching document among many (ordered)") + public void firstByJsonPathMatchOrdered() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByJsonPathMatchOrdered(db); + } + } + + @Test + @DisplayName("firstByJsonPath returns null when no document matches") + public void firstByJsonPathNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + FindFunctions.firstByJsonPathNoMatch(db); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLPatchIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLPatchIT.java new file mode 100644 index 0000000..2acd8fb --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLPatchIT.java @@ -0,0 +1,77 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.PgDB; + +/** + * PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions + */ +@DisplayName("Core | Java | PostgreSQL: Patch") +final public class PostgreSQLPatchIT { + + @Test + @DisplayName("byId patches an existing document") + public void byIdMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + PatchFunctions.byIdMatch(db); + } + } + + @Test + @DisplayName("byId succeeds for a non-existent document") + public void byIdNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + PatchFunctions.byIdNoMatch(db); + } + } + + @Test + @DisplayName("byFields patches matching document") + public void byFieldsMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + PatchFunctions.byFieldsMatch(db); + } + } + + @Test + @DisplayName("byFields succeeds when no documents match") + public void byFieldsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + PatchFunctions.byFieldsNoMatch(db); + } + } + + @Test + @DisplayName("byContains patches matching document") + public void byContainsMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + PatchFunctions.byContainsMatch(db); + } + } + + @Test + @DisplayName("byContains succeeds when no documents match") + public void byContainsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + PatchFunctions.byContainsNoMatch(db); + } + } + + @Test + @DisplayName("byJsonPath patches matching document") + public void byJsonPathMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + PatchFunctions.byJsonPathMatch(db); + } + } + + @Test + @DisplayName("byJsonPath succeeds when no documents match") + public void byJsonPathNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + PatchFunctions.byJsonPathNoMatch(db); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLRemoveFieldsIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLRemoveFieldsIT.java new file mode 100644 index 0000000..d01914e --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/PostgreSQLRemoveFieldsIT.java @@ -0,0 +1,109 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.PgDB; + +/** + * PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions + */ +@DisplayName("Core | Java | PostgreSQL: RemoveFields") +final public class PostgreSQLRemoveFieldsIT { + + @Test + @DisplayName("byId removes fields from an existing document") + public void byIdMatchFields() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byIdMatchFields(db); + } + } + + @Test + @DisplayName("byId succeeds when fields do not exist on an existing document") + public void byIdMatchNoFields() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byIdMatchNoFields(db); + } + } + + @Test + @DisplayName("byId succeeds when no document exists") + public void byIdNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byIdNoMatch(db); + } + } + + @Test + @DisplayName("byFields removes fields from matching documents") + public void byFieldsMatchFields() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byFieldsMatchFields(db); + } + } + + @Test + @DisplayName("byFields succeeds when fields do not exist on matching documents") + public void byFieldsMatchNoFields() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byFieldsMatchNoFields(db); + } + } + + @Test + @DisplayName("byFields succeeds when no matching documents exist") + public void byFieldsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byFieldsNoMatch(db); + } + } + + @Test + @DisplayName("byContains removes fields from matching documents") + public void byContainsMatchFields() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byContainsMatchFields(db); + } + } + + @Test + @DisplayName("byContains succeeds when fields do not exist on matching documents") + public void byContainsMatchNoFields() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byContainsMatchNoFields(db); + } + } + + @Test + @DisplayName("byContains succeeds when no matching documents exist") + public void byContainsNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byContainsNoMatch(db); + } + } + + @Test + @DisplayName("byJsonPath removes fields from matching documents") + public void byJsonPathMatchFields() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byJsonPathMatchFields(db); + } + } + + @Test + @DisplayName("byJsonPath succeeds when fields do not exist on matching documents") + public void byJsonPathMatchNoFields() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byJsonPathMatchNoFields(db); + } + } + + @Test + @DisplayName("byJsonPath succeeds when no matching documents exist") + public void byJsonPathNoMatch() throws DocumentException { + try (PgDB db = new PgDB()) { + RemoveFieldsFunctions.byJsonPathNoMatch(db); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/RemoveFieldsFunctions.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/RemoveFieldsFunctions.java new file mode 100644 index 0000000..8bdbd5c --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/RemoveFieldsFunctions.java @@ -0,0 +1,115 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.Field; +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static solutions.bitbadger.documents.core.tests.TypesKt.TEST_TABLE; +import static solutions.bitbadger.documents.java.extensions.ConnExt.*; + +final public class RemoveFieldsFunctions { + + public static void byIdMatchFields(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + removeFieldsById(db.getConn(), TEST_TABLE, "two", List.of("sub", "value")); + final Optional doc = findById(db.getConn(), TEST_TABLE, "two", JsonDocument.class); + assertTrue(doc.isPresent(), "There should have been a document returned"); + assertEquals("", doc.get().getValue(), "The value should have been empty"); + assertNull(doc.get().getSub(), "The sub-document should have been removed"); + } + + public static void byIdMatchNoFields(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsByFields(db.getConn(), TEST_TABLE, List.of(Field.exists("a_field_that_does_not_exist")))); + removeFieldsById(db.getConn(), TEST_TABLE, "one", List.of("a_field_that_does_not_exist")); // no exn = pass + } + + public static void byIdNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsById(db.getConn(), TEST_TABLE, "fifty")); + removeFieldsById(db.getConn(), TEST_TABLE, "fifty", List.of("sub")); // no exception = pass + } + + public static void byFieldsMatchFields(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List> fields = List.of(Field.equal("numValue", 17)); + removeFieldsByFields(db.getConn(), TEST_TABLE, fields, List.of("sub")); + final Optional doc = findFirstByFields(db.getConn(), TEST_TABLE, fields, JsonDocument.class); + assertTrue(doc.isPresent(), "The document should have been returned"); + assertEquals("four", doc.get().getId(), "An incorrect document was returned"); + assertNull(doc.get().getSub(), "The sub-document should have been removed"); + } + + public static void byFieldsMatchNoFields(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsByFields(db.getConn(), TEST_TABLE, List.of(Field.exists("nada")))); + removeFieldsByFields(db.getConn(), TEST_TABLE, List.of(Field.equal("numValue", 17)), List.of("nada")); + // no exception = pass + } + + public static void byFieldsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final List> fields = List.of(Field.notEqual("missing", "nope")); + assertFalse(existsByFields(db.getConn(), TEST_TABLE, fields)); + removeFieldsByFields(db.getConn(), TEST_TABLE, fields, List.of("value")); // no exception = pass + } + + public static void byContainsMatchFields(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Map> criteria = Map.of("sub", Map.of("foo", "green")); + removeFieldsByContains(db.getConn(), TEST_TABLE, criteria, List.of("value")); + final List docs = findByContains(db.getConn(), TEST_TABLE, criteria, JsonDocument.class); + assertEquals(2, docs.size(), "There should have been 2 documents returned"); + for (final JsonDocument doc : docs) { + assertTrue(List.of("two", "four").contains(doc.getId()), + String.format("An incorrect document was returned (%s)", doc.getId())); + assertEquals("", doc.getValue(), "The value should have been empty"); + } + } + + public static void byContainsMatchNoFields(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsByFields(db.getConn(), TEST_TABLE, List.of(Field.exists("invalid_field")))); + removeFieldsByContains(db.getConn(), TEST_TABLE, Map.of("sub", Map.of("foo", "green")), + List.of("invalid_field")); // no exception = pass + } + + public static void byContainsNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final Map contains = Map.of("value", "substantial"); + assertFalse(existsByContains(db.getConn(), TEST_TABLE, contains)); + removeFieldsByContains(db.getConn(), TEST_TABLE, contains, List.of("numValue")); // no exception = pass + } + + public static void byJsonPathMatchFields(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final String path = "$.value ? (@ == \"purple\")"; + removeFieldsByJsonPath(db.getConn(), TEST_TABLE, path, List.of("sub")); + final List docs = findByJsonPath(db.getConn(), TEST_TABLE, path, JsonDocument.class); + assertEquals(2, docs.size(), "There should have been 2 documents returned"); + for (final JsonDocument doc : docs) { + assertTrue(List.of("four", "five").contains(doc.getId()), + String.format("An incorrect document was returned (%s)", doc.getId())); + assertNull(doc.getSub(), "The sub-document should have been removed"); + } + } + + public static void byJsonPathMatchNoFields(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + assertFalse(existsByFields(db.getConn(), TEST_TABLE, List.of(Field.exists("submarine")))); + removeFieldsByJsonPath(db.getConn(), TEST_TABLE, "$.value ? (@ == \"purple\")", List.of("submarine")); + // no exception = pass + } + + public static void byJsonPathNoMatch(ThrowawayDatabase db) throws DocumentException { + JsonDocument.load(db); + final String path = "$.value ? (@ == \"mauve\")"; + assertFalse(existsByJsonPath(db.getConn(), TEST_TABLE, path)); + removeFieldsByJsonPath(db.getConn(), TEST_TABLE, path, List.of("value")); // no exception = pass + } +} diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/sqlite/CountIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteCountIT.java similarity index 84% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/sqlite/CountIT.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteCountIT.java index 1d9078b..bc8947e 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/sqlite/CountIT.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteCountIT.java @@ -1,18 +1,17 @@ -package solutions.bitbadger.documents.java.jvm.integration.sqlite; +package solutions.bitbadger.documents.core.tests.java.integration; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import solutions.bitbadger.documents.DocumentException; -import solutions.bitbadger.documents.java.jvm.integration.common.CountFunctions; -import solutions.bitbadger.documents.jvm.integration.sqlite.SQLiteDB; +import solutions.bitbadger.documents.core.tests.integration.SQLiteDB; import static org.junit.jupiter.api.Assertions.assertThrows; /** * SQLite integration tests for the `Count` object / `count*` connection extension functions */ -@DisplayName("JVM | Java | SQLite: Count") -public class CountIT { +@DisplayName("Core | Java | SQLite: Count") +public class SQLiteCountIT { @Test @DisplayName("all counts all documents") diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/sqlite/CustomIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteCustomIT.java similarity index 86% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/sqlite/CustomIT.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteCustomIT.java index 76acab9..6398554 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/jvm/integration/sqlite/CustomIT.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteCustomIT.java @@ -1,16 +1,15 @@ -package solutions.bitbadger.documents.java.jvm.integration.sqlite; +package solutions.bitbadger.documents.core.tests.java.integration; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import solutions.bitbadger.documents.DocumentException; -import solutions.bitbadger.documents.java.jvm.integration.common.CustomFunctions; -import solutions.bitbadger.documents.jvm.integration.sqlite.SQLiteDB; +import solutions.bitbadger.documents.core.tests.integration.SQLiteDB; /** * SQLite integration tests for the `Custom` object / `custom*` connection extension functions */ -@DisplayName("JVM | Java | SQLite: Custom") -final public class CustomIT { +@DisplayName("Core | Java | SQLite: Custom") +final public class SQLiteCustomIT { @Test @DisplayName("list succeeds with empty list") diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteDefinitionIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteDefinitionIT.java new file mode 100644 index 0000000..eabb698 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteDefinitionIT.java @@ -0,0 +1,47 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.SQLiteDB; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * SQLite integration tests for the `Definition` object / `ensure*` connection extension functions + */ +@DisplayName("Core | Java | SQLite: Definition") +final public class SQLiteDefinitionIT { + + @Test + @DisplayName("ensureTable creates table and index") + public void ensureTable() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DefinitionFunctions.ensuresATable(db); + } + } + + @Test + @DisplayName("ensureFieldIndex creates an index") + public void ensureFieldIndex() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DefinitionFunctions.ensuresAFieldIndex(db); + } + } + + @Test + @DisplayName("ensureDocumentIndex fails for full index") + public void ensureDocumentIndexFull() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> DefinitionFunctions.ensureDocumentIndexFull(db)); + } + } + + @Test + @DisplayName("ensureDocumentIndex fails for optimized index") + public void ensureDocumentIndexOptimized() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> DefinitionFunctions.ensureDocumentIndexOptimized(db)); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteDeleteIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteDeleteIT.java new file mode 100644 index 0000000..bfcf9d7 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteDeleteIT.java @@ -0,0 +1,63 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.SQLiteDB; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions + */ +@DisplayName("Core | Java | SQLite: Delete") +final public class SQLiteDeleteIT { + + @Test + @DisplayName("byId deletes a matching ID") + public void byIdMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DeleteFunctions.byIdMatch(db); + } + } + + @Test + @DisplayName("byId succeeds when no ID matches") + public void byIdNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DeleteFunctions.byIdNoMatch(db); + } + } + + @Test + @DisplayName("byFields deletes matching documents") + public void byFieldsMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DeleteFunctions.byFieldsMatch(db); + } + } + + @Test + @DisplayName("byFields succeeds when no documents match") + public void byFieldsNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DeleteFunctions.byFieldsNoMatch(db); + } + } + + @Test + @DisplayName("byContains fails") + public void byContainsMatch() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> DeleteFunctions.byContainsMatch(db)); + } + } + + @Test + @DisplayName("byJsonPath fails") + public void byJsonPathMatch() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> DeleteFunctions.byJsonPathMatch(db)); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteDocumentIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteDocumentIT.java new file mode 100644 index 0000000..d068af9 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteDocumentIT.java @@ -0,0 +1,84 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.SQLiteDB; + +/** + * SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions + */ +@DisplayName("Core | Java | SQLite: Document") +final public class SQLiteDocumentIT { + @Test + @DisplayName("insert works with default values") + public void insertDefault() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DocumentFunctions.insertDefault(db); + } + } + + @Test + @DisplayName("insert fails with duplicate key") + public void insertDupe() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DocumentFunctions.insertDupe(db); + } + } + + @Test + @DisplayName("insert succeeds with numeric auto IDs") + public void insertNumAutoId() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DocumentFunctions.insertNumAutoId(db); + } + } + + @Test + @DisplayName("insert succeeds with UUID auto ID") + public void insertUUIDAutoId() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DocumentFunctions.insertUUIDAutoId(db); + } + } + + @Test + @DisplayName("insert succeeds with random string auto ID") + public void insertStringAutoId() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DocumentFunctions.insertStringAutoId(db); + } + } + + @Test + @DisplayName("save updates an existing document") + public void saveMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DocumentFunctions.saveMatch(db); + } + } + + @Test + @DisplayName("save inserts a new document") + public void saveNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DocumentFunctions.saveNoMatch(db); + } + } + + @Test + @DisplayName("update replaces an existing document") + public void updateMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DocumentFunctions.updateMatch(db); + } + } + + @Test + @DisplayName("update succeeds when no document exists") + public void updateNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + DocumentFunctions.updateNoMatch(db); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteExistsIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteExistsIT.java new file mode 100644 index 0000000..c6a0aa6 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteExistsIT.java @@ -0,0 +1,63 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.SQLiteDB; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions + */ +@DisplayName("Core | Java | SQLite: Exists") +final public class SQLiteExistsIT { + + @Test + @DisplayName("byId returns true when a document matches the ID") + public void byIdMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + ExistsFunctions.byIdMatch(db); + } + } + + @Test + @DisplayName("byId returns false when no document matches the ID") + public void byIdNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + ExistsFunctions.byIdNoMatch(db); + } + } + + @Test + @DisplayName("byFields returns true when documents match") + public void byFieldsMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + ExistsFunctions.byFieldsMatch(db); + } + } + + @Test + @DisplayName("byFields returns false when no documents match") + public void byFieldsNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + ExistsFunctions.byFieldsNoMatch(db); + } + } + + @Test + @DisplayName("byContains fails") + public void byContainsMatch() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> ExistsFunctions.byContainsMatch(db)); + } + } + + @Test + @DisplayName("byJsonPath fails") + public void byJsonPathMatch() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> ExistsFunctions.byJsonPathMatch(db)); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteFindIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteFindIT.java new file mode 100644 index 0000000..66dc051 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteFindIT.java @@ -0,0 +1,191 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.SQLiteDB; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * SQLite integration tests for the `Find` object / `find*` connection extension functions + */ +@DisplayName("Core | Java | SQLite: Find") +final public class SQLiteFindIT { + + @Test + @DisplayName("all retrieves all documents") + public void allDefault() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.allDefault(db); + } + } + + @Test + @DisplayName("all sorts data ascending") + public void allAscending() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.allAscending(db); + } + } + + @Test + @DisplayName("all sorts data descending") + public void allDescending() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.allDescending(db); + } + } + + @Test + @DisplayName("all sorts data numerically") + public void allNumOrder() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.allNumOrder(db); + } + } + + @Test + @DisplayName("all succeeds with an empty table") + public void allEmpty() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.allEmpty(db); + } + } + + @Test + @DisplayName("byId retrieves a document via a string ID") + public void byIdString() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.byIdString(db); + } + } + + @Test + @DisplayName("byId retrieves a document via a numeric ID") + public void byIdNumber() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.byIdNumber(db); + } + } + + @Test + @DisplayName("byId returns null when a matching ID is not found") + public void byIdNotFound() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.byIdNotFound(db); + } + } + + @Test + @DisplayName("byFields retrieves matching documents") + public void byFieldsMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.byFieldsMatch(db); + } + } + + @Test + @DisplayName("byFields retrieves ordered matching documents") + public void byFieldsMatchOrdered() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.byFieldsMatchOrdered(db); + } + } + + @Test + @DisplayName("byFields retrieves matching documents with a numeric IN clause") + public void byFieldsMatchNumIn() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.byFieldsMatchNumIn(db); + } + } + + @Test + @DisplayName("byFields succeeds when no documents match") + public void byFieldsNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.byFieldsNoMatch(db); + } + } + + @Test + @DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison") + public void byFieldsMatchInArray() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.byFieldsMatchInArray(db); + } + } + + @Test + @DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison") + public void byFieldsNoMatchInArray() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.byFieldsNoMatchInArray(db); + } + } + + @Test + @DisplayName("byContains fails") + public void byContainsFails() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> FindFunctions.byContainsMatch(db)); + } + } + + @Test + @DisplayName("byJsonPath fails") + public void byJsonPathFails() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> FindFunctions.byJsonPathMatch(db)); + } + } + + @Test + @DisplayName("firstByFields retrieves a matching document") + public void firstByFieldsMatchOne() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.firstByFieldsMatchOne(db); + } + } + + @Test + @DisplayName("firstByFields retrieves a matching document among many") + public void firstByFieldsMatchMany() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.firstByFieldsMatchMany(db); + } + } + + @Test + @DisplayName("firstByFields retrieves a matching document among many (ordered)") + public void firstByFieldsMatchOrdered() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.firstByFieldsMatchOrdered(db); + } + } + + @Test + @DisplayName("firstByFields returns null when no document matches") + public void firstByFieldsNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + FindFunctions.firstByFieldsNoMatch(db); + } + } + + @Test + @DisplayName("firstByContains fails") + public void firstByContainsFails() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> FindFunctions.firstByContainsMatchOne(db)); + } + } + + @Test + @DisplayName("firstByJsonPath fails") + public void firstByJsonPathFails() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> FindFunctions.firstByJsonPathMatchOne(db)); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLitePatchIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLitePatchIT.java new file mode 100644 index 0000000..293d6d4 --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLitePatchIT.java @@ -0,0 +1,63 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.SQLiteDB; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions + */ +@DisplayName("Core | Java | SQLite: Patch") +final public class SQLitePatchIT { + + @Test + @DisplayName("byId patches an existing document") + public void byIdMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + PatchFunctions.byIdMatch(db); + } + } + + @Test + @DisplayName("byId succeeds for a non-existent document") + public void byIdNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + PatchFunctions.byIdNoMatch(db); + } + } + + @Test + @DisplayName("byFields patches matching document") + public void byFieldsMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + PatchFunctions.byFieldsMatch(db); + } + } + + @Test + @DisplayName("byFields succeeds when no documents match") + public void byFieldsNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + PatchFunctions.byFieldsNoMatch(db); + } + } + + @Test + @DisplayName("byContains fails") + public void byContainsMatch() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> ExistsFunctions.byContainsMatch(db)); + } + } + + @Test + @DisplayName("byJsonPath fails") + public void byJsonPathMatch() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> ExistsFunctions.byJsonPathMatch(db)); + } + } +} diff --git a/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteRemoveFieldsIT.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteRemoveFieldsIT.java new file mode 100644 index 0000000..3c825cc --- /dev/null +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SQLiteRemoveFieldsIT.java @@ -0,0 +1,79 @@ +package solutions.bitbadger.documents.core.tests.java.integration; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import solutions.bitbadger.documents.DocumentException; +import solutions.bitbadger.documents.core.tests.integration.SQLiteDB; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions + */ +@DisplayName("Core | Java | SQLite: RemoveFields") +final public class SQLiteRemoveFieldsIT { + + @Test + @DisplayName("byId removes fields from an existing document") + public void byIdMatchFields() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + RemoveFieldsFunctions.byIdMatchFields(db); + } + } + + @Test + @DisplayName("byId succeeds when fields do not exist on an existing document") + public void byIdMatchNoFields() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + RemoveFieldsFunctions.byIdMatchNoFields(db); + } + } + + @Test + @DisplayName("byId succeeds when no document exists") + public void byIdNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + RemoveFieldsFunctions.byIdNoMatch(db); + } + } + + @Test + @DisplayName("byFields removes fields from matching documents") + public void byFieldsMatchFields() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + RemoveFieldsFunctions.byFieldsMatchFields(db); + } + } + + @Test + @DisplayName("byFields succeeds when fields do not exist on matching documents") + public void byFieldsMatchNoFields() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + RemoveFieldsFunctions.byFieldsMatchNoFields(db); + } + } + + @Test + @DisplayName("byFields succeeds when no matching documents exist") + public void byFieldsNoMatch() throws DocumentException { + try (SQLiteDB db = new SQLiteDB()) { + RemoveFieldsFunctions.byFieldsNoMatch(db); + } + } + + @Test + @DisplayName("byContains fails") + public void byContainsMatch() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> RemoveFieldsFunctions.byContainsMatchFields(db)); + } + } + + @Test + @DisplayName("byJsonPath fails") + public void byJsonPathMatch() { + try (SQLiteDB db = new SQLiteDB()) { + assertThrows(DocumentException.class, () -> RemoveFieldsFunctions.byJsonPathMatchFields(db)); + } + } +} diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/SubDocument.java b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SubDocument.java similarity index 87% rename from src/jvm/src/test/java/solutions/bitbadger/documents/java/support/SubDocument.java rename to src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SubDocument.java index 6d5bdf9..843b51d 100644 --- a/src/jvm/src/test/java/solutions/bitbadger/documents/java/support/SubDocument.java +++ b/src/core/src/test/java/solutions/bitbadger/documents/core/tests/java/integration/SubDocument.java @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.java.support; +package solutions.bitbadger.documents.core.tests.java.integration; public class SubDocument { diff --git a/src/jvm/src/test/kotlin/AutoIdTest.kt b/src/core/src/test/kotlin/AutoIdTest.kt similarity index 94% rename from src/jvm/src/test/kotlin/AutoIdTest.kt rename to src/core/src/test/kotlin/AutoIdTest.kt index 87099ae..84f6f3e 100644 --- a/src/jvm/src/test/kotlin/AutoIdTest.kt +++ b/src/core/src/test/kotlin/AutoIdTest.kt @@ -1,9 +1,10 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -import solutions.bitbadger.documents.support.* +import solutions.bitbadger.documents.AutoId +import solutions.bitbadger.documents.DocumentException import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotEquals @@ -12,7 +13,7 @@ import kotlin.test.assertTrue /** * Unit tests for the `AutoId` enum */ -@DisplayName("JVM | Kotlin | AutoId") +@DisplayName("Core | Kotlin | AutoId") class AutoIdTest { @Test @@ -158,3 +159,9 @@ class AutoIdTest { assertThrows { AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id") } } } + +data class ByteIdClass(val id: Byte) +data class ShortIdClass(val id: Short) +data class IntIdClass(val id: Int) +data class LongIdClass(val id: Long) +data class StringIdClass(val id: String) diff --git a/src/jvm/src/test/kotlin/ComparisonTest.kt b/src/core/src/test/kotlin/ComparisonTest.kt similarity index 95% rename from src/jvm/src/test/kotlin/ComparisonTest.kt rename to src/core/src/test/kotlin/ComparisonTest.kt index 46c3cae..397ad3f 100644 --- a/src/jvm/src/test/kotlin/ComparisonTest.kt +++ b/src/core/src/test/kotlin/ComparisonTest.kt @@ -1,7 +1,8 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.* import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -9,7 +10,7 @@ import kotlin.test.assertTrue /** * Unit tests for the `ComparisonBetween` class */ -@DisplayName("JVM | Kotlin | ComparisonBetween") +@DisplayName("Core | Kotlin | ComparisonBetween") class ComparisonBetweenTest { @Test @@ -50,7 +51,7 @@ class ComparisonBetweenTest { /** * Unit tests for the `ComparisonIn` class */ -@DisplayName("JVM | Kotlin | ComparisonIn") +@DisplayName("Core | Kotlin | ComparisonIn") class ComparisonInTest { @Test @@ -92,7 +93,7 @@ class ComparisonInTest { /** * Unit tests for the `ComparisonInArray` class */ -@DisplayName("JVM | Kotlin | ComparisonInArray") +@DisplayName("Core | Kotlin | ComparisonInArray") class ComparisonInArrayTest { @Test @@ -138,7 +139,7 @@ class ComparisonInArrayTest { /** * Unit tests for the `ComparisonSingle` class */ -@DisplayName("JVM | Kotlin | ComparisonSingle") +@DisplayName("Core | Kotlin | ComparisonSingle") class ComparisonSingleTest { @Test diff --git a/src/jvm/src/test/kotlin/ConfigurationTest.kt b/src/core/src/test/kotlin/ConfigurationTest.kt similarity index 80% rename from src/jvm/src/test/kotlin/ConfigurationTest.kt rename to src/core/src/test/kotlin/ConfigurationTest.kt index bdc8e97..a740366 100644 --- a/src/jvm/src/test/kotlin/ConfigurationTest.kt +++ b/src/core/src/test/kotlin/ConfigurationTest.kt @@ -1,14 +1,18 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.DisplayName 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 kotlin.test.assertEquals /** * Unit tests for the `Configuration` object */ -@DisplayName("JVM | Kotlin | Configuration") +@DisplayName("Core | Kotlin | Configuration") class ConfigurationTest { @Test diff --git a/src/jvm/src/test/kotlin/query/CountQueryTest.kt b/src/core/src/test/kotlin/CountQueryTest.kt similarity index 87% rename from src/jvm/src/test/kotlin/query/CountQueryTest.kt rename to src/core/src/test/kotlin/CountQueryTest.kt index 9c3d413..d820473 100644 --- a/src/jvm/src/test/kotlin/query/CountQueryTest.kt +++ b/src/core/src/test/kotlin/CountQueryTest.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.query +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -6,14 +6,13 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TEST_TABLE +import solutions.bitbadger.documents.query.CountQuery import kotlin.test.assertEquals /** * Unit tests for the `Count` object */ -@DisplayName("JVM | Kotlin | Query | CountQuery") +@DisplayName("Core | Kotlin | Query | CountQuery") class CountQueryTest { /** @@ -27,7 +26,11 @@ class CountQueryTest { @Test @DisplayName("all generates correctly") fun all() = - assertEquals("SELECT COUNT(*) AS it FROM $TEST_TABLE", CountQuery.all(TEST_TABLE), "Count query not constructed correctly") + assertEquals( + "SELECT COUNT(*) AS it FROM $TEST_TABLE", + CountQuery.all(TEST_TABLE), + "Count query not constructed correctly" + ) @Test @DisplayName("byFields generates correctly | PostgreSQL") diff --git a/src/jvm/src/test/kotlin/query/DefinitionQueryTest.kt b/src/core/src/test/kotlin/DefinitionQueryTest.kt similarity index 95% rename from src/jvm/src/test/kotlin/query/DefinitionQueryTest.kt rename to src/core/src/test/kotlin/DefinitionQueryTest.kt index a0ccf41..697fffa 100644 --- a/src/jvm/src/test/kotlin/query/DefinitionQueryTest.kt +++ b/src/core/src/test/kotlin/DefinitionQueryTest.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.query +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -7,14 +7,13 @@ import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentIndex -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TEST_TABLE +import solutions.bitbadger.documents.query.DefinitionQuery import kotlin.test.assertEquals /** * Unit tests for the `Definition` object */ -@DisplayName("JVM | Kotlin | Query | DefinitionQuery") +@DisplayName("Core | Kotlin | Query | DefinitionQuery") class DefinitionQueryTest { /** diff --git a/src/jvm/src/test/kotlin/query/DeleteQueryTest.kt b/src/core/src/test/kotlin/DeleteQueryTest.kt similarity index 93% rename from src/jvm/src/test/kotlin/query/DeleteQueryTest.kt rename to src/core/src/test/kotlin/DeleteQueryTest.kt index cc56578..f33721b 100644 --- a/src/jvm/src/test/kotlin/query/DeleteQueryTest.kt +++ b/src/core/src/test/kotlin/DeleteQueryTest.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.query +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -6,14 +6,13 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TEST_TABLE +import solutions.bitbadger.documents.query.DeleteQuery import kotlin.test.assertEquals /** * Unit tests for the `Delete` object */ -@DisplayName("JVM | Kotlin | Query | DeleteQuery") +@DisplayName("Core | Kotlin | Query | DeleteQuery") class DeleteQueryTest { /** diff --git a/src/jvm/src/test/kotlin/DialectTest.kt b/src/core/src/test/kotlin/DialectTest.kt similarity index 87% rename from src/jvm/src/test/kotlin/DialectTest.kt rename to src/core/src/test/kotlin/DialectTest.kt index eb97769..ef7e916 100644 --- a/src/jvm/src/test/kotlin/DialectTest.kt +++ b/src/core/src/test/kotlin/DialectTest.kt @@ -1,7 +1,9 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.Dialect +import solutions.bitbadger.documents.DocumentException import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue @@ -10,7 +12,7 @@ import kotlin.test.fail /** * Unit tests for the `Dialect` enum */ -@DisplayName("JVM | Kotlin | Dialect") +@DisplayName("Core | Kotlin | Dialect") class DialectTest { @Test diff --git a/src/jvm/src/test/kotlin/DocumentIndexTest.kt b/src/core/src/test/kotlin/DocumentIndexTest.kt similarity index 78% rename from src/jvm/src/test/kotlin/DocumentIndexTest.kt rename to src/core/src/test/kotlin/DocumentIndexTest.kt index fccbb44..5ee6ef0 100644 --- a/src/jvm/src/test/kotlin/DocumentIndexTest.kt +++ b/src/core/src/test/kotlin/DocumentIndexTest.kt @@ -1,13 +1,14 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.DocumentIndex import kotlin.test.assertEquals /** * Unit tests for the `DocumentIndex` enum */ -@DisplayName("JVM | Kotlin | DocumentIndex") +@DisplayName("Core | Kotlin | DocumentIndex") class DocumentIndexTest { @Test diff --git a/src/jvm/src/test/kotlin/query/DocumentQueryTest.kt b/src/core/src/test/kotlin/DocumentQueryTest.kt similarity index 96% rename from src/jvm/src/test/kotlin/query/DocumentQueryTest.kt rename to src/core/src/test/kotlin/DocumentQueryTest.kt index e57340e..3565b41 100644 --- a/src/jvm/src/test/kotlin/query/DocumentQueryTest.kt +++ b/src/core/src/test/kotlin/DocumentQueryTest.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.query +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -7,15 +7,14 @@ import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.AutoId import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.DocumentException -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TEST_TABLE +import solutions.bitbadger.documents.query.DocumentQuery import kotlin.test.assertEquals import kotlin.test.assertTrue /** * Unit tests for the `Document` object */ -@DisplayName("JVM | Kotlin | Query | DocumentQuery") +@DisplayName("Core | Kotlin | Query | DocumentQuery") class DocumentQueryTest { /** diff --git a/src/jvm/src/test/kotlin/query/ExistsQueryTest.kt b/src/core/src/test/kotlin/ExistsQueryTest.kt similarity index 91% rename from src/jvm/src/test/kotlin/query/ExistsQueryTest.kt rename to src/core/src/test/kotlin/ExistsQueryTest.kt index 991a036..bfd066e 100644 --- a/src/jvm/src/test/kotlin/query/ExistsQueryTest.kt +++ b/src/core/src/test/kotlin/ExistsQueryTest.kt @@ -1,21 +1,18 @@ -package solutions.bitbadger.documents.query +package solutions.bitbadger.documents.core.tests 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 solutions.bitbadger.documents.query.ExistsQuery import kotlin.test.assertEquals /** * Unit tests for the `Exists` object */ -@DisplayName("JVM | Kotlin | Query | ExistsQuery") +@DisplayName("Core | Kotlin | Query | ExistsQuery") class ExistsQueryTest { /** diff --git a/src/jvm/src/test/kotlin/FieldMatchTest.kt b/src/core/src/test/kotlin/FieldMatchTest.kt similarity index 76% rename from src/jvm/src/test/kotlin/FieldMatchTest.kt rename to src/core/src/test/kotlin/FieldMatchTest.kt index 3976773..25ac7d7 100644 --- a/src/jvm/src/test/kotlin/FieldMatchTest.kt +++ b/src/core/src/test/kotlin/FieldMatchTest.kt @@ -1,13 +1,14 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.FieldMatch import kotlin.test.assertEquals /** * Unit tests for the `FieldMatch` enum */ -@DisplayName("JVM | Kotlin | FieldMatch") +@DisplayName("Core | Kotlin | FieldMatch") class FieldMatchTest { @Test diff --git a/src/jvm/src/test/kotlin/FieldTest.kt b/src/core/src/test/kotlin/FieldTest.kt similarity index 99% rename from src/jvm/src/test/kotlin/FieldTest.kt rename to src/core/src/test/kotlin/FieldTest.kt index d7a616a..3753e33 100644 --- a/src/jvm/src/test/kotlin/FieldTest.kt +++ b/src/core/src/test/kotlin/FieldTest.kt @@ -1,10 +1,10 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.core.tests 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.support.ForceDialect +import solutions.bitbadger.documents.* import kotlin.test.assertEquals import kotlin.test.assertNotSame import kotlin.test.assertNull @@ -12,7 +12,7 @@ import kotlin.test.assertNull /** * Unit tests for the `Field` class */ -@DisplayName("JVM | Kotlin | Field") +@DisplayName("Core | Kotlin | Field") class FieldTest { /** diff --git a/src/jvm/src/test/kotlin/query/FindQueryTest.kt b/src/core/src/test/kotlin/FindQueryTest.kt similarity index 93% rename from src/jvm/src/test/kotlin/query/FindQueryTest.kt rename to src/core/src/test/kotlin/FindQueryTest.kt index e458bf2..1f67bdd 100644 --- a/src/jvm/src/test/kotlin/query/FindQueryTest.kt +++ b/src/core/src/test/kotlin/FindQueryTest.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.query +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -6,14 +6,13 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TEST_TABLE +import solutions.bitbadger.documents.query.FindQuery import kotlin.test.assertEquals /** * Unit tests for the `Find` object */ -@DisplayName("JVM | Kotlin | Query | FindQuery") +@DisplayName("Core | Kotlin | Query | FindQuery") class FindQueryTest { /** diff --git a/src/jvm/src/test/kotlin/support/ForceDialect.kt b/src/core/src/test/kotlin/ForceDialect.kt similarity index 90% rename from src/jvm/src/test/kotlin/support/ForceDialect.kt rename to src/core/src/test/kotlin/ForceDialect.kt index 681361c..d77dee1 100644 --- a/src/jvm/src/test/kotlin/support/ForceDialect.kt +++ b/src/core/src/test/kotlin/ForceDialect.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.support +package solutions.bitbadger.documents.core.tests import solutions.bitbadger.documents.Configuration diff --git a/src/jvm/src/test/kotlin/OpTest.kt b/src/core/src/test/kotlin/OpTest.kt similarity index 94% rename from src/jvm/src/test/kotlin/OpTest.kt rename to src/core/src/test/kotlin/OpTest.kt index 19e0d91..1011512 100644 --- a/src/jvm/src/test/kotlin/OpTest.kt +++ b/src/core/src/test/kotlin/OpTest.kt @@ -1,13 +1,14 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.Op import kotlin.test.assertEquals /** * Unit tests for the `Op` enum */ -@DisplayName("JVM | Kotlin | Op") +@DisplayName("Core | Kotlin | Op") class OpTest { @Test diff --git a/src/jvm/src/test/kotlin/ParameterNameTest.kt b/src/core/src/test/kotlin/ParameterNameTest.kt similarity index 87% rename from src/jvm/src/test/kotlin/ParameterNameTest.kt rename to src/core/src/test/kotlin/ParameterNameTest.kt index 4e67e4a..f6c6f51 100644 --- a/src/jvm/src/test/kotlin/ParameterNameTest.kt +++ b/src/core/src/test/kotlin/ParameterNameTest.kt @@ -1,13 +1,14 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.ParameterName import kotlin.test.assertEquals /** * Unit tests for the `ParameterName` class */ -@DisplayName("JVM | Kotlin | ParameterName") +@DisplayName("Core | Kotlin | ParameterName") class ParameterNameTest { @Test diff --git a/src/jvm/src/test/kotlin/ParameterTest.kt b/src/core/src/test/kotlin/ParameterTest.kt similarity index 83% rename from src/jvm/src/test/kotlin/ParameterTest.kt rename to src/core/src/test/kotlin/ParameterTest.kt index 0ec84ae..7b26ebe 100644 --- a/src/jvm/src/test/kotlin/ParameterTest.kt +++ b/src/core/src/test/kotlin/ParameterTest.kt @@ -1,7 +1,10 @@ -package solutions.bitbadger.documents +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.DocumentException +import solutions.bitbadger.documents.Parameter +import solutions.bitbadger.documents.ParameterType import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull @@ -9,7 +12,7 @@ import kotlin.test.assertNull /** * Unit tests for the `Parameter` class */ -@DisplayName("JVM | Kotlin | Parameter") +@DisplayName("Core | Kotlin | Parameter") class ParameterTest { @Test diff --git a/src/jvm/src/test/kotlin/jvm/ParametersTest.kt b/src/core/src/test/kotlin/ParametersTest.kt similarity index 97% rename from src/jvm/src/test/kotlin/jvm/ParametersTest.kt rename to src/core/src/test/kotlin/ParametersTest.kt index 33a1af2..15dcddc 100644 --- a/src/jvm/src/test/kotlin/jvm/ParametersTest.kt +++ b/src/core/src/test/kotlin/ParametersTest.kt @@ -1,10 +1,10 @@ -package solutions.bitbadger.documents.jvm +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.support.ForceDialect +import solutions.bitbadger.documents.java.Parameters import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotSame @@ -13,7 +13,7 @@ import kotlin.test.assertSame /** * Unit tests for the `Parameters` object */ -@DisplayName("JVM | Kotlin | Parameters") +@DisplayName("Core | Kotlin | Parameters") class ParametersTest { /** diff --git a/src/jvm/src/test/kotlin/query/PatchQueryTest.kt b/src/core/src/test/kotlin/PatchQueryTest.kt similarity index 93% rename from src/jvm/src/test/kotlin/query/PatchQueryTest.kt rename to src/core/src/test/kotlin/PatchQueryTest.kt index dbb4169..0d17457 100644 --- a/src/jvm/src/test/kotlin/query/PatchQueryTest.kt +++ b/src/core/src/test/kotlin/PatchQueryTest.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.query +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -6,14 +6,13 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TEST_TABLE +import solutions.bitbadger.documents.query.PatchQuery import kotlin.test.assertEquals /** * Unit tests for the `Patch` object */ -@DisplayName("JVM | Kotlin | Query | PatchQuery") +@DisplayName("Core | Kotlin | Query | PatchQuery") class PatchQueryTest { /** diff --git a/src/jvm/src/test/kotlin/query/QueryTest.kt b/src/core/src/test/kotlin/QueryTest.kt similarity index 97% rename from src/jvm/src/test/kotlin/query/QueryTest.kt rename to src/core/src/test/kotlin/QueryTest.kt index 1d3ec1d..3bd65f6 100644 --- a/src/jvm/src/test/kotlin/query/QueryTest.kt +++ b/src/core/src/test/kotlin/QueryTest.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.query +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -6,13 +6,13 @@ import org.junit.jupiter.api.Test import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.FieldMatch -import solutions.bitbadger.documents.support.ForceDialect +import solutions.bitbadger.documents.query.* import kotlin.test.assertEquals /** * Unit tests for the package-level query functions */ -@DisplayName("JVM | Kotlin | Query | Package Functions") +@DisplayName("Core | Kotlin | Query | Package Functions") class QueryTest { /** diff --git a/src/jvm/src/test/kotlin/query/RemoveFieldsQueryTest.kt b/src/core/src/test/kotlin/RemoveFieldsQueryTest.kt similarity index 94% rename from src/jvm/src/test/kotlin/query/RemoveFieldsQueryTest.kt rename to src/core/src/test/kotlin/RemoveFieldsQueryTest.kt index 06a2523..fde9db1 100644 --- a/src/jvm/src/test/kotlin/query/RemoveFieldsQueryTest.kt +++ b/src/core/src/test/kotlin/RemoveFieldsQueryTest.kt @@ -1,18 +1,17 @@ -package solutions.bitbadger.documents.query +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TEST_TABLE +import solutions.bitbadger.documents.query.RemoveFieldsQuery import kotlin.test.assertEquals /** * Unit tests for the `RemoveFields` object */ -@DisplayName("JVM | Kotlin | Query | RemoveFieldsQuery") +@DisplayName("Core | Kotlin | Query | RemoveFieldsQuery") class RemoveFieldsQueryTest { /** diff --git a/src/kotlin/src/test/kotlin/Types.kt b/src/core/src/test/kotlin/Types.kt similarity index 75% rename from src/kotlin/src/test/kotlin/Types.kt rename to src/core/src/test/kotlin/Types.kt index 51d0b38..cc222fa 100644 --- a/src/kotlin/src/test/kotlin/Types.kt +++ b/src/core/src/test/kotlin/Types.kt @@ -1,22 +1,19 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.core.tests -import kotlinx.serialization.Serializable -import solutions.bitbadger.documents.extensions.insert +import solutions.bitbadger.documents.core.tests.integration.ThrowawayDatabase +import solutions.bitbadger.documents.java.Document -/** The test table name to use for integration tests */ +/** The test table name to use for unit/integration tests */ const val TEST_TABLE = "test_table" -@Serializable data class NumIdDocument(val key: Int, val text: String) { constructor() : this(0, "") } -@Serializable data class SubDocument(val foo: String, val bar: String) { constructor() : this("", "") } -@Serializable data class ArrayDocument(val id: String, val values: List) { constructor() : this("", listOf()) @@ -31,7 +28,6 @@ data class ArrayDocument(val id: String, val values: List) { } } -@Serializable data class JsonDocument(val id: String, val value: String = "", val numValue: Int = 0, val sub: SubDocument? = null) { constructor() : this("") @@ -46,7 +42,7 @@ data class JsonDocument(val id: String, val value: String = "", val numValue: In JsonDocument("five", "purple", 18, null) ) -// fun load(db: ThrowawayDatabase, tableName: String = TEST_TABLE) = -// testDocuments.forEach { db.conn.insert(tableName, it) } + fun load(db: ThrowawayDatabase, tableName: String = TEST_TABLE) = + testDocuments.forEach { Document.insert(tableName, it, db.conn) } } } diff --git a/src/jvm/src/test/kotlin/query/WhereTest.kt b/src/core/src/test/kotlin/WhereTest.kt similarity index 97% rename from src/jvm/src/test/kotlin/query/WhereTest.kt rename to src/core/src/test/kotlin/WhereTest.kt index c436ec8..58cc59d 100644 --- a/src/jvm/src/test/kotlin/query/WhereTest.kt +++ b/src/core/src/test/kotlin/WhereTest.kt @@ -1,17 +1,17 @@ -package solutions.bitbadger.documents.query +package solutions.bitbadger.documents.core.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.support.ForceDialect +import solutions.bitbadger.documents.query.Where import kotlin.test.assertEquals /** * Unit tests for the `Where` object */ -@DisplayName("JVM | Kotlin | Query | Where") +@DisplayName("Core | Kotlin | Query | Where") class WhereTest { /** diff --git a/src/jvm/src/test/kotlin/jvm/integration/common/Count.kt b/src/core/src/test/kotlin/integration/CountFunctions.kt similarity index 88% rename from src/jvm/src/test/kotlin/jvm/integration/common/Count.kt rename to src/core/src/test/kotlin/integration/CountFunctions.kt index 5ed8ecc..48155bf 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/common/Count.kt +++ b/src/core/src/test/kotlin/integration/CountFunctions.kt @@ -1,14 +1,15 @@ -package solutions.bitbadger.documents.jvm.integration.common +package solutions.bitbadger.documents.core.tests.integration import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.extensions.* -import solutions.bitbadger.documents.support.* +import solutions.bitbadger.documents.core.tests.JsonDocument +import solutions.bitbadger.documents.core.tests.TEST_TABLE +import solutions.bitbadger.documents.java.extensions.* import kotlin.test.assertEquals /** * Integration tests for the `Count` object */ -object Count { +object CountFunctions { fun all(db: ThrowawayDatabase) { JsonDocument.load(db) diff --git a/src/jvm/src/test/kotlin/jvm/integration/common/Custom.kt b/src/core/src/test/kotlin/integration/CustomFunctions.kt similarity index 73% rename from src/jvm/src/test/kotlin/jvm/integration/common/Custom.kt rename to src/core/src/test/kotlin/integration/CustomFunctions.kt index 19e0404..e25fff4 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/common/Custom.kt +++ b/src/core/src/test/kotlin/integration/CustomFunctions.kt @@ -1,44 +1,54 @@ -package solutions.bitbadger.documents.jvm.integration.common +package solutions.bitbadger.documents.core.tests.integration import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.extensions.* -import solutions.bitbadger.documents.jvm.Results +import solutions.bitbadger.documents.core.tests.* +import solutions.bitbadger.documents.java.extensions.* +import solutions.bitbadger.documents.java.Results import solutions.bitbadger.documents.query.CountQuery import solutions.bitbadger.documents.query.DeleteQuery import solutions.bitbadger.documents.query.FindQuery -import solutions.bitbadger.documents.support.* -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull +import kotlin.test.* /** * Integration tests for the `Custom` object */ -object Custom { +object CustomFunctions { fun listEmpty(db: ThrowawayDatabase) { JsonDocument.load(db) db.conn.deleteByFields(TEST_TABLE, listOf(Field.exists(Configuration.idField))) - val result = db.conn.customList(FindQuery.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData) + val result = + db.conn.customList(FindQuery.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData) assertEquals(0, result.size, "There should have been no results") } fun listAll(db: ThrowawayDatabase) { JsonDocument.load(db) - val result = db.conn.customList(FindQuery.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData) + val result = + db.conn.customList(FindQuery.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData) assertEquals(5, result.size, "There should have been 5 results") } fun singleNone(db: ThrowawayDatabase) = - assertNull( - db.conn.customSingle(FindQuery.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData), + assertFalse( + db.conn.customSingle( + FindQuery.all(TEST_TABLE), + listOf(), + JsonDocument::class.java, + Results::fromData + ).isPresent, "There should not have been a document returned" ) fun singleOne(db: ThrowawayDatabase) { JsonDocument.load(db) - assertNotNull( - db.conn.customSingle(FindQuery.all(TEST_TABLE), listOf(), JsonDocument::class.java, Results::fromData), + assertTrue( + db.conn.customSingle( + FindQuery.all(TEST_TABLE), + listOf(), + JsonDocument::class.java, + Results::fromData + ).isPresent, "There should not have been a document returned" ) } diff --git a/src/jvm/src/test/kotlin/jvm/integration/common/Definition.kt b/src/core/src/test/kotlin/integration/DefinitionFunctions.kt similarity index 84% rename from src/jvm/src/test/kotlin/jvm/integration/common/Definition.kt rename to src/core/src/test/kotlin/integration/DefinitionFunctions.kt index 0d7aa26..dde6574 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/common/Definition.kt +++ b/src/core/src/test/kotlin/integration/DefinitionFunctions.kt @@ -1,18 +1,15 @@ -package solutions.bitbadger.documents.jvm.integration.common +package solutions.bitbadger.documents.core.tests.integration import solutions.bitbadger.documents.DocumentIndex -import solutions.bitbadger.documents.extensions.ensureDocumentIndex -import solutions.bitbadger.documents.extensions.ensureFieldIndex -import solutions.bitbadger.documents.extensions.ensureTable -import solutions.bitbadger.documents.support.TEST_TABLE -import solutions.bitbadger.documents.support.ThrowawayDatabase +import solutions.bitbadger.documents.core.tests.TEST_TABLE +import solutions.bitbadger.documents.java.extensions.* import kotlin.test.assertFalse import kotlin.test.assertTrue /** * Integration tests for the `Definition` object / `ensure*` connection extension functions */ -object Definition { +object DefinitionFunctions { fun ensureTable(db: ThrowawayDatabase) { assertFalse(db.dbObjectExists("ensured"), "The 'ensured' table should not exist") diff --git a/src/jvm/src/test/kotlin/jvm/integration/common/Delete.kt b/src/core/src/test/kotlin/integration/DeleteFunctions.kt similarity index 90% rename from src/jvm/src/test/kotlin/jvm/integration/common/Delete.kt rename to src/core/src/test/kotlin/integration/DeleteFunctions.kt index e0ceada..919947a 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/common/Delete.kt +++ b/src/core/src/test/kotlin/integration/DeleteFunctions.kt @@ -1,16 +1,14 @@ -package solutions.bitbadger.documents.jvm.integration.common +package solutions.bitbadger.documents.core.tests.integration import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.extensions.* -import solutions.bitbadger.documents.support.JsonDocument -import solutions.bitbadger.documents.support.TEST_TABLE -import solutions.bitbadger.documents.support.ThrowawayDatabase +import solutions.bitbadger.documents.core.tests.* +import solutions.bitbadger.documents.java.extensions.* import kotlin.test.assertEquals /** * Integration tests for the `Delete` object */ -object Delete { +object DeleteFunctions { fun byIdMatch(db: ThrowawayDatabase) { JsonDocument.load(db) diff --git a/src/jvm/src/test/kotlin/jvm/integration/common/Document.kt b/src/core/src/test/kotlin/integration/DocumentFunctions.kt similarity index 86% rename from src/jvm/src/test/kotlin/jvm/integration/common/Document.kt rename to src/core/src/test/kotlin/integration/DocumentFunctions.kt index 5b8e7ec..dee2043 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/common/Document.kt +++ b/src/core/src/test/kotlin/integration/DocumentFunctions.kt @@ -1,16 +1,18 @@ -package solutions.bitbadger.documents.jvm.integration.common +package solutions.bitbadger.documents.core.tests.integration +import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.AutoId import solutions.bitbadger.documents.Configuration +import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.extensions.* -import solutions.bitbadger.documents.support.* +import solutions.bitbadger.documents.core.tests.* +import solutions.bitbadger.documents.java.extensions.* import kotlin.test.* /** * Integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions */ -object Document { +object DocumentFunctions { fun insertDefault(db: ThrowawayDatabase) { assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table") @@ -23,11 +25,8 @@ object Document { fun insertDupe(db: ThrowawayDatabase) { db.conn.insert(TEST_TABLE, JsonDocument("a", "", 0, null)) - try { + assertThrows("Inserting a document with a duplicate key should have thrown an exception") { db.conn.insert(TEST_TABLE, JsonDocument("a", "b", 22, null)) - fail("Inserting a document with a duplicate key should have thrown an exception") - } catch (_: Exception) { - // yay } } @@ -92,8 +91,9 @@ object Document { fun saveMatch(db: ThrowawayDatabase) { JsonDocument.load(db) db.conn.save(TEST_TABLE, JsonDocument("two", numValue = 44)) - val doc = db.conn.findById(TEST_TABLE, "two", JsonDocument::class.java) - assertNotNull(doc, "There should have been a document returned") + val tryDoc = db.conn.findById(TEST_TABLE, "two", JsonDocument::class.java) + assertTrue(tryDoc.isPresent, "There should have been a document returned") + val doc = tryDoc.get() assertEquals("two", doc.id, "An incorrect document was returned") assertEquals("", doc.value, "The \"value\" field was not updated") assertEquals(44, doc.numValue, "The \"numValue\" field was not updated") @@ -103,8 +103,8 @@ object Document { fun saveNoMatch(db: ThrowawayDatabase) { JsonDocument.load(db) db.conn.save(TEST_TABLE, JsonDocument("test", sub = SubDocument("a", "b"))) - assertNotNull( - db.conn.findById(TEST_TABLE, "test", JsonDocument::class.java), + assertTrue( + db.conn.findById(TEST_TABLE, "test", JsonDocument::class.java).isPresent, "The test document should have been saved" ) } @@ -112,8 +112,9 @@ object Document { fun updateMatch(db: ThrowawayDatabase) { JsonDocument.load(db) db.conn.update(TEST_TABLE, "one", JsonDocument("one", "howdy", 8, SubDocument("y", "z"))) - val doc = db.conn.findById(TEST_TABLE, "one", JsonDocument::class.java) - assertNotNull(doc, "There should have been a document returned") + val tryDoc = db.conn.findById(TEST_TABLE, "one", JsonDocument::class.java) + assertTrue(tryDoc.isPresent, "There should have been a document returned") + val doc = tryDoc.get() assertEquals("one", doc.id, "An incorrect document was returned") assertEquals("howdy", doc.value, "The \"value\" field was not updated") assertEquals(8, doc.numValue, "The \"numValue\" field was not updated") diff --git a/src/jvm/src/test/kotlin/jvm/integration/common/Exists.kt b/src/core/src/test/kotlin/integration/ExistsFunctions.kt similarity index 91% rename from src/jvm/src/test/kotlin/jvm/integration/common/Exists.kt rename to src/core/src/test/kotlin/integration/ExistsFunctions.kt index a981f81..194aefb 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/common/Exists.kt +++ b/src/core/src/test/kotlin/integration/ExistsFunctions.kt @@ -1,15 +1,15 @@ -package solutions.bitbadger.documents.jvm.integration.common +package solutions.bitbadger.documents.core.tests.integration import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.extensions.* -import solutions.bitbadger.documents.support.* +import solutions.bitbadger.documents.core.tests.* +import solutions.bitbadger.documents.java.extensions.* import kotlin.test.assertFalse import kotlin.test.assertTrue /** * Integration tests for the `Exists` object */ -object Exists { +object ExistsFunctions { fun byIdMatch(db: ThrowawayDatabase) { JsonDocument.load(db) diff --git a/src/jvm/src/test/kotlin/jvm/integration/common/Find.kt b/src/core/src/test/kotlin/integration/FindFunctions.kt similarity index 79% rename from src/jvm/src/test/kotlin/jvm/integration/common/Find.kt rename to src/core/src/test/kotlin/integration/FindFunctions.kt index 03aaa94..23e14a1 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/common/Find.kt +++ b/src/core/src/test/kotlin/integration/FindFunctions.kt @@ -1,19 +1,16 @@ -package solutions.bitbadger.documents.jvm.integration.common +package solutions.bitbadger.documents.core.tests.integration import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.FieldMatch -import solutions.bitbadger.documents.extensions.* -import solutions.bitbadger.documents.support.* -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.assertTrue +import solutions.bitbadger.documents.core.tests.* +import solutions.bitbadger.documents.java.extensions.* +import kotlin.test.* /** * Integration tests for the `Find` object */ -object Find { +object FindFunctions { fun allDefault(db: ThrowawayDatabase) { JsonDocument.load(db) @@ -71,8 +68,8 @@ object Find { fun byIdString(db: ThrowawayDatabase) { JsonDocument.load(db) val doc = db.conn.findById(TEST_TABLE, "two", JsonDocument::class.java) - assertNotNull(doc, "The document should have been returned") - assertEquals("two", doc.id, "An incorrect document was returned") + assertTrue(doc.isPresent, "The document should have been returned") + assertEquals("two", doc.get().id, "An incorrect document was returned") } fun byIdNumber(db: ThrowawayDatabase) { @@ -80,7 +77,7 @@ object Find { try { db.conn.insert(TEST_TABLE, NumIdDocument(18, "howdy")) val doc = db.conn.findById(TEST_TABLE, 18, NumIdDocument::class.java) - assertNotNull(doc, "The document should have been returned") + assertTrue(doc.isPresent, "The document should have been returned") } finally { Configuration.idField = "id" } @@ -88,8 +85,8 @@ object Find { fun byIdNotFound(db: ThrowawayDatabase) { JsonDocument.load(db) - assertNull( - db.conn.findById(TEST_TABLE, "x", JsonDocument::class.java), + assertFalse( + db.conn.findById(TEST_TABLE, "x", JsonDocument::class.java).isPresent, "There should have been no document returned" ) } @@ -147,8 +144,8 @@ object Find { ArrayDocument::class.java ) assertEquals(2, docs.size, "There should have been two documents returned") - assertTrue(listOf("first", "second").contains(docs[0].id), "An incorrect document was returned (${docs[0].id}") - assertTrue(listOf("first", "second").contains(docs[1].id), "An incorrect document was returned (${docs[1].id}") + assertTrue(listOf("first", "second").contains(docs[0].id), "An incorrect document was returned (${docs[0].id})") + assertTrue(listOf("first", "second").contains(docs[1].id), "An incorrect document was returned (${docs[1].id})") } fun byFieldsNoMatchInArray(db: ThrowawayDatabase) { @@ -168,8 +165,8 @@ object Find { JsonDocument.load(db) val docs = db.conn.findByContains(TEST_TABLE, mapOf("value" to "purple"), JsonDocument::class.java) assertEquals(2, docs.size, "There should have been 2 documents returned") - assertTrue(listOf("four", "five").contains(docs[0].id), "An incorrect document was returned (${docs[0].id}") - assertTrue(listOf("four", "five").contains(docs[1].id), "An incorrect document was returned (${docs[1].id}") + assertTrue(listOf("four", "five").contains(docs[0].id), "An incorrect document was returned (${docs[0].id})") + assertTrue(listOf("four", "five").contains(docs[1].id), "An incorrect document was returned (${docs[1].id})") } fun byContainsMatchOrdered(db: ThrowawayDatabase) { @@ -197,8 +194,8 @@ object Find { JsonDocument.load(db) val docs = db.conn.findByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)", JsonDocument::class.java) assertEquals(2, docs.size, "There should have been 2 documents returned") - assertTrue(listOf("four", "five").contains(docs[0].id), "An incorrect document was returned (${docs[0].id}") - assertTrue(listOf("four", "five").contains(docs[1].id), "An incorrect document was returned (${docs[1].id}") + assertTrue(listOf("four", "five").contains(docs[0].id), "An incorrect document was returned (${docs[0].id})") + assertTrue(listOf("four", "five").contains(docs[1].id), "An incorrect document was returned (${docs[1].id})") } fun byJsonPathMatchOrdered(db: ThrowawayDatabase) { @@ -226,16 +223,16 @@ object Find { JsonDocument.load(db) val doc = db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("value", "another")), JsonDocument::class.java) - assertNotNull(doc, "There should have been a document returned") - assertEquals("two", doc.id, "The incorrect document was returned") + assertTrue(doc.isPresent, "There should have been a document returned") + assertEquals("two", doc.get().id, "The incorrect document was returned") } fun firstByFieldsMatchMany(db: ThrowawayDatabase) { JsonDocument.load(db) val doc = db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("sub.foo", "green")), JsonDocument::class.java) - assertNotNull(doc, "There should have been a document returned") - assertTrue(listOf("two", "four").contains(doc.id), "An incorrect document was returned (${doc.id}") + assertTrue(doc.isPresent, "There should have been a document returned") + assertTrue(listOf("two", "four").contains(doc.get().id), "An incorrect document was returned (${doc.get().id})") } fun firstByFieldsMatchOrdered(db: ThrowawayDatabase) { @@ -245,14 +242,18 @@ object Find { Field.named("n:numValue DESC") ) ) - assertNotNull(doc, "There should have been a document returned") - assertEquals("four", doc.id, "An incorrect document was returned") + assertTrue(doc.isPresent, "There should have been a document returned") + assertEquals("four", doc.get().id, "An incorrect document was returned") } fun firstByFieldsNoMatch(db: ThrowawayDatabase) { JsonDocument.load(db) - assertNull( - db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("value", "absent")), JsonDocument::class.java), + assertFalse( + db.conn.findFirstByFields( + TEST_TABLE, + listOf(Field.equal("value", "absent")), + JsonDocument::class.java + ).isPresent, "There should have been no document returned" ) } @@ -260,15 +261,18 @@ object Find { fun firstByContainsMatchOne(db: ThrowawayDatabase) { JsonDocument.load(db) val doc = db.conn.findFirstByContains(TEST_TABLE, mapOf("value" to "FIRST!"), JsonDocument::class.java) - assertNotNull(doc, "There should have been a document returned") - assertEquals("one", doc.id, "An incorrect document was returned") + assertTrue(doc.isPresent, "There should have been a document returned") + assertEquals("one", doc.get().id, "An incorrect document was returned") } fun firstByContainsMatchMany(db: ThrowawayDatabase) { JsonDocument.load(db) val doc = db.conn.findFirstByContains(TEST_TABLE, mapOf("value" to "purple"), JsonDocument::class.java) - assertNotNull(doc, "There should have been a document returned") - assertTrue(listOf("four", "five").contains(doc.id), "An incorrect document was returned (${doc.id}") + assertTrue(doc.isPresent, "There should have been a document returned") + assertTrue( + listOf("four", "five").contains(doc.get().id), + "An incorrect document was returned (${doc.get().id})" + ) } fun firstByContainsMatchOrdered(db: ThrowawayDatabase) { @@ -279,14 +283,14 @@ object Find { JsonDocument::class.java, listOf(Field.named("sub.bar NULLS FIRST")) ) - assertNotNull(doc, "There should have been a document returned") - assertEquals("five", doc.id, "An incorrect document was returned") + assertTrue(doc.isPresent, "There should have been a document returned") + assertEquals("five", doc.get().id, "An incorrect document was returned") } fun firstByContainsNoMatch(db: ThrowawayDatabase) { JsonDocument.load(db) - assertNull( - db.conn.findFirstByContains(TEST_TABLE, mapOf("value" to "indigo"), JsonDocument::class.java), + assertFalse( + db.conn.findFirstByContains(TEST_TABLE, mapOf("value" to "indigo"), JsonDocument::class.java).isPresent, "There should have been no document returned" ) } @@ -294,15 +298,18 @@ object Find { fun firstByJsonPathMatchOne(db: ThrowawayDatabase) { JsonDocument.load(db) val doc = db.conn.findFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10)", JsonDocument::class.java) - assertNotNull(doc, "There should have been a document returned") - assertEquals("two", doc.id, "An incorrect document was returned") + assertTrue(doc.isPresent, "There should have been a document returned") + assertEquals("two", doc.get().id, "An incorrect document was returned") } fun firstByJsonPathMatchMany(db: ThrowawayDatabase) { JsonDocument.load(db) val doc = db.conn.findFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)", JsonDocument::class.java) - assertNotNull(doc, "There should have been a document returned") - assertTrue(listOf("four", "five").contains(doc.id), "An incorrect document was returned (${doc.id}") + assertTrue(doc.isPresent, "There should have been a document returned") + assertTrue( + listOf("four", "five").contains(doc.get().id), + "An incorrect document was returned (${doc.get().id})" + ) } fun firstByJsonPathMatchOrdered(db: ThrowawayDatabase) { @@ -313,14 +320,14 @@ object Find { JsonDocument::class.java, listOf(Field.named("id DESC")) ) - assertNotNull(doc, "There should have been a document returned") - assertEquals("four", doc.id, "An incorrect document was returned") + assertTrue(doc.isPresent, "There should have been a document returned") + assertEquals("four", doc.get().id, "An incorrect document was returned") } fun firstByJsonPathNoMatch(db: ThrowawayDatabase) { JsonDocument.load(db) - assertNull( - db.conn.findFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)", JsonDocument::class.java), + assertFalse( + db.conn.findFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)", JsonDocument::class.java).isPresent, "There should have been no document returned" ) } diff --git a/src/jvm/src/test/kotlin/support/JacksonDocumentSerializer.kt b/src/core/src/test/kotlin/integration/JacksonDocumentSerializer.kt similarity index 68% rename from src/jvm/src/test/kotlin/support/JacksonDocumentSerializer.kt rename to src/core/src/test/kotlin/integration/JacksonDocumentSerializer.kt index fb211c2..c3d15db 100644 --- a/src/jvm/src/test/kotlin/support/JacksonDocumentSerializer.kt +++ b/src/core/src/test/kotlin/integration/JacksonDocumentSerializer.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.support +package solutions.bitbadger.documents.core.tests.integration import solutions.bitbadger.documents.DocumentSerializer import com.fasterxml.jackson.databind.ObjectMapper @@ -8,11 +8,11 @@ import com.fasterxml.jackson.databind.ObjectMapper */ class JacksonDocumentSerializer : DocumentSerializer { - val mapper = ObjectMapper() + private val mapper = ObjectMapper() - override fun serialize(document: TDoc) = + override fun serialize(document: TDoc): String = mapper.writeValueAsString(document) - override fun deserialize(json: String, clazz: Class) = + override fun deserialize(json: String, clazz: Class): TDoc = mapper.readValue(json, clazz) } diff --git a/src/core/src/test/kotlin/integration/PatchFunctions.kt b/src/core/src/test/kotlin/integration/PatchFunctions.kt new file mode 100644 index 0000000..6f98006 --- /dev/null +++ b/src/core/src/test/kotlin/integration/PatchFunctions.kt @@ -0,0 +1,88 @@ +package solutions.bitbadger.documents.core.tests.integration + +import solutions.bitbadger.documents.Field +import solutions.bitbadger.documents.core.tests.* +import solutions.bitbadger.documents.java.extensions.* +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/** + * Integration tests for the `Patch` object + */ +object PatchFunctions { + + fun byIdMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + db.conn.patchById(TEST_TABLE, "one", mapOf("numValue" to 44)) + val doc = db.conn.findById(TEST_TABLE, "one", JsonDocument::class.java) + assertTrue(doc.isPresent, "There should have been a document returned") + assertEquals("one", doc.get().id, "An incorrect document was returned") + assertEquals(44, doc.get().numValue, "The document was not patched") + } + + fun byIdNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse(db.conn.existsById(TEST_TABLE, "forty-seven"), "Document with ID \"forty-seven\" should not exist") + db.conn.patchById(TEST_TABLE, "forty-seven", mapOf("foo" to "green")) // no exception = pass + } + + fun byFieldsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + db.conn.patchByFields(TEST_TABLE, listOf(Field.equal("value", "purple")), mapOf("numValue" to 77)) + assertEquals( + 2, + db.conn.countByFields(TEST_TABLE, listOf(Field.equal("numValue", 77))), + "There should have been 2 documents with numeric value 77" + ) + } + + fun byFieldsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val fields = listOf(Field.equal("value", "burgundy")) + assertFalse( + db.conn.existsByFields(TEST_TABLE, fields), + "There should be no documents with value of \"burgundy\"" + ) + db.conn.patchByFields(TEST_TABLE, fields, mapOf("foo" to "green")) // no exception = pass + } + + fun byContainsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val contains = mapOf("value" to "another") + db.conn.patchByContains(TEST_TABLE, contains, mapOf("numValue" to 12)) + val doc = db.conn.findFirstByContains(TEST_TABLE, contains, JsonDocument::class.java) + assertTrue(doc.isPresent, "There should have been a document returned") + assertEquals("two", doc.get().id, "The incorrect document was returned") + assertEquals(12, doc.get().numValue, "The document was not updated") + } + + fun byContainsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val contains = mapOf("value" to "updated") + assertFalse(db.conn.existsByContains(TEST_TABLE, contains), "There should be no matching documents") + db.conn.patchByContains(TEST_TABLE, contains, mapOf("sub.foo" to "green")) // no exception = pass + } + + fun byJsonPathMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val path = "$.numValue ? (@ > 10)" + db.conn.patchByJsonPath(TEST_TABLE, path, mapOf("value" to "blue")) + val docs = db.conn.findByJsonPath(TEST_TABLE, path, JsonDocument::class.java) + assertEquals(2, docs.size, "There should have been two documents returned") + docs.forEach { + assertTrue(listOf("four", "five").contains(it.id), "An incorrect document was returned (${it.id})") + assertEquals("blue", it.value, "The value for ID ${it.id} was incorrect") + } + } + + fun byJsonPathNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val path = "$.numValue ? (@ > 100)" + assertFalse( + db.conn.existsByJsonPath(TEST_TABLE, path), + "There should be no documents with numeric values over 100" + ) + db.conn.patchByJsonPath(TEST_TABLE, path, mapOf("value" to "blue")) // no exception = pass + } +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/postgresql/PgDB.kt b/src/core/src/test/kotlin/integration/PgDB.kt similarity index 82% rename from src/jvm/src/test/kotlin/jvm/integration/postgresql/PgDB.kt rename to src/core/src/test/kotlin/integration/PgDB.kt index 6f60f71..4586d5f 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/postgresql/PgDB.kt +++ b/src/core/src/test/kotlin/integration/PgDB.kt @@ -1,11 +1,10 @@ -package solutions.bitbadger.documents.jvm.integration.postgresql +package solutions.bitbadger.documents.core.tests.integration import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.extensions.customNonQuery -import solutions.bitbadger.documents.extensions.customScalar -import solutions.bitbadger.documents.extensions.ensureTable -import solutions.bitbadger.documents.jvm.* -import solutions.bitbadger.documents.support.* +import solutions.bitbadger.documents.core.tests.TEST_TABLE +import solutions.bitbadger.documents.java.DocumentConfig +import solutions.bitbadger.documents.java.Results +import solutions.bitbadger.documents.java.extensions.* /** * A wrapper for a throwaway PostgreSQL database diff --git a/src/core/src/test/kotlin/integration/PostgreSQLCountIT.kt b/src/core/src/test/kotlin/integration/PostgreSQLCountIT.kt new file mode 100644 index 0000000..4fa7fb6 --- /dev/null +++ b/src/core/src/test/kotlin/integration/PostgreSQLCountIT.kt @@ -0,0 +1,46 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import kotlin.test.Test + +/** + * PostgreSQL integration tests for the `Count` object / `count*` connection extension functions + */ +@DisplayName("Java | Kotlin | PostgreSQL: Count") +class PostgreSQLCountIT { + + @Test + @DisplayName("all counts all documents") + fun all() = + PgDB().use(CountFunctions::all) + + @Test + @DisplayName("byFields counts documents by a numeric value") + fun byFieldsNumeric() = + PgDB().use(CountFunctions::byFieldsNumeric) + + @Test + @DisplayName("byFields counts documents by a alphanumeric value") + fun byFieldsAlpha() = + PgDB().use(CountFunctions::byFieldsAlpha) + + @Test + @DisplayName("byContains counts documents when matches are found") + fun byContainsMatch() = + PgDB().use(CountFunctions::byContainsMatch) + + @Test + @DisplayName("byContains counts documents when no matches are found") + fun byContainsNoMatch() = + PgDB().use(CountFunctions::byContainsNoMatch) + + @Test + @DisplayName("byJsonPath counts documents when matches are found") + fun byJsonPathMatch() = + PgDB().use(CountFunctions::byJsonPathMatch) + + @Test + @DisplayName("byJsonPath counts documents when no matches are found") + fun byJsonPathNoMatch() = + PgDB().use(CountFunctions::byJsonPathNoMatch) +} diff --git a/src/core/src/test/kotlin/integration/PostgreSQLCustomIT.kt b/src/core/src/test/kotlin/integration/PostgreSQLCustomIT.kt new file mode 100644 index 0000000..8e943db --- /dev/null +++ b/src/core/src/test/kotlin/integration/PostgreSQLCustomIT.kt @@ -0,0 +1,47 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName + +import kotlin.test.Test + +/** + * PostgreSQL integration tests for the `Custom` object / `custom*` connection extension functions + */ +@DisplayName("Core | Kotlin | PostgreSQL: Custom") +class PostgreSQLCustomIT { + + @Test + @DisplayName("list succeeds with empty list") + fun listEmpty() = + PgDB().use(CustomFunctions::listEmpty) + + @Test + @DisplayName("list succeeds with a non-empty list") + fun listAll() = + PgDB().use(CustomFunctions::listAll) + + @Test + @DisplayName("single succeeds when document not found") + fun singleNone() = + PgDB().use(CustomFunctions::singleNone) + + @Test + @DisplayName("single succeeds when a document is found") + fun singleOne() = + PgDB().use(CustomFunctions::singleOne) + + @Test + @DisplayName("nonQuery makes changes") + fun nonQueryChanges() = + PgDB().use(CustomFunctions::nonQueryChanges) + + @Test + @DisplayName("nonQuery makes no changes when where clause matches nothing") + fun nonQueryNoChanges() = + PgDB().use(CustomFunctions::nonQueryNoChanges) + + @Test + @DisplayName("scalar succeeds") + fun scalar() = + PgDB().use(CustomFunctions::scalar) +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/postgresql/DefinitionIT.kt b/src/core/src/test/kotlin/integration/PostgreSQLDefinitionIT.kt similarity index 58% rename from src/jvm/src/test/kotlin/jvm/integration/postgresql/DefinitionIT.kt rename to src/core/src/test/kotlin/integration/PostgreSQLDefinitionIT.kt index bb01c6c..e9f71ec 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/postgresql/DefinitionIT.kt +++ b/src/core/src/test/kotlin/integration/PostgreSQLDefinitionIT.kt @@ -1,32 +1,31 @@ -package solutions.bitbadger.documents.jvm.integration.postgresql +package solutions.bitbadger.documents.core.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.Definition import kotlin.test.Test /** * PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions */ -@DisplayName("JVM | Kotlin | PostgreSQL: Definition") -class DefinitionIT { +@DisplayName("Core | Kotlin | PostgreSQL: Definition") +class PostgreSQLDefinitionIT { @Test @DisplayName("ensureTable creates table and index") fun ensureTable() = - PgDB().use(Definition::ensureTable) + PgDB().use(DefinitionFunctions::ensureTable) @Test @DisplayName("ensureFieldIndex creates an index") fun ensureFieldIndex() = - PgDB().use(Definition::ensureFieldIndex) + PgDB().use(DefinitionFunctions::ensureFieldIndex) @Test @DisplayName("ensureDocumentIndex creates a full index") fun ensureDocumentIndexFull() = - PgDB().use(Definition::ensureDocumentIndexFull) + PgDB().use(DefinitionFunctions::ensureDocumentIndexFull) @Test @DisplayName("ensureDocumentIndex creates an optimized index") fun ensureDocumentIndexOptimized() = - PgDB().use(Definition::ensureDocumentIndexOptimized) + PgDB().use(DefinitionFunctions::ensureDocumentIndexOptimized) } diff --git a/src/core/src/test/kotlin/integration/PostgreSQLDeleteIT.kt b/src/core/src/test/kotlin/integration/PostgreSQLDeleteIT.kt new file mode 100644 index 0000000..6e3e57c --- /dev/null +++ b/src/core/src/test/kotlin/integration/PostgreSQLDeleteIT.kt @@ -0,0 +1,51 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import kotlin.test.Test + +/** + * PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions + */ +@DisplayName("Core | Kotlin | PostgreSQL: Delete") +class PostgreSQLDeleteIT { + + @Test + @DisplayName("byId deletes a matching ID") + fun byIdMatch() = + PgDB().use(DeleteFunctions::byIdMatch) + + @Test + @DisplayName("byId succeeds when no ID matches") + fun byIdNoMatch() = + PgDB().use(DeleteFunctions::byIdNoMatch) + + @Test + @DisplayName("byFields deletes matching documents") + fun byFieldsMatch() = + PgDB().use(DeleteFunctions::byFieldsMatch) + + @Test + @DisplayName("byFields succeeds when no documents match") + fun byFieldsNoMatch() = + PgDB().use(DeleteFunctions::byFieldsNoMatch) + + @Test + @DisplayName("byContains deletes matching documents") + fun byContainsMatch() = + PgDB().use(DeleteFunctions::byContainsMatch) + + @Test + @DisplayName("byContains succeeds when no documents match") + fun byContainsNoMatch() = + PgDB().use(DeleteFunctions::byContainsNoMatch) + + @Test + @DisplayName("byJsonPath deletes matching documents") + fun byJsonPathMatch() = + PgDB().use(DeleteFunctions::byJsonPathMatch) + + @Test + @DisplayName("byJsonPath succeeds when no documents match") + fun byJsonPathNoMatch() = + PgDB().use(DeleteFunctions::byJsonPathNoMatch) +} diff --git a/src/core/src/test/kotlin/integration/PostgreSQLDocumentIT.kt b/src/core/src/test/kotlin/integration/PostgreSQLDocumentIT.kt new file mode 100644 index 0000000..9696573 --- /dev/null +++ b/src/core/src/test/kotlin/integration/PostgreSQLDocumentIT.kt @@ -0,0 +1,56 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import kotlin.test.Test + +/** + * PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions + */ +@DisplayName("Core | Kotlin | PostgreSQL: Document") +class PostgreSQLDocumentIT { + + @Test + @DisplayName("insert works with default values") + fun insertDefault() = + PgDB().use(DocumentFunctions::insertDefault) + + @Test + @DisplayName("insert fails with duplicate key") + fun insertDupe() = + PgDB().use(DocumentFunctions::insertDupe) + + @Test + @DisplayName("insert succeeds with numeric auto IDs") + fun insertNumAutoId() = + PgDB().use(DocumentFunctions::insertNumAutoId) + + @Test + @DisplayName("insert succeeds with UUID auto ID") + fun insertUUIDAutoId() = + PgDB().use(DocumentFunctions::insertUUIDAutoId) + + @Test + @DisplayName("insert succeeds with random string auto ID") + fun insertStringAutoId() = + PgDB().use(DocumentFunctions::insertStringAutoId) + + @Test + @DisplayName("save updates an existing document") + fun saveMatch() = + PgDB().use(DocumentFunctions::saveMatch) + + @Test + @DisplayName("save inserts a new document") + fun saveNoMatch() = + PgDB().use(DocumentFunctions::saveNoMatch) + + @Test + @DisplayName("update replaces an existing document") + fun updateMatch() = + PgDB().use(DocumentFunctions::updateMatch) + + @Test + @DisplayName("update succeeds when no document exists") + fun updateNoMatch() = + PgDB().use(DocumentFunctions::updateNoMatch) +} diff --git a/src/core/src/test/kotlin/integration/PostgreSQLExistsIT.kt b/src/core/src/test/kotlin/integration/PostgreSQLExistsIT.kt new file mode 100644 index 0000000..f497f64 --- /dev/null +++ b/src/core/src/test/kotlin/integration/PostgreSQLExistsIT.kt @@ -0,0 +1,51 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import kotlin.test.Test + +/** + * PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions + */ +@DisplayName("Core | Kotlin | PostgreSQL: Exists") +class PostgreSQLExistsIT { + + @Test + @DisplayName("byId returns true when a document matches the ID") + fun byIdMatch() = + PgDB().use(ExistsFunctions::byIdMatch) + + @Test + @DisplayName("byId returns false when no document matches the ID") + fun byIdNoMatch() = + PgDB().use(ExistsFunctions::byIdNoMatch) + + @Test + @DisplayName("byFields returns true when documents match") + fun byFieldsMatch() = + PgDB().use(ExistsFunctions::byFieldsMatch) + + @Test + @DisplayName("byFields returns false when no documents match") + fun byFieldsNoMatch() = + PgDB().use(ExistsFunctions::byFieldsNoMatch) + + @Test + @DisplayName("byContains returns true when documents match") + fun byContainsMatch() = + PgDB().use(ExistsFunctions::byContainsMatch) + + @Test + @DisplayName("byContains returns false when no documents match") + fun byContainsNoMatch() = + PgDB().use(ExistsFunctions::byContainsNoMatch) + + @Test + @DisplayName("byJsonPath returns true when documents match") + fun byJsonPathMatch() = + PgDB().use(ExistsFunctions::byJsonPathMatch) + + @Test + @DisplayName("byJsonPath returns false when no documents match") + fun byJsonPathNoMatch() = + PgDB().use(ExistsFunctions::byJsonPathNoMatch) +} diff --git a/src/core/src/test/kotlin/integration/PostgreSQLFindIT.kt b/src/core/src/test/kotlin/integration/PostgreSQLFindIT.kt new file mode 100644 index 0000000..0c84aa0 --- /dev/null +++ b/src/core/src/test/kotlin/integration/PostgreSQLFindIT.kt @@ -0,0 +1,171 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import kotlin.test.Test + +/** + * PostgreSQL integration tests for the `Find` object / `find*` connection extension functions + */ +@DisplayName("Core | Kotlin | PostgreSQL: Find") +class PostgreSQLFindIT { + + @Test + @DisplayName("all retrieves all documents") + fun allDefault() = + PgDB().use(FindFunctions::allDefault) + + @Test + @DisplayName("all sorts data ascending") + fun allAscending() = + PgDB().use(FindFunctions::allAscending) + + @Test + @DisplayName("all sorts data descending") + fun allDescending() = + PgDB().use(FindFunctions::allDescending) + + @Test + @DisplayName("all sorts data numerically") + fun allNumOrder() = + PgDB().use(FindFunctions::allNumOrder) + + @Test + @DisplayName("all succeeds with an empty table") + fun allEmpty() = + PgDB().use(FindFunctions::allEmpty) + + @Test + @DisplayName("byId retrieves a document via a string ID") + fun byIdString() = + PgDB().use(FindFunctions::byIdString) + + @Test + @DisplayName("byId retrieves a document via a numeric ID") + fun byIdNumber() = + PgDB().use(FindFunctions::byIdNumber) + + @Test + @DisplayName("byId returns null when a matching ID is not found") + fun byIdNotFound() = + PgDB().use(FindFunctions::byIdNotFound) + + @Test + @DisplayName("byFields retrieves matching documents") + fun byFieldsMatch() = + PgDB().use(FindFunctions::byFieldsMatch) + + @Test + @DisplayName("byFields retrieves ordered matching documents") + fun byFieldsMatchOrdered() = + PgDB().use(FindFunctions::byFieldsMatchOrdered) + + @Test + @DisplayName("byFields retrieves matching documents with a numeric IN clause") + fun byFieldsMatchNumIn() = + PgDB().use(FindFunctions::byFieldsMatchNumIn) + + @Test + @DisplayName("byFields succeeds when no documents match") + fun byFieldsNoMatch() = + PgDB().use(FindFunctions::byFieldsNoMatch) + + @Test + @DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison") + fun byFieldsMatchInArray() = + PgDB().use(FindFunctions::byFieldsMatchInArray) + + @Test + @DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison") + fun byFieldsNoMatchInArray() = + PgDB().use(FindFunctions::byFieldsNoMatchInArray) + + @Test + @DisplayName("byContains retrieves matching documents") + fun byContainsMatch() = + PgDB().use(FindFunctions::byContainsMatch) + + @Test + @DisplayName("byContains retrieves ordered matching documents") + fun byContainsMatchOrdered() = + PgDB().use(FindFunctions::byContainsMatchOrdered) + + @Test + @DisplayName("byContains succeeds when no documents match") + fun byContainsNoMatch() = + PgDB().use(FindFunctions::byContainsNoMatch) + + @Test + @DisplayName("byJsonPath retrieves matching documents") + fun byJsonPathMatch() = + PgDB().use(FindFunctions::byJsonPathMatch) + + @Test + @DisplayName("byJsonPath retrieves ordered matching documents") + fun byJsonPathMatchOrdered() = + PgDB().use(FindFunctions::byJsonPathMatchOrdered) + + @Test + @DisplayName("byJsonPath succeeds when no documents match") + fun byJsonPathNoMatch() = + PgDB().use(FindFunctions::byJsonPathNoMatch) + + @Test + @DisplayName("firstByFields retrieves a matching document") + fun firstByFieldsMatchOne() = + PgDB().use(FindFunctions::firstByFieldsMatchOne) + + @Test + @DisplayName("firstByFields retrieves a matching document among many") + fun firstByFieldsMatchMany() = + PgDB().use(FindFunctions::firstByFieldsMatchMany) + + @Test + @DisplayName("firstByFields retrieves a matching document among many (ordered)") + fun firstByFieldsMatchOrdered() = + PgDB().use(FindFunctions::firstByFieldsMatchOrdered) + + @Test + @DisplayName("firstByFields returns null when no document matches") + fun firstByFieldsNoMatch() = + PgDB().use(FindFunctions::firstByFieldsNoMatch) + + @Test + @DisplayName("firstByContains retrieves a matching document") + fun firstByContainsMatchOne() = + PgDB().use(FindFunctions::firstByContainsMatchOne) + + @Test + @DisplayName("firstByContains retrieves a matching document among many") + fun firstByContainsMatchMany() = + PgDB().use(FindFunctions::firstByContainsMatchMany) + + @Test + @DisplayName("firstByContains retrieves a matching document among many (ordered)") + fun firstByContainsMatchOrdered() = + PgDB().use(FindFunctions::firstByContainsMatchOrdered) + + @Test + @DisplayName("firstByContains returns null when no document matches") + fun firstByContainsNoMatch() = + PgDB().use(FindFunctions::firstByContainsNoMatch) + + @Test + @DisplayName("firstByJsonPath retrieves a matching document") + fun firstByJsonPathMatchOne() = + PgDB().use(FindFunctions::firstByJsonPathMatchOne) + + @Test + @DisplayName("firstByJsonPath retrieves a matching document among many") + fun firstByJsonPathMatchMany() = + PgDB().use(FindFunctions::firstByJsonPathMatchMany) + + @Test + @DisplayName("firstByJsonPath retrieves a matching document among many (ordered)") + fun firstByJsonPathMatchOrdered() = + PgDB().use(FindFunctions::firstByJsonPathMatchOrdered) + + @Test + @DisplayName("firstByJsonPath returns null when no document matches") + fun firstByJsonPathNoMatch() = + PgDB().use(FindFunctions::firstByJsonPathNoMatch) +} diff --git a/src/core/src/test/kotlin/integration/PostgreSQLPatchIT.kt b/src/core/src/test/kotlin/integration/PostgreSQLPatchIT.kt new file mode 100644 index 0000000..2455b57 --- /dev/null +++ b/src/core/src/test/kotlin/integration/PostgreSQLPatchIT.kt @@ -0,0 +1,51 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import kotlin.test.Test + +/** + * PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions + */ +@DisplayName("Core | Kotlin | PostgreSQL: Patch") +class PostgreSQLPatchIT { + + @Test + @DisplayName("byId patches an existing document") + fun byIdMatch() = + PgDB().use(PatchFunctions::byIdMatch) + + @Test + @DisplayName("byId succeeds for a non-existent document") + fun byIdNoMatch() = + PgDB().use(PatchFunctions::byIdNoMatch) + + @Test + @DisplayName("byFields patches matching document") + fun byFieldsMatch() = + PgDB().use(PatchFunctions::byFieldsMatch) + + @Test + @DisplayName("byFields succeeds when no documents match") + fun byFieldsNoMatch() = + PgDB().use(PatchFunctions::byFieldsNoMatch) + + @Test + @DisplayName("byContains patches matching document") + fun byContainsMatch() = + PgDB().use(PatchFunctions::byContainsMatch) + + @Test + @DisplayName("byContains succeeds when no documents match") + fun byContainsNoMatch() = + PgDB().use(PatchFunctions::byContainsNoMatch) + + @Test + @DisplayName("byJsonPath patches matching document") + fun byJsonPathMatch() = + PgDB().use(PatchFunctions::byJsonPathMatch) + + @Test + @DisplayName("byJsonPath succeeds when no documents match") + fun byJsonPathNoMatch() = + PgDB().use(PatchFunctions::byJsonPathNoMatch) +} diff --git a/src/core/src/test/kotlin/integration/PostgreSQLRemoveFieldsIT.kt b/src/core/src/test/kotlin/integration/PostgreSQLRemoveFieldsIT.kt new file mode 100644 index 0000000..9a2e2d1 --- /dev/null +++ b/src/core/src/test/kotlin/integration/PostgreSQLRemoveFieldsIT.kt @@ -0,0 +1,71 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import kotlin.test.Test + +/** + * PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions + */ +@DisplayName("Core | Kotlin | PostgreSQL: RemoveFields") +class PostgreSQLRemoveFieldsIT { + + @Test + @DisplayName("byId removes fields from an existing document") + fun byIdMatchFields() = + PgDB().use(RemoveFieldsFunctions::byIdMatchFields) + + @Test + @DisplayName("byId succeeds when fields do not exist on an existing document") + fun byIdMatchNoFields() = + PgDB().use(RemoveFieldsFunctions::byIdMatchNoFields) + + @Test + @DisplayName("byId succeeds when no document exists") + fun byIdNoMatch() = + PgDB().use(RemoveFieldsFunctions::byIdNoMatch) + + @Test + @DisplayName("byFields removes fields from matching documents") + fun byFieldsMatchFields() = + PgDB().use(RemoveFieldsFunctions::byFieldsMatchFields) + + @Test + @DisplayName("byFields succeeds when fields do not exist on matching documents") + fun byFieldsMatchNoFields() = + PgDB().use(RemoveFieldsFunctions::byFieldsMatchNoFields) + + @Test + @DisplayName("byFields succeeds when no matching documents exist") + fun byFieldsNoMatch() = + PgDB().use(RemoveFieldsFunctions::byFieldsNoMatch) + + @Test + @DisplayName("byContains removes fields from matching documents") + fun byContainsMatchFields() = + PgDB().use(RemoveFieldsFunctions::byContainsMatchFields) + + @Test + @DisplayName("byContains succeeds when fields do not exist on matching documents") + fun byContainsMatchNoFields() = + PgDB().use(RemoveFieldsFunctions::byContainsMatchNoFields) + + @Test + @DisplayName("byContains succeeds when no matching documents exist") + fun byContainsNoMatch() = + PgDB().use(RemoveFieldsFunctions::byContainsNoMatch) + + @Test + @DisplayName("byJsonPath removes fields from matching documents") + fun byJsonPathMatchFields() = + PgDB().use(RemoveFieldsFunctions::byJsonPathMatchFields) + + @Test + @DisplayName("byJsonPath succeeds when fields do not exist on matching documents") + fun byJsonPathMatchNoFields() = + PgDB().use(RemoveFieldsFunctions::byJsonPathMatchNoFields) + + @Test + @DisplayName("byJsonPath succeeds when no matching documents exist") + fun byJsonPathNoMatch() = + PgDB().use(RemoveFieldsFunctions::byJsonPathNoMatch) +} diff --git a/src/core/src/test/kotlin/integration/RemoveFieldsFunctions.kt b/src/core/src/test/kotlin/integration/RemoveFieldsFunctions.kt new file mode 100644 index 0000000..2096c08 --- /dev/null +++ b/src/core/src/test/kotlin/integration/RemoveFieldsFunctions.kt @@ -0,0 +1,107 @@ +package solutions.bitbadger.documents.core.tests.integration + +import solutions.bitbadger.documents.Field +import solutions.bitbadger.documents.core.tests.* +import solutions.bitbadger.documents.java.extensions.* +import kotlin.test.* + +/** + * Integration tests for the `RemoveFields` object + */ +object RemoveFieldsFunctions { + + fun byIdMatchFields(db: ThrowawayDatabase) { + JsonDocument.load(db) + db.conn.removeFieldsById(TEST_TABLE, "two", listOf("sub", "value")) + val doc = db.conn.findById(TEST_TABLE, "two", JsonDocument::class.java) + assertTrue(doc.isPresent, "There should have been a document returned") + assertEquals("", doc.get().value, "The value should have been empty") + assertNull(doc.get().sub, "The sub-document should have been removed") + } + + fun byIdMatchNoFields(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse(db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("a_field_that_does_not_exist")))) + db.conn.removeFieldsById(TEST_TABLE, "one", listOf("a_field_that_does_not_exist")) // no exception = pass + } + + fun byIdNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse(db.conn.existsById(TEST_TABLE, "fifty")) + db.conn.removeFieldsById(TEST_TABLE, "fifty", listOf("sub")) // no exception = pass + } + + fun byFieldsMatchFields(db: ThrowawayDatabase) { + JsonDocument.load(db) + val fields = listOf(Field.equal("numValue", 17)) + db.conn.removeFieldsByFields(TEST_TABLE, fields, listOf("sub")) + val doc = db.conn.findFirstByFields(TEST_TABLE, fields, JsonDocument::class.java) + assertTrue(doc.isPresent, "The document should have been returned") + assertEquals("four", doc.get().id, "An incorrect document was returned") + assertNull(doc.get().sub, "The sub-document should have been removed") + } + + fun byFieldsMatchNoFields(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse(db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("nada")))) + db.conn.removeFieldsByFields(TEST_TABLE, listOf(Field.equal("numValue", 17)), listOf("nada")) // no exn = pass + } + + fun byFieldsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val fields = listOf(Field.notEqual("missing", "nope")) + assertFalse(db.conn.existsByFields(TEST_TABLE, fields)) + db.conn.removeFieldsByFields(TEST_TABLE, fields, listOf("value")) // no exception = pass + } + + fun byContainsMatchFields(db: ThrowawayDatabase) { + JsonDocument.load(db) + val criteria = mapOf("sub" to mapOf("foo" to "green")) + db.conn.removeFieldsByContains(TEST_TABLE, criteria, listOf("value")) + val docs = db.conn.findByContains(TEST_TABLE, criteria, JsonDocument::class.java) + assertEquals(2, docs.size, "There should have been 2 documents returned") + docs.forEach { + assertTrue(listOf("two", "four").contains(it.id), "An incorrect document was returned (${it.id})") + assertEquals("", it.value, "The value should have been empty") + } + } + + fun byContainsMatchNoFields(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse(db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("invalid_field")))) + db.conn.removeFieldsByContains(TEST_TABLE, mapOf("sub" to mapOf("foo" to "green")), listOf("invalid_field")) + // no exception = pass + } + + fun byContainsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val contains = mapOf("value" to "substantial") + assertFalse(db.conn.existsByContains(TEST_TABLE, contains)) + db.conn.removeFieldsByContains(TEST_TABLE, contains, listOf("numValue")) + } + + fun byJsonPathMatchFields(db: ThrowawayDatabase) { + JsonDocument.load(db) + val path = "$.value ? (@ == \"purple\")" + db.conn.removeFieldsByJsonPath(TEST_TABLE, path, listOf("sub")) + val docs = db.conn.findByJsonPath(TEST_TABLE, path, JsonDocument::class.java) + assertEquals(2, docs.size, "There should have been 2 documents returned") + docs.forEach { + assertTrue(listOf("four", "five").contains(it.id), "An incorrect document was returned (${it.id})") + assertNull(it.sub, "The sub-document should have been removed") + } + } + + fun byJsonPathMatchNoFields(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse(db.conn.existsByFields(TEST_TABLE, listOf(Field.exists("submarine")))) + db.conn.removeFieldsByJsonPath(TEST_TABLE, "$.value ? (@ == \"purple\")", listOf("submarine")) // no exn = pass + } + + fun byJsonPathNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val path = "$.value ? (@ == \"mauve\")" + assertFalse(db.conn.existsByJsonPath(TEST_TABLE, path)) + db.conn.removeFieldsByJsonPath(TEST_TABLE, path, listOf("value")) // no exception = pass + } +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/sqlite/CountIT.kt b/src/core/src/test/kotlin/integration/SQLiteCountIT.kt similarity index 59% rename from src/jvm/src/test/kotlin/jvm/integration/sqlite/CountIT.kt rename to src/core/src/test/kotlin/integration/SQLiteCountIT.kt index 566cb4b..7e2aece 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/sqlite/CountIT.kt +++ b/src/core/src/test/kotlin/integration/SQLiteCountIT.kt @@ -1,41 +1,40 @@ -package solutions.bitbadger.documents.jvm.integration.sqlite +package solutions.bitbadger.documents.core.tests.integration import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException -import solutions.bitbadger.documents.jvm.integration.common.Count import kotlin.test.Test /** * SQLite integration tests for the `Count` object / `count*` connection extension functions */ -@DisplayName("JVM | Kotlin | SQLite: Count") -class CountIT { +@DisplayName("Java | Kotlin | SQLite: Count") +class SQLiteCountIT { @Test @DisplayName("all counts all documents") fun all() = - SQLiteDB().use(Count::all) + SQLiteDB().use(CountFunctions::all) @Test @DisplayName("byFields counts documents by a numeric value") fun byFieldsNumeric() = - SQLiteDB().use(Count::byFieldsNumeric) + SQLiteDB().use(CountFunctions::byFieldsNumeric) @Test @DisplayName("byFields counts documents by a alphanumeric value") fun byFieldsAlpha() = - SQLiteDB().use(Count::byFieldsAlpha) + SQLiteDB().use(CountFunctions::byFieldsAlpha) @Test @DisplayName("byContains fails") fun byContainsMatch() { - assertThrows { SQLiteDB().use(Count::byContainsMatch) } + assertThrows { SQLiteDB().use(CountFunctions::byContainsMatch) } } @Test @DisplayName("byJsonPath fails") fun byJsonPathMatch() { - assertThrows { SQLiteDB().use(Count::byJsonPathMatch) } + assertThrows { SQLiteDB().use(CountFunctions::byJsonPathMatch) } } } diff --git a/src/core/src/test/kotlin/integration/SQLiteCustomIT.kt b/src/core/src/test/kotlin/integration/SQLiteCustomIT.kt new file mode 100644 index 0000000..470b040 --- /dev/null +++ b/src/core/src/test/kotlin/integration/SQLiteCustomIT.kt @@ -0,0 +1,46 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import kotlin.test.Test + +/** + * SQLite integration tests for the `Custom` object / `custom*` connection extension functions + */ +@DisplayName("Core | Kotlin | SQLite: Custom") +class SQLiteCustomIT { + + @Test + @DisplayName("list succeeds with empty list") + fun listEmpty() = + SQLiteDB().use(CustomFunctions::listEmpty) + + @Test + @DisplayName("list succeeds with a non-empty list") + fun listAll() = + SQLiteDB().use(CustomFunctions::listAll) + + @Test + @DisplayName("single succeeds when document not found") + fun singleNone() = + SQLiteDB().use(CustomFunctions::singleNone) + + @Test + @DisplayName("single succeeds when a document is found") + fun singleOne() = + SQLiteDB().use(CustomFunctions::singleOne) + + @Test + @DisplayName("nonQuery makes changes") + fun nonQueryChanges() = + SQLiteDB().use(CustomFunctions::nonQueryChanges) + + @Test + @DisplayName("nonQuery makes no changes when where clause matches nothing") + fun nonQueryNoChanges() = + SQLiteDB().use(CustomFunctions::nonQueryNoChanges) + + @Test + @DisplayName("scalar succeeds") + fun scalar() = + SQLiteDB().use(CustomFunctions::scalar) +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/sqlite/SQLiteDB.kt b/src/core/src/test/kotlin/integration/SQLiteDB.kt similarity index 74% rename from src/jvm/src/test/kotlin/jvm/integration/sqlite/SQLiteDB.kt rename to src/core/src/test/kotlin/integration/SQLiteDB.kt index 609124e..1c930a0 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/sqlite/SQLiteDB.kt +++ b/src/core/src/test/kotlin/integration/SQLiteDB.kt @@ -1,10 +1,10 @@ -package solutions.bitbadger.documents.jvm.integration.sqlite +package solutions.bitbadger.documents.core.tests.integration import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.extensions.customScalar -import solutions.bitbadger.documents.extensions.ensureTable -import solutions.bitbadger.documents.jvm.* -import solutions.bitbadger.documents.support.* +import solutions.bitbadger.documents.core.tests.TEST_TABLE +import solutions.bitbadger.documents.java.DocumentConfig +import solutions.bitbadger.documents.java.Results +import solutions.bitbadger.documents.java.extensions.* import java.io.File /** diff --git a/src/jvm/src/test/kotlin/jvm/integration/sqlite/DefinitionIT.kt b/src/core/src/test/kotlin/integration/SQLiteDefinitionIT.kt similarity index 68% rename from src/jvm/src/test/kotlin/jvm/integration/sqlite/DefinitionIT.kt rename to src/core/src/test/kotlin/integration/SQLiteDefinitionIT.kt index bc47dc0..9f0ff14 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/sqlite/DefinitionIT.kt +++ b/src/core/src/test/kotlin/integration/SQLiteDefinitionIT.kt @@ -1,36 +1,35 @@ -package solutions.bitbadger.documents.jvm.integration.sqlite +package solutions.bitbadger.documents.core.tests.integration import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException -import solutions.bitbadger.documents.jvm.integration.common.Definition import kotlin.test.Test /** * SQLite integration tests for the `Definition` object / `ensure*` connection extension functions */ -@DisplayName("JVM | Kotlin | SQLite: Definition") -class DefinitionIT { +@DisplayName("Core | Kotlin | SQLite: Definition") +class SQLiteDefinitionIT { @Test @DisplayName("ensureTable creates table and index") fun ensureTable() = - SQLiteDB().use(Definition::ensureTable) + SQLiteDB().use(DefinitionFunctions::ensureTable) @Test @DisplayName("ensureFieldIndex creates an index") fun ensureFieldIndex() = - SQLiteDB().use(Definition::ensureFieldIndex) + SQLiteDB().use(DefinitionFunctions::ensureFieldIndex) @Test @DisplayName("ensureDocumentIndex fails for full index") fun ensureDocumentIndexFull() { - assertThrows { SQLiteDB().use(Definition::ensureDocumentIndexFull) } + assertThrows { SQLiteDB().use(DefinitionFunctions::ensureDocumentIndexFull) } } @Test @DisplayName("ensureDocumentIndex fails for optimized index") fun ensureDocumentIndexOptimized() { - assertThrows { SQLiteDB().use(Definition::ensureDocumentIndexOptimized) } + assertThrows { SQLiteDB().use(DefinitionFunctions::ensureDocumentIndexOptimized) } } } diff --git a/src/jvm/src/test/kotlin/jvm/integration/sqlite/DeleteIT.kt b/src/core/src/test/kotlin/integration/SQLiteDeleteIT.kt similarity index 59% rename from src/jvm/src/test/kotlin/jvm/integration/sqlite/DeleteIT.kt rename to src/core/src/test/kotlin/integration/SQLiteDeleteIT.kt index bbdda99..fa1acad 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/sqlite/DeleteIT.kt +++ b/src/core/src/test/kotlin/integration/SQLiteDeleteIT.kt @@ -1,46 +1,45 @@ -package solutions.bitbadger.documents.jvm.integration.sqlite +package solutions.bitbadger.documents.core.tests.integration import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException -import solutions.bitbadger.documents.jvm.integration.common.Delete import kotlin.test.Test /** * SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions */ -@DisplayName("JVM | Kotlin | SQLite: Delete") -class DeleteIT { +@DisplayName("Core | Kotlin | SQLite: Delete") +class SQLiteDeleteIT { @Test @DisplayName("byId deletes a matching ID") fun byIdMatch() = - SQLiteDB().use(Delete::byIdMatch) + SQLiteDB().use(DeleteFunctions::byIdMatch) @Test @DisplayName("byId succeeds when no ID matches") fun byIdNoMatch() = - SQLiteDB().use(Delete::byIdNoMatch) + SQLiteDB().use(DeleteFunctions::byIdNoMatch) @Test @DisplayName("byFields deletes matching documents") fun byFieldsMatch() = - SQLiteDB().use(Delete::byFieldsMatch) + SQLiteDB().use(DeleteFunctions::byFieldsMatch) @Test @DisplayName("byFields succeeds when no documents match") fun byFieldsNoMatch() = - SQLiteDB().use(Delete::byFieldsNoMatch) + SQLiteDB().use(DeleteFunctions::byFieldsNoMatch) @Test @DisplayName("byContains fails") fun byContainsFails() { - assertThrows { SQLiteDB().use(Delete::byContainsMatch) } + assertThrows { SQLiteDB().use(DeleteFunctions::byContainsMatch) } } @Test @DisplayName("byJsonPath fails") fun byJsonPathFails() { - assertThrows { SQLiteDB().use(Delete::byJsonPathMatch) } + assertThrows { SQLiteDB().use(DeleteFunctions::byJsonPathMatch) } } } diff --git a/src/core/src/test/kotlin/integration/SQLiteDocumentIT.kt b/src/core/src/test/kotlin/integration/SQLiteDocumentIT.kt new file mode 100644 index 0000000..f062725 --- /dev/null +++ b/src/core/src/test/kotlin/integration/SQLiteDocumentIT.kt @@ -0,0 +1,56 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import kotlin.test.Test + +/** + * SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions + */ +@DisplayName("Core | Kotlin | SQLite: Document") +class SQLiteDocumentIT { + + @Test + @DisplayName("insert works with default values") + fun insertDefault() = + SQLiteDB().use(DocumentFunctions::insertDefault) + + @Test + @DisplayName("insert fails with duplicate key") + fun insertDupe() = + SQLiteDB().use(DocumentFunctions::insertDupe) + + @Test + @DisplayName("insert succeeds with numeric auto IDs") + fun insertNumAutoId() = + SQLiteDB().use(DocumentFunctions::insertNumAutoId) + + @Test + @DisplayName("insert succeeds with UUID auto ID") + fun insertUUIDAutoId() = + SQLiteDB().use(DocumentFunctions::insertUUIDAutoId) + + @Test + @DisplayName("insert succeeds with random string auto ID") + fun insertStringAutoId() = + SQLiteDB().use(DocumentFunctions::insertStringAutoId) + + @Test + @DisplayName("save updates an existing document") + fun saveMatch() = + SQLiteDB().use(DocumentFunctions::saveMatch) + + @Test + @DisplayName("save inserts a new document") + fun saveNoMatch() = + SQLiteDB().use(DocumentFunctions::saveNoMatch) + + @Test + @DisplayName("update replaces an existing document") + fun updateMatch() = + SQLiteDB().use(DocumentFunctions::updateMatch) + + @Test + @DisplayName("update succeeds when no document exists") + fun updateNoMatch() = + SQLiteDB().use(DocumentFunctions::updateNoMatch) +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/sqlite/ExistsIT.kt b/src/core/src/test/kotlin/integration/SQLiteExistsIT.kt similarity index 61% rename from src/jvm/src/test/kotlin/jvm/integration/sqlite/ExistsIT.kt rename to src/core/src/test/kotlin/integration/SQLiteExistsIT.kt index b4b65f2..e4ad1a9 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/sqlite/ExistsIT.kt +++ b/src/core/src/test/kotlin/integration/SQLiteExistsIT.kt @@ -1,46 +1,45 @@ -package solutions.bitbadger.documents.jvm.integration.sqlite +package solutions.bitbadger.documents.core.tests.integration import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException -import solutions.bitbadger.documents.jvm.integration.common.Exists import kotlin.test.Test /** * SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions */ -@DisplayName("JVM | Kotlin | SQLite: Exists") -class ExistsIT { +@DisplayName("Core | Kotlin | SQLite: Exists") +class SQLiteExistsIT { @Test @DisplayName("byId returns true when a document matches the ID") fun byIdMatch() = - SQLiteDB().use(Exists::byIdMatch) + SQLiteDB().use(ExistsFunctions::byIdMatch) @Test @DisplayName("byId returns false when no document matches the ID") fun byIdNoMatch() = - SQLiteDB().use(Exists::byIdNoMatch) + SQLiteDB().use(ExistsFunctions::byIdNoMatch) @Test @DisplayName("byFields returns true when documents match") fun byFieldsMatch() = - SQLiteDB().use(Exists::byFieldsMatch) + SQLiteDB().use(ExistsFunctions::byFieldsMatch) @Test @DisplayName("byFields returns false when no documents match") fun byFieldsNoMatch() = - SQLiteDB().use(Exists::byFieldsNoMatch) + SQLiteDB().use(ExistsFunctions::byFieldsNoMatch) @Test @DisplayName("byContains fails") fun byContainsFails() { - assertThrows { SQLiteDB().use(Exists::byContainsMatch) } + assertThrows { SQLiteDB().use(ExistsFunctions::byContainsMatch) } } @Test @DisplayName("byJsonPath fails") fun byJsonPathFails() { - assertThrows { SQLiteDB().use(Exists::byJsonPathMatch) } + assertThrows { SQLiteDB().use(ExistsFunctions::byJsonPathMatch) } } } diff --git a/src/core/src/test/kotlin/integration/SQLiteFindIT.kt b/src/core/src/test/kotlin/integration/SQLiteFindIT.kt new file mode 100644 index 0000000..25f1f02 --- /dev/null +++ b/src/core/src/test/kotlin/integration/SQLiteFindIT.kt @@ -0,0 +1,127 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.DocumentException +import kotlin.test.Test + +/** + * SQLite integration tests for the `Find` object / `find*` connection extension functions + */ +@DisplayName("Core | Kotlin | SQLite: Find") +class SQLiteFindIT { + + @Test + @DisplayName("all retrieves all documents") + fun allDefault() = + SQLiteDB().use(FindFunctions::allDefault) + + @Test + @DisplayName("all sorts data ascending") + fun allAscending() = + SQLiteDB().use(FindFunctions::allAscending) + + @Test + @DisplayName("all sorts data descending") + fun allDescending() = + SQLiteDB().use(FindFunctions::allDescending) + + @Test + @DisplayName("all sorts data numerically") + fun allNumOrder() = + SQLiteDB().use(FindFunctions::allNumOrder) + + @Test + @DisplayName("all succeeds with an empty table") + fun allEmpty() = + SQLiteDB().use(FindFunctions::allEmpty) + + @Test + @DisplayName("byId retrieves a document via a string ID") + fun byIdString() = + SQLiteDB().use(FindFunctions::byIdString) + + @Test + @DisplayName("byId retrieves a document via a numeric ID") + fun byIdNumber() = + SQLiteDB().use(FindFunctions::byIdNumber) + + @Test + @DisplayName("byId returns null when a matching ID is not found") + fun byIdNotFound() = + SQLiteDB().use(FindFunctions::byIdNotFound) + + @Test + @DisplayName("byFields retrieves matching documents") + fun byFieldsMatch() = + SQLiteDB().use(FindFunctions::byFieldsMatch) + + @Test + @DisplayName("byFields retrieves ordered matching documents") + fun byFieldsMatchOrdered() = + SQLiteDB().use(FindFunctions::byFieldsMatchOrdered) + + @Test + @DisplayName("byFields retrieves matching documents with a numeric IN clause") + fun byFieldsMatchNumIn() = + SQLiteDB().use(FindFunctions::byFieldsMatchNumIn) + + @Test + @DisplayName("byFields succeeds when no documents match") + fun byFieldsNoMatch() = + SQLiteDB().use(FindFunctions::byFieldsNoMatch) + + @Test + @DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison") + fun byFieldsMatchInArray() = + SQLiteDB().use(FindFunctions::byFieldsMatchInArray) + + @Test + @DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison") + fun byFieldsNoMatchInArray() = + SQLiteDB().use(FindFunctions::byFieldsNoMatchInArray) + + @Test + @DisplayName("byContains fails") + fun byContainsFails() { + assertThrows { SQLiteDB().use(FindFunctions::byContainsMatch) } + } + + @Test + @DisplayName("byJsonPath fails") + fun byJsonPathFails() { + assertThrows { SQLiteDB().use(FindFunctions::byJsonPathMatch) } + } + + @Test + @DisplayName("firstByFields retrieves a matching document") + fun firstByFieldsMatchOne() = + SQLiteDB().use(FindFunctions::firstByFieldsMatchOne) + + @Test + @DisplayName("firstByFields retrieves a matching document among many") + fun firstByFieldsMatchMany() = + SQLiteDB().use(FindFunctions::firstByFieldsMatchMany) + + @Test + @DisplayName("firstByFields retrieves a matching document among many (ordered)") + fun firstByFieldsMatchOrdered() = + SQLiteDB().use(FindFunctions::firstByFieldsMatchOrdered) + + @Test + @DisplayName("firstByFields returns null when no document matches") + fun firstByFieldsNoMatch() = + SQLiteDB().use(FindFunctions::firstByFieldsNoMatch) + + @Test + @DisplayName("firstByContains fails") + fun firstByContainsFails() { + assertThrows { SQLiteDB().use(FindFunctions::firstByContainsMatchOne) } + } + + @Test + @DisplayName("firstByJsonPath fails") + fun firstByJsonPathFails() { + assertThrows { SQLiteDB().use(FindFunctions::firstByJsonPathMatchOne) } + } +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/sqlite/PatchIT.kt b/src/core/src/test/kotlin/integration/SQLitePatchIT.kt similarity index 60% rename from src/jvm/src/test/kotlin/jvm/integration/sqlite/PatchIT.kt rename to src/core/src/test/kotlin/integration/SQLitePatchIT.kt index 7918bef..abbdcb0 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/sqlite/PatchIT.kt +++ b/src/core/src/test/kotlin/integration/SQLitePatchIT.kt @@ -1,46 +1,45 @@ -package solutions.bitbadger.documents.jvm.integration.sqlite +package solutions.bitbadger.documents.core.tests.integration import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException -import solutions.bitbadger.documents.jvm.integration.common.Patch import kotlin.test.Test /** * SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions */ -@DisplayName("JVM | Kotlin | SQLite: Patch") -class PatchIT { +@DisplayName("Core | Kotlin | SQLite: Patch") +class SQLitePatchIT { @Test @DisplayName("byId patches an existing document") fun byIdMatch() = - SQLiteDB().use(Patch::byIdMatch) + SQLiteDB().use(PatchFunctions::byIdMatch) @Test @DisplayName("byId succeeds for a non-existent document") fun byIdNoMatch() = - SQLiteDB().use(Patch::byIdNoMatch) + SQLiteDB().use(PatchFunctions::byIdNoMatch) @Test @DisplayName("byFields patches matching document") fun byFieldsMatch() = - SQLiteDB().use(Patch::byFieldsMatch) + SQLiteDB().use(PatchFunctions::byFieldsMatch) @Test @DisplayName("byFields succeeds when no documents match") fun byFieldsNoMatch() = - SQLiteDB().use(Patch::byFieldsNoMatch) + SQLiteDB().use(PatchFunctions::byFieldsNoMatch) @Test @DisplayName("byContains fails") fun byContainsFails() { - assertThrows { SQLiteDB().use(Patch::byContainsMatch) } + assertThrows { SQLiteDB().use(PatchFunctions::byContainsMatch) } } @Test @DisplayName("byJsonPath fails") fun byJsonPathFails() { - assertThrows { SQLiteDB().use(Patch::byJsonPathMatch) } + assertThrows { SQLiteDB().use(PatchFunctions::byJsonPathMatch) } } } \ No newline at end of file diff --git a/src/core/src/test/kotlin/integration/SQLiteRemoveFieldsIT.kt b/src/core/src/test/kotlin/integration/SQLiteRemoveFieldsIT.kt new file mode 100644 index 0000000..5fbc6c0 --- /dev/null +++ b/src/core/src/test/kotlin/integration/SQLiteRemoveFieldsIT.kt @@ -0,0 +1,55 @@ +package solutions.bitbadger.documents.core.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.DocumentException +import kotlin.test.Test + +/** + * SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions + */ +@DisplayName("Core | Kotlin | SQLite: RemoveFields") +class SQLiteRemoveFieldsIT { + + @Test + @DisplayName("byId removes fields from an existing document") + fun byIdMatchFields() = + SQLiteDB().use(RemoveFieldsFunctions::byIdMatchFields) + + @Test + @DisplayName("byId succeeds when fields do not exist on an existing document") + fun byIdMatchNoFields() = + SQLiteDB().use(RemoveFieldsFunctions::byIdMatchNoFields) + + @Test + @DisplayName("byId succeeds when no document exists") + fun byIdNoMatch() = + SQLiteDB().use(RemoveFieldsFunctions::byIdNoMatch) + + @Test + @DisplayName("byFields removes fields from matching documents") + fun byFieldsMatchFields() = + SQLiteDB().use(RemoveFieldsFunctions::byFieldsMatchFields) + + @Test + @DisplayName("byFields succeeds when fields do not exist on matching documents") + fun byFieldsMatchNoFields() = + SQLiteDB().use(RemoveFieldsFunctions::byFieldsMatchNoFields) + + @Test + @DisplayName("byFields succeeds when no matching documents exist") + fun byFieldsNoMatch() = + SQLiteDB().use(RemoveFieldsFunctions::byFieldsNoMatch) + + @Test + @DisplayName("byContains fails") + fun byContainsFails() { + assertThrows { SQLiteDB().use(RemoveFieldsFunctions::byContainsMatchFields) } + } + + @Test + @DisplayName("byJsonPath fails") + fun byJsonPathFails() { + assertThrows { SQLiteDB().use(RemoveFieldsFunctions::byJsonPathMatchFields) } + } +} diff --git a/src/jvm/src/test/kotlin/support/ThrowawayDatabase.kt b/src/core/src/test/kotlin/integration/ThrowawayDatabase.kt similarity index 88% rename from src/jvm/src/test/kotlin/support/ThrowawayDatabase.kt rename to src/core/src/test/kotlin/integration/ThrowawayDatabase.kt index 9c63c30..4f454d1 100644 --- a/src/jvm/src/test/kotlin/support/ThrowawayDatabase.kt +++ b/src/core/src/test/kotlin/integration/ThrowawayDatabase.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.support +package solutions.bitbadger.documents.core.tests.integration import java.sql.Connection diff --git a/src/groovy/groovy.iml b/src/groovy/groovy.iml new file mode 100644 index 0000000..056f882 --- /dev/null +++ b/src/groovy/groovy.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/groovy/pom.xml b/src/groovy/pom.xml new file mode 100644 index 0000000..22d1d7c --- /dev/null +++ b/src/groovy/pom.xml @@ -0,0 +1,109 @@ + + + 4.0.0 + + solutions.bitbadger + documents + 4.0.0-alpha1-SNAPSHOT + ../../pom.xml + + + solutions.bitbadger.documents + groovy + + ${project.groupId}:${project.artifactId} + Expose a document store interface for PostgreSQL and SQLite (Groovy Library) + https://bitbadger.solutions/open-source/relational-documents/jvm/ + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + 4.1.1 + + + + addSources + addTestSources + generateStubs + compile + generateTestStubs + compileTests + removeStubs + removeTestStubs + + + + + + org.apache.groovy + groovy + ${groovy.version} + runtime + pom + + + + + maven-surefire-plugin + ${surefire.version} + + + maven-failsafe-plugin + ${failsafe.version} + + + + integration-test + verify + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${java.version} + ${java.version} + + + + + + + + solutions.bitbadger.documents + core + ${project.version} + + + org.apache.groovy + groovy + ${groovy.version} + + + org.apache.groovy + groovy-test + ${groovy.version} + test + + + org.apache.groovy + groovy-test-junit5 + ${groovy.version} + test + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + + + + \ No newline at end of file diff --git a/src/groovy/src/main/groovy/.gitkeep b/src/groovy/src/main/groovy/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/groovy/src/main/java/module-info.java b/src/groovy/src/main/java/module-info.java new file mode 100644 index 0000000..4bba727 --- /dev/null +++ b/src/groovy/src/main/java/module-info.java @@ -0,0 +1,3 @@ +module solutions.bitbadger.documents.groovy { + requires solutions.bitbadger.documents.core; +} diff --git a/src/groovy/src/main/resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule b/src/groovy/src/main/resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule new file mode 100644 index 0000000..e5291f8 --- /dev/null +++ b/src/groovy/src/main/resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule @@ -0,0 +1,3 @@ +moduleName=Document Extensions for Connection +moduleVersion=4.0.0-alpha1 +extensionClasses=solutions.bitbadger.documents.java.extensions.ConnExt diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/AutoIdTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/AutoIdTest.groovy similarity index 97% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/AutoIdTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/AutoIdTest.groovy index 1cecfa8..f4501e7 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/AutoIdTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/AutoIdTest.groovy @@ -1,17 +1,16 @@ -package solutions.bitbadger.documents.groovy +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import solutions.bitbadger.documents.AutoId import solutions.bitbadger.documents.DocumentException -import solutions.bitbadger.documents.groovy.support.* import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `AutoId` enum */ -@DisplayName('JVM | Groovy | AutoId') +@DisplayName('Groovy | AutoId') class AutoIdTest { @Test diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/ByteIdClass.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ByteIdClass.groovy similarity index 62% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/ByteIdClass.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ByteIdClass.groovy index 62442f6..f8506c7 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/ByteIdClass.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ByteIdClass.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.support +package solutions.bitbadger.documents.groovy.tests class ByteIdClass { byte id diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/ConfigurationTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ConfigurationTest.groovy similarity index 93% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/ConfigurationTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ConfigurationTest.groovy index 87d7383..6dc8f3a 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/ConfigurationTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ConfigurationTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -12,7 +12,7 @@ import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Configuration` object */ -@DisplayName('JVM | Groovy | Configuration') +@DisplayName('Groovy | Configuration') class ConfigurationTest { @Test diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/CountQueryTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/CountQueryTest.groovy similarity index 91% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/CountQueryTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/CountQueryTest.groovy index 95d62b5..10f69f4 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/CountQueryTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/CountQueryTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.query +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -6,15 +6,14 @@ import org.junit.jupiter.api.Test import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.query.CountQuery -import solutions.bitbadger.documents.support.ForceDialect -import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE +import static Types.TEST_TABLE import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Count` object */ -@DisplayName('JVM | Groovy | Query | CountQuery') +@DisplayName('Groovy | Query | CountQuery') class CountQueryTest { /** diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/DefinitionQueryTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DefinitionQueryTest.groovy similarity index 95% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/DefinitionQueryTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DefinitionQueryTest.groovy index 6ce06fb..347d90c 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/DefinitionQueryTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DefinitionQueryTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.query +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -7,15 +7,14 @@ import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentIndex import solutions.bitbadger.documents.query.DefinitionQuery -import solutions.bitbadger.documents.support.ForceDialect -import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE +import static Types.TEST_TABLE import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Definition` object */ -@DisplayName('JVM | Groovy | Query | DefinitionQuery') +@DisplayName('Groovy | Query | DefinitionQuery') class DefinitionQueryTest { /** diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/DeleteQueryTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DeleteQueryTest.groovy similarity index 92% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/DeleteQueryTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DeleteQueryTest.groovy index 4b16942..c2a4128 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/DeleteQueryTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DeleteQueryTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.query +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -6,15 +6,14 @@ 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 static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE +import static Types.TEST_TABLE import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Delete` object */ -@DisplayName('JVM | Groovy | Query | DeleteQuery') +@DisplayName('Groovy | Query | DeleteQuery') class DeleteQueryTest { /** diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/DialectTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DialectTest.groovy similarity index 94% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/DialectTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DialectTest.groovy index 2aae920..f1905ba 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/DialectTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DialectTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Dialect` enum */ -@DisplayName('JVM | Groovy | Dialect') +@DisplayName('Groovy | Dialect') class DialectTest { @Test diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/DocumentIndexTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DocumentIndexTest.groovy similarity index 87% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/DocumentIndexTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DocumentIndexTest.groovy index a79aa5e..26f54d7 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/DocumentIndexTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DocumentIndexTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals /** * Unit tests for the `DocumentIndex` enum */ -@DisplayName('JVM | Groovy | DocumentIndex') +@DisplayName('Groovy | DocumentIndex') class DocumentIndexTest { @Test diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/DocumentQueryTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DocumentQueryTest.groovy similarity index 95% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/DocumentQueryTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DocumentQueryTest.groovy index 29edebb..d912547 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/DocumentQueryTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/DocumentQueryTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.query +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -7,15 +7,14 @@ 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 solutions.bitbadger.documents.support.TypesKt.TEST_TABLE +import static Types.TEST_TABLE import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Document` object */ -@DisplayName('JVM | Groovy | Query | DocumentQuery') +@DisplayName('Groovy | Query | DocumentQuery') class DocumentQueryTest { /** diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/FieldMatchTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/FieldMatchTest.groovy similarity index 86% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/FieldMatchTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/FieldMatchTest.groovy index f1078e7..2be46d8 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/FieldMatchTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/FieldMatchTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals /** * Unit tests for the `FieldMatch` enum */ -@DisplayName('JVM | Groovy | FieldMatch') +@DisplayName('Groovy | FieldMatch') class FieldMatchTest { @Test diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/FieldTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/FieldTest.groovy similarity index 99% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/FieldTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/FieldTest.groovy index da2214e..82abe77 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/FieldTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/FieldTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -8,14 +8,13 @@ import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.FieldFormat import solutions.bitbadger.documents.Op -import solutions.bitbadger.documents.support.ForceDialect import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Field` class */ -@DisplayName('JVM | Groovy | Field') +@DisplayName('Groovy | Field') class FieldTest { /** diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/FindQueryTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/FindQueryTest.groovy similarity index 93% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/FindQueryTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/FindQueryTest.groovy index 7d557c2..bec1d7b 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/FindQueryTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/FindQueryTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.query +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -6,15 +6,14 @@ 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 static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE +import static Types.TEST_TABLE import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Find` object */ -@DisplayName('JVM | Groovy | Query | FindQuery') +@DisplayName('Groovy | Query | FindQuery') class FindQueryTest { /** diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ForceDialect.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ForceDialect.groovy new file mode 100644 index 0000000..5784dd9 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ForceDialect.groovy @@ -0,0 +1,19 @@ +package solutions.bitbadger.documents.groovy.tests + +import solutions.bitbadger.documents.Configuration + +final class ForceDialect { + + static void postgres() { + Configuration.connectionString = ":postgresql:" + } + + static void sqlite() { + Configuration.connectionString = ":sqlite:" + } + + static void none() { + Configuration.connectionString = null + } + +} diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/IntIdClass.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/IntIdClass.groovy similarity index 61% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/IntIdClass.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/IntIdClass.groovy index 63d7f75..867b0ff 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/IntIdClass.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/IntIdClass.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.support +package solutions.bitbadger.documents.groovy.tests class IntIdClass { int id diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/LongIdClass.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/LongIdClass.groovy similarity index 62% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/LongIdClass.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/LongIdClass.groovy index 377fa1d..a6b1f07 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/LongIdClass.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/LongIdClass.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.support +package solutions.bitbadger.documents.groovy.tests class LongIdClass { long id diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/OpTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/OpTest.groovy similarity index 96% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/OpTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/OpTest.groovy index f4bd6e3..84c2d9e 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/OpTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/OpTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals /** * Unit tests for the `Op` enum */ -@DisplayName('JVM | Groovy | Op') +@DisplayName('Groovy | Op') class OpTest { @Test diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/ParameterNameTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ParameterNameTest.groovy similarity index 92% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/ParameterNameTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ParameterNameTest.groovy index 0acc5d0..947dac9 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/ParameterNameTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ParameterNameTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals /** * Unit tests for the `ParameterName` class */ -@DisplayName('JVM | Groovy | ParameterName') +@DisplayName('Groovy | ParameterName') class ParameterNameTest { @Test diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/ParameterTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ParameterTest.groovy similarity index 93% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/ParameterTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ParameterTest.groovy index 76564b1..83d373a 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/ParameterTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ParameterTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Parameter` class */ -@DisplayName('JVM | Groovy | Parameter') +@DisplayName('Groovy | Parameter') class ParameterTest { @Test diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/ParametersTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ParametersTest.groovy similarity index 96% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/ParametersTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ParametersTest.groovy index a8581a7..499a2d1 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/ParametersTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ParametersTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.jvm +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -7,15 +7,14 @@ import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Parameter import solutions.bitbadger.documents.ParameterType -import solutions.bitbadger.documents.jvm.Parameters -import solutions.bitbadger.documents.support.ForceDialect +import solutions.bitbadger.documents.java.Parameters import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Parameters` object */ -@DisplayName('JVM | Groovy | Parameters') +@DisplayName('Groovy | Parameters') class ParametersTest { /** diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/PatchQueryTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/PatchQueryTest.groovy similarity index 92% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/PatchQueryTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/PatchQueryTest.groovy index c362109..0a00a6f 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/PatchQueryTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/PatchQueryTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.query +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -6,15 +6,14 @@ 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 static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE +import static Types.TEST_TABLE import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Patch` object */ -@DisplayName('JVM | Groovy | Query | PatchQuery') +@DisplayName('Groovy | Query | PatchQuery') class PatchQueryTest { /** diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/QueryUtilsTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/QueryUtilsTest.groovy similarity index 97% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/QueryUtilsTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/QueryUtilsTest.groovy index 0643482..300eccf 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/QueryUtilsTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/QueryUtilsTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.query +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -7,14 +7,13 @@ import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.FieldMatch import solutions.bitbadger.documents.query.QueryUtils -import solutions.bitbadger.documents.support.ForceDialect import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `QueryUtils` class */ -@DisplayName('JVM | Groovy | Query | QueryUtils') +@DisplayName('Groovy | Query | QueryUtils') class QueryUtilsTest { /** diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/RemoveFieldsQueryTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/RemoveFieldsQueryTest.groovy similarity index 94% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/RemoveFieldsQueryTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/RemoveFieldsQueryTest.groovy index 546b06c..0c4c5e2 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/RemoveFieldsQueryTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/RemoveFieldsQueryTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.query +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -8,15 +8,14 @@ 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 static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE +import static Types.TEST_TABLE import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `RemoveFields` object */ -@DisplayName('JVM | Groovy | Query | RemoveFieldsQuery') +@DisplayName('Groovy | Query | RemoveFieldsQuery') class RemoveFieldsQueryTest { /** diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/ShortIdClass.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ShortIdClass.groovy similarity index 63% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/ShortIdClass.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ShortIdClass.groovy index fca6ea0..bf03a6a 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/ShortIdClass.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/ShortIdClass.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.support +package solutions.bitbadger.documents.groovy.tests class ShortIdClass { short id diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/StringIdClass.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/StringIdClass.groovy similarity index 64% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/StringIdClass.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/StringIdClass.groovy index 90e68e9..544cf37 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/StringIdClass.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/StringIdClass.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.support +package solutions.bitbadger.documents.groovy.tests class StringIdClass { String id diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/Types.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/Types.groovy new file mode 100644 index 0000000..5d2f67e --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/Types.groovy @@ -0,0 +1,6 @@ +package solutions.bitbadger.documents.groovy.tests + +final class Types { + + public static final String TEST_TABLE = 'test_table' +} diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/WhereTest.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/WhereTest.groovy similarity index 97% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/WhereTest.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/WhereTest.groovy index e8fea87..170b6a6 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/WhereTest.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/WhereTest.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.query +package solutions.bitbadger.documents.groovy.tests import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName @@ -7,14 +7,13 @@ import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.FieldMatch import solutions.bitbadger.documents.query.Where -import solutions.bitbadger.documents.support.ForceDialect import static org.junit.jupiter.api.Assertions.* /** * Unit tests for the `Where` object */ -@DisplayName('JVM | Groovy | Query | Where') +@DisplayName('Groovy | Query | Where') class WhereTest { /** diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/ArrayDocument.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/ArrayDocument.groovy new file mode 100644 index 0000000..59af483 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/ArrayDocument.groovy @@ -0,0 +1,18 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +class ArrayDocument { + + String id + List values + + ArrayDocument(String id = '', List values = List.of()) { + this.id = id + this.values = values + } + + /** A set of documents used for integration tests */ + static List testDocuments = List.of( + new ArrayDocument("first", List.of("a", "b", "c")), + new ArrayDocument("second", List.of("c", "d", "e")), + new ArrayDocument("third", List.of("x", "y", "z"))) +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/CountFunctions.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/CountFunctions.groovy new file mode 100644 index 0000000..5f9e852 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/CountFunctions.groovy @@ -0,0 +1,50 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.Field + +import static org.junit.jupiter.api.Assertions.* +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +final class CountFunctions { + + static void all(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should have been 5 documents in the table' + } + + static void byFieldsNumeric(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(3L, db.conn.countByFields(TEST_TABLE, List.of(Field.between('numValue', 10, 20))), + 'There should have been 3 matching documents') + } + + static void byFieldsAlpha(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(1L, db.conn.countByFields(TEST_TABLE, List.of(Field.between('value', 'aardvark', 'apple'))), + 'There should have been 1 matching document') + } + + static void byContainsMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(2L, db.conn.countByContains(TEST_TABLE, Map.of('value', 'purple')), + 'There should have been 2 matching documents') + } + + static void byContainsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(0L, db.conn.countByContains(TEST_TABLE, Map.of('value', 'magenta')), + 'There should have been no matching documents') + } + + static void byJsonPathMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(2L, db.conn.countByJsonPath(TEST_TABLE, '$.numValue ? (@ < 5)'), + 'There should have been 2 matching documents') + } + + static void byJsonPathNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(0L, db.conn.countByJsonPath(TEST_TABLE, '$.numValue ? (@ > 100)'), + 'There should have been no matching documents') + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/CustomFunctions.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/CustomFunctions.groovy new file mode 100644 index 0000000..72208e0 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/CustomFunctions.groovy @@ -0,0 +1,69 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.Configuration +import solutions.bitbadger.documents.Field +import solutions.bitbadger.documents.Parameter +import solutions.bitbadger.documents.ParameterType +import solutions.bitbadger.documents.java.Results +import solutions.bitbadger.documents.query.CountQuery +import solutions.bitbadger.documents.query.DeleteQuery +import solutions.bitbadger.documents.query.FindQuery + +import static org.junit.jupiter.api.Assertions.* +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +final class CustomFunctions { + + static void listEmpty(ThrowawayDatabase db) { + JsonDocument.load db + db.conn.deleteByFields TEST_TABLE, List.of(Field.exists(Configuration.idField)) + def result = db.conn.customList FindQuery.all(TEST_TABLE), List.of(), JsonDocument, Results.&fromData + assertEquals 0, result.size(), 'There should have been no results' + } + + static void listAll(ThrowawayDatabase db) { + JsonDocument.load db + def result = db.conn.customList FindQuery.all(TEST_TABLE), List.of(), JsonDocument, Results.&fromData + assertEquals 5, result.size(), 'There should have been 5 results' + } + + static void singleNone(ThrowawayDatabase db) { + assertFalse(db.conn.customSingle(FindQuery.all(TEST_TABLE), List.of(), JsonDocument, Results.&fromData) + .isPresent(), + 'There should not have been a document returned') + + } + + static void singleOne(ThrowawayDatabase db) { + JsonDocument.load db + assertTrue(db.conn.customSingle(FindQuery.all(TEST_TABLE), List.of(), JsonDocument, Results.&fromData) + .isPresent(), + 'There should not have been a document returned') + } + + static void nonQueryChanges(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), List.of(), Long, Results.&toCount), + 'There should have been 5 documents in the table') + db.conn.customNonQuery("DELETE FROM $TEST_TABLE") + assertEquals(0L, db.conn.customScalar(CountQuery.all(TEST_TABLE), List.of(), Long, Results.&toCount), + 'There should have been no documents in the table') + } + + static void nonQueryNoChanges(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), List.of(), Long, Results.&toCount), + 'There should have been 5 documents in the table') + db.conn.customNonQuery(DeleteQuery.byId(TEST_TABLE, 'eighty-two'), + List.of(new Parameter(':id', ParameterType.STRING, 'eighty-two'))) + assertEquals(5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), List.of(), Long, Results.&toCount), + 'There should still have been 5 documents in the table') + } + + static void scalar(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(3L, + db.conn.customScalar("SELECT 3 AS it FROM $TEST_TABLE LIMIT 1", List.of(), Long, Results.&toCount), + 'The number 3 should have been returned') + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/DefinitionFunctions.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/DefinitionFunctions.groovy new file mode 100644 index 0000000..7108a70 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/DefinitionFunctions.groovy @@ -0,0 +1,41 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.DocumentIndex + +import static org.junit.jupiter.api.Assertions.* +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +final class DefinitionFunctions { + + static void ensureTable(ThrowawayDatabase db) { + assertFalse db.dbObjectExists('ensured'), 'The "ensured" table should not exist' + assertFalse db.dbObjectExists('idx_ensured_key'), 'The PK index for the "ensured" table should not exist' + db.conn.ensureTable 'ensured' + assertTrue db.dbObjectExists('ensured'), 'The "ensured" table should exist' + assertTrue db.dbObjectExists('idx_ensured_key'), 'The PK index for the "ensured" table should now exist' + } + + static void ensureFieldIndex(ThrowawayDatabase db) { + assertFalse db.dbObjectExists("idx_${TEST_TABLE}_test"), 'The test index should not exist' + db.conn.ensureFieldIndex TEST_TABLE, 'test', List.of('id', 'category') + assertTrue db.dbObjectExists("idx_${TEST_TABLE}_test"), 'The test index should now exist' + } + + static void ensureDocumentIndexFull(ThrowawayDatabase db) { + assertFalse db.dbObjectExists('doc_table'), 'The "doc_table" table should not exist' + db.conn.ensureTable 'doc_table' + assertTrue db.dbObjectExists('doc_table'), 'The "doc_table" table should exist' + assertFalse db.dbObjectExists('idx_doc_table_document'), 'The document index should not exist' + db.conn.ensureDocumentIndex 'doc_table', DocumentIndex.FULL + assertTrue db.dbObjectExists('idx_doc_table_document'), 'The document index should exist' + } + + static void ensureDocumentIndexOptimized(ThrowawayDatabase db) { + assertFalse db.dbObjectExists('doc_table'), 'The "doc_table" table should not exist' + db.conn.ensureTable 'doc_table' + assertTrue db.dbObjectExists('doc_table'), 'The "doc_table" table should exist' + assertFalse db.dbObjectExists('idx_doc_table_document'), 'The document index should not exist' + db.conn.ensureDocumentIndex 'doc_table', DocumentIndex.OPTIMIZED + assertTrue db.dbObjectExists('idx_doc_table_document'), 'The document index should exist' + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/DeleteFunctions.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/DeleteFunctions.groovy new file mode 100644 index 0000000..6d51048 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/DeleteFunctions.groovy @@ -0,0 +1,65 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.Field + +import static org.junit.jupiter.api.Assertions.* +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +final class DeleteFunctions { + + static void byIdMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table' + db.conn.deleteById TEST_TABLE, 'four' + assertEquals 4L, db.conn.countAll(TEST_TABLE), 'There should now be 4 documents in the table' + } + + static void byIdNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table' + db.conn.deleteById TEST_TABLE, 'negative four' + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should still be 5 documents in the table' + } + + static void byFieldsMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table' + db.conn.deleteByFields TEST_TABLE, List.of(Field.notEqual('value', 'purple')) + assertEquals 2L, db.conn.countAll(TEST_TABLE), 'There should now be 2 documents in the table' + } + + static void byFieldsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table' + db.conn.deleteByFields TEST_TABLE, List.of(Field.equal('value', 'crimson')) + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should still be 5 documents in the table' + } + + static void byContainsMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table' + db.conn.deleteByContains TEST_TABLE, Map.of('value', 'purple') + assertEquals 3L, db.conn.countAll(TEST_TABLE), 'There should now be 3 documents in the table' + } + + static void byContainsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table' + db.conn.deleteByContains TEST_TABLE, Map.of('target', 'acquired') + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should still be 5 documents in the table' + } + + static void byJsonPathMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table' + db.conn.deleteByJsonPath TEST_TABLE, '$.value ? (@ == "purple")' + assertEquals 3L, db.conn.countAll(TEST_TABLE), 'There should now be 3 documents in the table' + } + + static void byJsonPathNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table' + db.conn.deleteByJsonPath TEST_TABLE, '$.numValue ? (@ > 100)' + assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should still be 5 documents in the table' + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/DocumentFunctions.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/DocumentFunctions.groovy new file mode 100644 index 0000000..5330ebc --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/DocumentFunctions.groovy @@ -0,0 +1,129 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.AutoId +import solutions.bitbadger.documents.Configuration +import solutions.bitbadger.documents.DocumentException +import solutions.bitbadger.documents.Field + +import static org.junit.jupiter.api.Assertions.* +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +final class DocumentFunctions { + + static void insertDefault(ThrowawayDatabase db) { + assertEquals 0L, db.conn.countAll(TEST_TABLE), 'There should be no documents in the table' + def doc = new JsonDocument('turkey', 'yum', 5, new SubDocument('gobble', 'gobble!')) + db.conn.insert TEST_TABLE, doc + def after = db.conn.findAll TEST_TABLE, JsonDocument + assertEquals 1, after.size(), 'There should be one document in the table' + assertEquals doc.id, after[0].id, 'The document ID was not inserted correctly' + assertEquals doc.value, after[0].value, 'The document value was not inserted correctly' + assertEquals doc.numValue, after[0].numValue, 'The document numValue was not inserted correctly' + assertNotNull doc.sub, "The subdocument was not inserted" + assertEquals doc.sub.foo, after[0].sub.foo, 'The subdocument "foo" property was not inserted correctly' + assertEquals doc.sub.bar, after[0].sub.bar, 'The subdocument "bar" property was not inserted correctly' + } + + static void insertDupe(ThrowawayDatabase db) { + db.conn.insert TEST_TABLE, new JsonDocument('a', '', 0, null) + assertThrows(DocumentException, () -> db.conn.insert(TEST_TABLE, new JsonDocument('a', 'b', 22, null)), + 'Inserting a document with a duplicate key should have thrown an exception') + } + + static void insertNumAutoId(ThrowawayDatabase db) { + try { + Configuration.autoIdStrategy = AutoId.NUMBER + Configuration.idField = 'key' + assertEquals 0L, db.conn.countAll(TEST_TABLE), 'There should be no documents in the table' + + db.conn.insert TEST_TABLE, new NumIdDocument(0, 'one') + db.conn.insert TEST_TABLE, new NumIdDocument(0, 'two') + db.conn.insert TEST_TABLE, new NumIdDocument(77, 'three') + db.conn.insert TEST_TABLE, new NumIdDocument(0, 'four') + + def after = db.conn.findAll TEST_TABLE, NumIdDocument, List.of(Field.named('key')) + assertEquals 4, after.size(), 'There should have been 4 documents returned' + assertEquals('1|2|77|78', + after*.key*.toString().inject('') { acc, it -> acc == '' ? it : "$acc|$it" }.toString(), + 'The IDs were not generated correctly') + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED + Configuration.idField = 'id' + } + } + + static void insertUUIDAutoId(ThrowawayDatabase db) { + try { + Configuration.autoIdStrategy = AutoId.UUID + assertEquals 0L, db.conn.countAll(TEST_TABLE), 'There should be no documents in the table' + + db.conn.insert TEST_TABLE, new JsonDocument('') + + def after = db.conn.findAll TEST_TABLE, JsonDocument + assertEquals 1, after.size(), 'There should have been 1 document returned' + assertEquals 32, after[0].id.length(), 'The ID was not generated correctly' + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED + } + } + + static void insertStringAutoId(ThrowawayDatabase db) { + try { + Configuration.autoIdStrategy = AutoId.RANDOM_STRING + assertEquals 0L, db.conn.countAll(TEST_TABLE), 'There should be no documents in the table' + + db.conn.insert TEST_TABLE, new JsonDocument('') + + Configuration.idStringLength = 21 + db.conn.insert TEST_TABLE, new JsonDocument('') + + def after = db.conn.findAll TEST_TABLE, JsonDocument + assertEquals 2, after.size(), 'There should have been 2 documents returned' + assertEquals 16, after[0].id.length(), "The first document's ID was not generated correctly" + assertEquals 21, after[1].id.length(), "The second document's ID was not generated correctly" + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED + Configuration.idStringLength = 16 + } + } + + static void saveMatch(ThrowawayDatabase db) { + JsonDocument.load db + db.conn.save TEST_TABLE, new JsonDocument('two', '', 44) + def tryDoc = db.conn.findById TEST_TABLE, 'two', JsonDocument + assertTrue tryDoc.isPresent(), 'There should have been a document returned' + def doc = tryDoc.get() + assertEquals 'two', doc.id, 'An incorrect document was returned' + assertEquals '', doc.value, 'The "value" field was not updated' + assertEquals 44, doc.numValue, 'The "numValue" field was not updated' + assertNull doc.sub, 'The "sub" field was not updated' + } + + static void saveNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + db.conn.save TEST_TABLE, new JsonDocument('test', '', 0, new SubDocument('a', 'b')) + assertTrue(db.conn.findById(TEST_TABLE, 'test', JsonDocument).isPresent(), + 'The test document should have been saved') + } + + static void updateMatch(ThrowawayDatabase db) { + JsonDocument.load db + db.conn.update TEST_TABLE, 'one', new JsonDocument('one', 'howdy', 8, new SubDocument('y', 'z')) + def tryDoc = db.conn.findById TEST_TABLE, 'one', JsonDocument + assertTrue tryDoc.isPresent(), 'There should have been a document returned' + def doc = tryDoc.get() + assertEquals 'one', doc.id, 'An incorrect document was returned' + assertEquals 'howdy', doc.value, 'The "value" field was not updated' + assertEquals 8, doc.numValue, 'The "numValue" field was not updated' + assertNotNull doc.sub, 'The sub-document should not be null' + assertEquals 'y', doc.sub.foo, 'The sub-document "foo" field was not updated' + assertEquals 'z', doc.sub.bar, 'The sub-document "bar" field was not updated' + } + + static void updateNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse db.conn.existsById(TEST_TABLE, 'two-hundred') + db.conn.update TEST_TABLE, 'two-hundred', new JsonDocument('two-hundred', '', 200) + assertFalse db.conn.existsById(TEST_TABLE, 'two-hundred') + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/ExistsFunctions.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/ExistsFunctions.groovy new file mode 100644 index 0000000..115940d --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/ExistsFunctions.groovy @@ -0,0 +1,55 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.Field + +import static org.junit.jupiter.api.Assertions.* +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +final class ExistsFunctions { + + static void byIdMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertTrue db.conn.existsById(TEST_TABLE, 'three'), 'The document with ID "three" should exist' + } + + static void byIdNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse db.conn.existsById(TEST_TABLE, 'seven'), 'The document with ID "seven" should not exist' + } + + static void byFieldsMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertTrue(db.conn.existsByFields(TEST_TABLE, List.of(Field.equal('numValue', 10))), + 'Matching documents should have been found') + } + + static void byFieldsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse(db.conn.existsByFields(TEST_TABLE, List.of(Field.equal('nothing', 'none'))), + 'No matching documents should have been found') + } + + static void byContainsMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertTrue(db.conn.existsByContains(TEST_TABLE, Map.of('value', 'purple')), + 'Matching documents should have been found') + } + + static void byContainsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse(db.conn.existsByContains(TEST_TABLE, Map.of('value', 'violet')), + 'Matching documents should not have been found') + } + + static void byJsonPathMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertTrue(db.conn.existsByJsonPath(TEST_TABLE, '$.numValue ? (@ == 10)'), + 'Matching documents should have been found') + } + + static void byJsonPathNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse(db.conn.existsByJsonPath(TEST_TABLE, '$.numValue ? (@ == 10.1)'), + 'Matching documents should not have been found') + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/FindFunctions.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/FindFunctions.groovy new file mode 100644 index 0000000..570f1da --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/FindFunctions.groovy @@ -0,0 +1,249 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.Configuration +import solutions.bitbadger.documents.Field +import solutions.bitbadger.documents.FieldMatch + +import static org.junit.jupiter.api.Assertions.* +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +final class FindFunctions { + + private static String docIds(List docs) { + return docs*.id.inject('') { acc, it -> acc == '' ? it : "$acc|$it" } + } + + static void allDefault(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals 5, db.conn.findAll(TEST_TABLE, JsonDocument).size(), 'There should have been 5 documents returned' + } + + static void allAscending(ThrowawayDatabase db) { + JsonDocument.load db + def docs = db.conn.findAll TEST_TABLE, JsonDocument, List.of(Field.named('id')) + assertEquals 5, docs.size(), 'There should have been 5 documents returned' + assertEquals 'five|four|one|three|two', docIds(docs), 'The documents were not ordered correctly' + } + + static void allDescending(ThrowawayDatabase db) { + JsonDocument.load db + def docs = db.conn.findAll TEST_TABLE, JsonDocument, List.of(Field.named('id DESC')) + assertEquals 5, docs.size(), 'There should have been 5 documents returned' + assertEquals 'two|three|one|four|five', docIds(docs), 'The documents were not ordered correctly' + } + + static void allNumOrder(ThrowawayDatabase db) { + JsonDocument.load db + def docs = db.conn.findAll(TEST_TABLE, JsonDocument, + List.of(Field.named('sub.foo NULLS LAST'), Field.named('n:numValue'))) + assertEquals 5, docs.size(), 'There should have been 5 documents returned' + assertEquals 'two|four|one|three|five', docIds(docs), 'The documents were not ordered correctly' + } + + static void allEmpty(ThrowawayDatabase db) { + assertEquals 0, db.conn.findAll(TEST_TABLE, JsonDocument).size(), 'There should have been no documents returned' + } + + static void byIdString(ThrowawayDatabase db) { + JsonDocument.load db + def doc = db.conn.findById TEST_TABLE, 'two', JsonDocument + assertTrue doc.isPresent(), 'The document should have been returned' + assertEquals 'two', doc.get().id, 'An incorrect document was returned' + } + + static void byIdNumber(ThrowawayDatabase db) { + Configuration.idField = 'key' + try { + db.conn.insert TEST_TABLE, new NumIdDocument(18, 'howdy') + assertTrue(db.conn.findById(TEST_TABLE, 18, NumIdDocument).isPresent(), + 'The document should have been returned') + } finally { + Configuration.idField = 'id' + } + } + + static void byIdNotFound(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse(db.conn.findById(TEST_TABLE, 'x', JsonDocument).isPresent(), + 'There should have been no document returned') + } + + static void byFieldsMatch(ThrowawayDatabase db) { + JsonDocument.load db + def docs = db.conn.findByFields(TEST_TABLE, + List.of(Field.any('value', List.of('blue', 'purple')), Field.exists('sub')), JsonDocument, + FieldMatch.ALL) + assertEquals 1, docs.size(), 'There should have been a document returned' + assertEquals 'four', docs[0].id, 'The incorrect document was returned' + } + + static void byFieldsMatchOrdered(ThrowawayDatabase db) { + JsonDocument.load db + def docs = db.conn.findByFields(TEST_TABLE, List.of(Field.equal('value', 'purple')), JsonDocument, null, + List.of(Field.named('id'))) + assertEquals 2, docs.size(), 'There should have been 2 documents returned' + assertEquals 'five|four', docIds(docs), 'The documents were not ordered correctly' + } + + static void byFieldsMatchNumIn(ThrowawayDatabase db) { + JsonDocument.load db + def docs = db.conn.findByFields TEST_TABLE, List.of(Field.any('numValue', List.of(2, 4, 6, 8))), JsonDocument + assertEquals 1, docs.size(), 'There should have been a document returned' + assertEquals 'three', docs[0].id, 'The incorrect document was returned' + } + + static void byFieldsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(0, db.conn.findByFields(TEST_TABLE, List.of(Field.greater('numValue', 100)), JsonDocument).size(), + 'There should have been no documents returned') + } + + static void byFieldsMatchInArray(ThrowawayDatabase db) { + ArrayDocument.testDocuments.forEach { db.conn.insert TEST_TABLE, it } + def docs = db.conn.findByFields(TEST_TABLE, List.of(Field.inArray('values', TEST_TABLE, List.of('c'))), + ArrayDocument) + assertEquals 2, docs.size(), 'There should have been two documents returned' + assertTrue(List.of('first', 'second').contains(docs[0].id), + "An incorrect document was returned (${docs[0].id})") + assertTrue(List.of('first', 'second').contains(docs[1].id), + "An incorrect document was returned (${docs[1].id})") + } + + static void byFieldsNoMatchInArray(ThrowawayDatabase db) { + ArrayDocument.testDocuments.forEach { db.conn.insert TEST_TABLE, it } + assertEquals(0, + db.conn.findByFields(TEST_TABLE, List.of(Field.inArray('values', TEST_TABLE, List.of('j'))), + ArrayDocument).size(), + 'There should have been no documents returned') + } + + static void byContainsMatch(ThrowawayDatabase db) { + JsonDocument.load db + def docs = db.conn.findByContains TEST_TABLE, Map.of('value', 'purple'), JsonDocument + assertEquals 2, docs.size(), 'There should have been 2 documents returned' + assertTrue List.of('four', 'five').contains(docs[0].id), "An incorrect document was returned (${docs[0].id})" + assertTrue List.of('four', 'five').contains(docs[1].id), "An incorrect document was returned (${docs[1].id})" + } + + static void byContainsMatchOrdered(ThrowawayDatabase db) { + JsonDocument.load db + def docs = db.conn.findByContains(TEST_TABLE, Map.of('sub', Map.of('foo', 'green')), JsonDocument, + List.of(Field.named('value'))) + assertEquals 2, docs.size(), 'There should have been 2 documents returned' + assertEquals 'two|four', docIds(docs), 'The documents were not ordered correctly' + } + + static void byContainsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(0, db.conn.findByContains(TEST_TABLE, Map.of('value', 'indigo'), JsonDocument).size(), + 'There should have been no documents returned') + } + + static void byJsonPathMatch(ThrowawayDatabase db) { + JsonDocument.load db + def docs = db.conn.findByJsonPath TEST_TABLE, '$.numValue ? (@ > 10)', JsonDocument + assertEquals 2, docs.size(), 'There should have been 2 documents returned' + assertTrue List.of('four', 'five').contains(docs[0].id), "An incorrect document was returned (${docs[0].id})" + assertTrue List.of('four', 'five').contains(docs[1].id), "An incorrect document was returned (${docs[1].id})" + } + + static void byJsonPathMatchOrdered(ThrowawayDatabase db) { + JsonDocument.load db + def docs = db.conn.findByJsonPath TEST_TABLE, '$.numValue ? (@ > 10)', JsonDocument, List.of(Field.named('id')) + assertEquals 2, docs.size(), 'There should have been 2 documents returned' + assertEquals 'five|four', docIds(docs), 'The documents were not ordered correctly' + } + + static void byJsonPathNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertEquals(0, db.conn.findByJsonPath(TEST_TABLE, '$.numValue ? (@ > 100)', JsonDocument).size(), + 'There should have been no documents returned') + } + + static void firstByFieldsMatchOne(ThrowawayDatabase db) { + JsonDocument.load db + def doc = db.conn.findFirstByFields TEST_TABLE, List.of(Field.equal('value', 'another')), JsonDocument + assertTrue doc.isPresent(), 'There should have been a document returned' + assertEquals 'two', doc.get().id, 'The incorrect document was returned' + } + + static void firstByFieldsMatchMany(ThrowawayDatabase db) { + JsonDocument.load db + def doc = db.conn.findFirstByFields TEST_TABLE, List.of(Field.equal('sub.foo', 'green')), JsonDocument + assertTrue doc.isPresent(), 'There should have been a document returned' + assertTrue List.of('two', 'four').contains(doc.get().id), "An incorrect document was returned (${doc.get().id})" + } + + static void firstByFieldsMatchOrdered(ThrowawayDatabase db) { + JsonDocument.load db + def doc = db.conn.findFirstByFields(TEST_TABLE, List.of(Field.equal('sub.foo', 'green')), JsonDocument, null, + List.of(Field.named('n:numValue DESC'))) + assertTrue doc.isPresent(), 'There should have been a document returned' + assertEquals 'four', doc.get().id, 'An incorrect document was returned' + } + + static void firstByFieldsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse(db.conn.findFirstByFields(TEST_TABLE, List.of(Field.equal('value', 'absent')), JsonDocument) + .isPresent(), + 'There should have been no document returned') + } + + static void firstByContainsMatchOne(ThrowawayDatabase db) { + JsonDocument.load db + def doc = db.conn.findFirstByContains TEST_TABLE, Map.of('value', 'FIRST!'), JsonDocument + assertTrue doc.isPresent(), 'There should have been a document returned' + assertEquals 'one', doc.get().id, 'An incorrect document was returned' + } + + static void firstByContainsMatchMany(ThrowawayDatabase db) { + JsonDocument.load db + def doc = db.conn.findFirstByContains TEST_TABLE, Map.of('value', 'purple'), JsonDocument + assertTrue doc.isPresent(), 'There should have been a document returned' + assertTrue(List.of('four', 'five').contains(doc.get().id), + "An incorrect document was returned (${doc.get().id})") + } + + static void firstByContainsMatchOrdered(ThrowawayDatabase db) { + JsonDocument.load db + def doc = db.conn.findFirstByContains(TEST_TABLE, Map.of('value', 'purple'), JsonDocument, + List.of(Field.named('sub.bar NULLS FIRST'))) + assertTrue doc.isPresent(), 'There should have been a document returned' + assertEquals 'five', doc.get().id, 'An incorrect document was returned' + } + + static void firstByContainsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse(db.conn.findFirstByContains(TEST_TABLE, Map.of('value', 'indigo'), JsonDocument).isPresent(), + 'There should have been no document returned') + } + + static void firstByJsonPathMatchOne(ThrowawayDatabase db) { + JsonDocument.load db + def doc = db.conn.findFirstByJsonPath TEST_TABLE, '$.numValue ? (@ == 10)', JsonDocument + assertTrue doc.isPresent(), 'There should have been a document returned' + assertEquals 'two', doc.get().id, 'An incorrect document was returned' + } + + static void firstByJsonPathMatchMany(ThrowawayDatabase db) { + JsonDocument.load db + def doc = db.conn.findFirstByJsonPath TEST_TABLE, '$.numValue ? (@ > 10)', JsonDocument + assertTrue doc.isPresent(), 'There should have been a document returned' + assertTrue(List.of('four', 'five').contains(doc.get().id), + "An incorrect document was returned (${doc.get().id})") + } + + static void firstByJsonPathMatchOrdered(ThrowawayDatabase db) { + JsonDocument.load db + def doc = db.conn.findFirstByJsonPath(TEST_TABLE, '$.numValue ? (@ > 10)', JsonDocument, + List.of(Field.named('id DESC'))) + assertTrue doc.isPresent(), 'There should have been a document returned' + assertEquals 'four', doc.get().id, 'An incorrect document was returned' + } + + static void firstByJsonPathNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse(db.conn.findFirstByJsonPath(TEST_TABLE, '$.numValue ? (@ > 100)', JsonDocument).isPresent(), + 'There should have been no document returned') + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/JacksonDocumentSerializer.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/JacksonDocumentSerializer.groovy new file mode 100644 index 0000000..cd9db60 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/JacksonDocumentSerializer.groovy @@ -0,0 +1,22 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import com.fasterxml.jackson.databind.ObjectMapper +import solutions.bitbadger.documents.DocumentSerializer + +/** + * A JSON serializer using Jackson's default options + */ +class JacksonDocumentSerializer implements DocumentSerializer { + + private def mapper = new ObjectMapper() + + @Override + def String serialize(TDoc document) { + return mapper.writeValueAsString(document) + } + + @Override + def TDoc deserialize(String json, Class clazz) { + return mapper.readValue(json, clazz) + } +} diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/JsonDocument.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/JsonDocument.groovy similarity index 71% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/JsonDocument.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/JsonDocument.groovy index 78ba92e..71594e8 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/JsonDocument.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/JsonDocument.groovy @@ -1,9 +1,6 @@ -package solutions.bitbadger.documents.groovy.support +package solutions.bitbadger.documents.groovy.tests.integration -import solutions.bitbadger.documents.jvm.Document -import solutions.bitbadger.documents.support.ThrowawayDatabase - -import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE class JsonDocument { String id @@ -26,6 +23,6 @@ class JsonDocument { new JsonDocument("five", "purple", 18)) static void load(ThrowawayDatabase db, String tableName = TEST_TABLE) { - testDocuments.forEach { Document.insert(tableName, it, db.conn) } + testDocuments.forEach { db.conn.insert(tableName, it) } } } diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/NumIdDocument.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/NumIdDocument.groovy new file mode 100644 index 0000000..a980ef2 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/NumIdDocument.groovy @@ -0,0 +1,11 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +class NumIdDocument { + int key + String value + + NumIdDocument(int key = 0, String value = "") { + this.key = key + this.value = value + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PatchFunctions.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PatchFunctions.groovy new file mode 100644 index 0000000..684a347 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PatchFunctions.groovy @@ -0,0 +1,75 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.Field + +import static org.junit.jupiter.api.Assertions.* +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +final class PatchFunctions { + + static void byIdMatch(ThrowawayDatabase db) { + JsonDocument.load db + db.conn.patchById TEST_TABLE, 'one', Map.of('numValue', 44) + def doc = db.conn.findById TEST_TABLE, 'one', JsonDocument + assertTrue doc.isPresent(), 'There should have been a document returned' + assertEquals 'one', doc.get().id, 'An incorrect document was returned' + assertEquals 44, doc.get().numValue, 'The document was not patched' + } + + static void byIdNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse db.conn.existsById(TEST_TABLE, 'forty-seven'), 'Document with ID "forty-seven" should not exist' + db.conn.patchById TEST_TABLE, 'forty-seven', Map.of('foo', 'green') // no exception = pass + } + + static void byFieldsMatch(ThrowawayDatabase db) { + JsonDocument.load db + db.conn.patchByFields TEST_TABLE, List.of(Field.equal('value', 'purple')), Map.of('numValue', 77) + assertEquals(2, db.conn.countByFields(TEST_TABLE, List.of(Field.equal('numValue', 77))), + 'There should have been 2 documents with numeric value 77') + } + + static void byFieldsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + def fields = List.of Field.equal('value', 'burgundy') + assertFalse db.conn.existsByFields(TEST_TABLE, fields), 'There should be no documents with value of "burgundy"' + db.conn.patchByFields TEST_TABLE, fields, Map.of('foo', 'green') // no exception = pass + } + + static void byContainsMatch(ThrowawayDatabase db) { + JsonDocument.load db + def contains = Map.of 'value', 'another' + db.conn.patchByContains TEST_TABLE, contains, Map.of('numValue', 12) + def doc = db.conn.findFirstByContains TEST_TABLE, contains, JsonDocument + assertTrue doc.isPresent(), 'There should have been a document returned' + assertEquals 'two', doc.get().id, 'The incorrect document was returned' + assertEquals 12, doc.get().numValue, 'The document was not updated' + } + + static void byContainsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + def contains = Map.of 'value', 'updated' + assertFalse db.conn.existsByContains(TEST_TABLE, contains), 'There should be no matching documents' + db.conn.patchByContains TEST_TABLE, contains, Map.of('sub.foo', 'green') // no exception = pass + } + + static void byJsonPathMatch(ThrowawayDatabase db) { + JsonDocument.load db + def path = '$.numValue ? (@ > 10)' + db.conn.patchByJsonPath TEST_TABLE, path, Map.of('value', 'blue') + def docs = db.conn.findByJsonPath TEST_TABLE, path, JsonDocument + assertEquals 2, docs.size(), 'There should have been two documents returned' + docs.forEach { + assertTrue List.of('four', 'five').contains(it.id), "An incorrect document was returned (${it.id})" + assertEquals 'blue', it.value, "The value for ID ${it.id} was incorrect" + } + } + + static void byJsonPathNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + def path = '$.numValue ? (@ > 100)' + assertFalse(db.conn.existsByJsonPath(TEST_TABLE, path), + 'There should be no documents with numeric values over 100') + db.conn.patchByJsonPath TEST_TABLE, path, Map.of('value', 'blue') // no exception = pass + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PgDB.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PgDB.groovy new file mode 100644 index 0000000..e60ad50 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PgDB.groovy @@ -0,0 +1,50 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.Configuration +import solutions.bitbadger.documents.Parameter +import solutions.bitbadger.documents.ParameterType +import solutions.bitbadger.documents.java.DocumentConfig +import solutions.bitbadger.documents.java.Results + +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +/** + * A wrapper for a throwaway PostgreSQL database + */ +class PgDB implements ThrowawayDatabase { + + PgDB() { + Configuration.setConnectionString connString('postgres') + Configuration.dbConn().withCloseable { it.customNonQuery "CREATE DATABASE $dbName" } + + Configuration.setConnectionString connString(dbName) + conn = Configuration.dbConn() + conn.ensureTable TEST_TABLE + + // Use a Jackson-based document serializer for testing + DocumentConfig.serializer = new JacksonDocumentSerializer() + } + + void close() { + conn.close() + Configuration.setConnectionString connString('postgres') + Configuration.dbConn().withCloseable { it.customNonQuery "DROP DATABASE $dbName" } + Configuration.setConnectionString null + } + + boolean dbObjectExists(String name) { + conn.customScalar('SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = :name) AS it', + List.of(new Parameter(':name', ParameterType.STRING, name)), Boolean, Results.&toExists) + } + + /** + * Create a connection string for the given database + * + * @param database The database to which the library should connect + * @return The connection string for the database + */ + private static String connString(String database) { + return "jdbc:postgresql://localhost/$database?user=postgres&password=postgres" + } + +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLCountIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLCountIT.groovy new file mode 100644 index 0000000..0cb222a --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLCountIT.groovy @@ -0,0 +1,53 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * PostgreSQL integration tests for the `Count` object / `count*` connection extension functions + */ +@DisplayName('Groovy | PostgreSQL: Count') +final class PostgreSQLCountIT { + + @Test + @DisplayName('all counts all documents') + void all() { + new PgDB().withCloseable CountFunctions.&all + } + + @Test + @DisplayName('byFields counts documents by a numeric value') + void byFieldsNumeric() { + new PgDB().withCloseable CountFunctions.&byFieldsNumeric + } + + @Test + @DisplayName('byFields counts documents by a alphanumeric value') + void byFieldsAlpha() { + new PgDB().withCloseable CountFunctions.&byFieldsAlpha + } + + @Test + @DisplayName('byContains counts documents when matches are found') + void byContainsMatch() { + new PgDB().withCloseable CountFunctions.&byContainsMatch + } + + @Test + @DisplayName('byContains counts documents when no matches are found') + void byContainsNoMatch() { + new PgDB().withCloseable CountFunctions.&byContainsNoMatch + } + + @Test + @DisplayName('byJsonPath counts documents when matches are found') + void byJsonPathMatch() { + new PgDB().withCloseable CountFunctions.&byJsonPathMatch + } + + @Test + @DisplayName('byJsonPath counts documents when no matches are found') + void byJsonPathNoMatch() { + new PgDB().withCloseable CountFunctions.&byJsonPathNoMatch + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLCustomIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLCustomIT.groovy new file mode 100644 index 0000000..cd7570d --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLCustomIT.groovy @@ -0,0 +1,53 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * PostgreSQL integration tests for the `Custom` object / `custom*` connection extension functions + */ +@DisplayName('Groovy | PostgreSQL: Custom') +final class PostgreSQLCustomIT { + + @Test + @DisplayName('list succeeds with empty list') + void listEmpty() { + new PgDB().withCloseable CustomFunctions.&listEmpty + } + + @Test + @DisplayName('list succeeds with a non-empty list') + void listAll() { + new PgDB().withCloseable CustomFunctions.&listAll + } + + @Test + @DisplayName('single succeeds when document not found') + void singleNone() { + new PgDB().withCloseable CustomFunctions.&singleNone + } + + @Test + @DisplayName('single succeeds when a document is found') + void singleOne() { + new PgDB().withCloseable CustomFunctions.&singleOne + } + + @Test + @DisplayName('nonQuery makes changes') + void nonQueryChanges() { + new PgDB().withCloseable CustomFunctions.&nonQueryChanges + } + + @Test + @DisplayName('nonQuery makes no changes when where clause matches nothing') + void nonQueryNoChanges() { + new PgDB().withCloseable CustomFunctions.&nonQueryNoChanges + } + + @Test + @DisplayName('scalar succeeds') + void scalar() { + new PgDB().withCloseable CustomFunctions.&scalar + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLDefinitionIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLDefinitionIT.groovy new file mode 100644 index 0000000..d5ea0e1 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLDefinitionIT.groovy @@ -0,0 +1,35 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions + */ +@DisplayName('Groovy | PostgreSQL: Definition') +final class PostgreSQLDefinitionIT { + + @Test + @DisplayName('ensureTable creates table and index') + void ensureTable() { + new PgDB().withCloseable DefinitionFunctions.&ensureTable + } + + @Test + @DisplayName('ensureFieldIndex creates an index') + void ensureFieldIndex() { + new PgDB().withCloseable DefinitionFunctions.&ensureFieldIndex + } + + @Test + @DisplayName('ensureDocumentIndex creates a full index') + void ensureDocumentIndexFull() { + new PgDB().withCloseable DefinitionFunctions.&ensureDocumentIndexFull + } + + @Test + @DisplayName('ensureDocumentIndex creates an optimized index') + void ensureDocumentIndexOptimized() { + new PgDB().withCloseable DefinitionFunctions.&ensureDocumentIndexOptimized + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLDeleteIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLDeleteIT.groovy new file mode 100644 index 0000000..d0474a7 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLDeleteIT.groovy @@ -0,0 +1,59 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions + */ +@DisplayName('Groovy | PostgreSQL: Delete') +final class PostgreSQLDeleteIT { + + @Test + @DisplayName('byId deletes a matching ID') + void byIdMatch() { + new PgDB().withCloseable DeleteFunctions.&byIdMatch + } + + @Test + @DisplayName('byId succeeds when no ID matches') + void byIdNoMatch() { + new PgDB().withCloseable DeleteFunctions.&byIdNoMatch + } + + @Test + @DisplayName('byFields deletes matching documents') + void byFieldsMatch() { + new PgDB().withCloseable DeleteFunctions.&byFieldsMatch + } + + @Test + @DisplayName('byFields succeeds when no documents match') + void byFieldsNoMatch() { + new PgDB().withCloseable DeleteFunctions.&byFieldsNoMatch + } + + @Test + @DisplayName('byContains deletes matching documents') + void byContainsMatch() { + new PgDB().withCloseable DeleteFunctions.&byContainsMatch + } + + @Test + @DisplayName('byContains succeeds when no documents match') + void byContainsNoMatch() { + new PgDB().withCloseable DeleteFunctions.&byContainsNoMatch + } + + @Test + @DisplayName('byJsonPath deletes matching documents') + void byJsonPathMatch() { + new PgDB().withCloseable DeleteFunctions.&byJsonPathMatch + } + + @Test + @DisplayName('byJsonPath succeeds when no documents match') + void byJsonPathNoMatch() { + new PgDB().withCloseable DeleteFunctions.&byJsonPathNoMatch + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLDocumentIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLDocumentIT.groovy new file mode 100644 index 0000000..6b885f1 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLDocumentIT.groovy @@ -0,0 +1,65 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions + */ +@DisplayName('Groovy | PostgreSQL: Document') +final class PostgreSQLDocumentIT { + + @Test + @DisplayName('insert works with default values') + void insertDefault() { + new PgDB().withCloseable DocumentFunctions.&insertDefault + } + + @Test + @DisplayName('insert fails with duplicate key') + void insertDupe() { + new PgDB().withCloseable DocumentFunctions.&insertDupe + } + + @Test + @DisplayName('insert succeeds with numeric auto IDs') + void insertNumAutoId() { + new PgDB().withCloseable DocumentFunctions.&insertNumAutoId + } + + @Test + @DisplayName('insert succeeds with UUID auto ID') + void insertUUIDAutoId() { + new PgDB().withCloseable DocumentFunctions.&insertUUIDAutoId + } + + @Test + @DisplayName('insert succeeds with random string auto ID') + void insertStringAutoId() { + new PgDB().withCloseable DocumentFunctions.&insertStringAutoId + } + + @Test + @DisplayName('save updates an existing document') + void saveMatch() { + new PgDB().withCloseable DocumentFunctions.&saveMatch + } + + @Test + @DisplayName('save inserts a new document') + void saveNoMatch() { + new PgDB().withCloseable DocumentFunctions.&saveNoMatch + } + + @Test + @DisplayName('update replaces an existing document') + void updateMatch() { + new PgDB().withCloseable DocumentFunctions.&updateMatch + } + + @Test + @DisplayName('update succeeds when no document exists') + void updateNoMatch() { + new PgDB().withCloseable DocumentFunctions.&updateNoMatch + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLExistsIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLExistsIT.groovy new file mode 100644 index 0000000..83877cd --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLExistsIT.groovy @@ -0,0 +1,59 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions + */ +@DisplayName('Groovy | PostgreSQL: Exists') +final class PostgreSQLExistsIT { + + @Test + @DisplayName('byId returns true when a document matches the ID') + void byIdMatch() { + new PgDB().withCloseable ExistsFunctions.&byIdMatch + } + + @Test + @DisplayName('byId returns false when no document matches the ID') + void byIdNoMatch() { + new PgDB().withCloseable ExistsFunctions.&byIdNoMatch + } + + @Test + @DisplayName('byFields returns true when documents match') + void byFieldsMatch() { + new PgDB().withCloseable ExistsFunctions.&byFieldsMatch + } + + @Test + @DisplayName('byFields returns false when no documents match') + void byFieldsNoMatch() { + new PgDB().withCloseable ExistsFunctions.&byFieldsNoMatch + } + + @Test + @DisplayName('byContains returns true when documents match') + void byContainsMatch() { + new PgDB().withCloseable ExistsFunctions.&byContainsMatch + } + + @Test + @DisplayName('byContains returns false when no documents match') + void byContainsNoMatch() { + new PgDB().withCloseable ExistsFunctions.&byContainsNoMatch + } + + @Test + @DisplayName('byJsonPath returns true when documents match') + void byJsonPathMatch() { + new PgDB().withCloseable ExistsFunctions.&byJsonPathMatch + } + + @Test + @DisplayName('byJsonPath returns false when no documents match') + void byJsonPathNoMatch() { + new PgDB().withCloseable ExistsFunctions.&byJsonPathNoMatch + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLFindIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLFindIT.groovy new file mode 100644 index 0000000..d59cffc --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLFindIT.groovy @@ -0,0 +1,203 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * PostgreSQL integration tests for the `Find` object / `find*` connection extension functions + */ +@DisplayName('Groovy | PostgreSQL: Find') +final class PostgreSQLFindIT { + + @Test + @DisplayName('all retrieves all documents') + void allDefault() { + new PgDB().withCloseable FindFunctions.&allDefault + } + + @Test + @DisplayName('all sorts data ascending') + void allAscending() { + new PgDB().withCloseable FindFunctions.&allAscending + } + + @Test + @DisplayName('all sorts data descending') + void allDescending() { + new PgDB().withCloseable FindFunctions.&allDescending + } + + @Test + @DisplayName('all sorts data numerically') + void allNumOrder() { + new PgDB().withCloseable FindFunctions.&allNumOrder + } + + @Test + @DisplayName('all succeeds with an empty table') + void allEmpty() { + new PgDB().withCloseable FindFunctions.&allEmpty + } + + @Test + @DisplayName('byId retrieves a document via a string ID') + void byIdString() { + new PgDB().withCloseable FindFunctions.&byIdString + } + + @Test + @DisplayName('byId retrieves a document via a numeric ID') + void byIdNumber() { + new PgDB().withCloseable FindFunctions.&byIdNumber + } + + @Test + @DisplayName('byId returns null when a matching ID is not found') + void byIdNotFound() { + new PgDB().withCloseable FindFunctions.&byIdNotFound + } + + @Test + @DisplayName('byFields retrieves matching documents') + void byFieldsMatch() { + new PgDB().withCloseable FindFunctions.&byFieldsMatch + } + + @Test + @DisplayName('byFields retrieves ordered matching documents') + void byFieldsMatchOrdered() { + new PgDB().withCloseable FindFunctions.&byFieldsMatchOrdered + } + + @Test + @DisplayName('byFields retrieves matching documents with a numeric IN clause') + void byFieldsMatchNumIn() { + new PgDB().withCloseable FindFunctions.&byFieldsMatchNumIn + } + + @Test + @DisplayName('byFields succeeds when no documents match') + void byFieldsNoMatch() { + new PgDB().withCloseable FindFunctions.&byFieldsNoMatch + } + + @Test + @DisplayName('byFields retrieves matching documents with an IN_ARRAY comparison') + void byFieldsMatchInArray() { + new PgDB().withCloseable FindFunctions.&byFieldsMatchInArray + } + + @Test + @DisplayName('byFields succeeds when no documents match an IN_ARRAY comparison') + void byFieldsNoMatchInArray() { + new PgDB().withCloseable FindFunctions.&byFieldsNoMatchInArray + } + + @Test + @DisplayName('byContains retrieves matching documents') + void byContainsMatch() { + new PgDB().withCloseable FindFunctions.&byContainsMatch + } + + @Test + @DisplayName('byContains retrieves ordered matching documents') + void byContainsMatchOrdered() { + new PgDB().withCloseable FindFunctions.&byContainsMatchOrdered + } + + @Test + @DisplayName('byContains succeeds when no documents match') + void byContainsNoMatch() { + new PgDB().withCloseable FindFunctions.&byContainsNoMatch + } + + @Test + @DisplayName('byJsonPath retrieves matching documents') + void byJsonPathMatch() { + new PgDB().withCloseable FindFunctions.&byJsonPathMatch + } + + @Test + @DisplayName('byJsonPath retrieves ordered matching documents') + void byJsonPathMatchOrdered() { + new PgDB().withCloseable FindFunctions.&byJsonPathMatchOrdered + } + + @Test + @DisplayName('byJsonPath succeeds when no documents match') + void byJsonPathNoMatch() { + new PgDB().withCloseable FindFunctions.&byJsonPathNoMatch + } + + @Test + @DisplayName('firstByFields retrieves a matching document') + void firstByFieldsMatchOne() { + new PgDB().withCloseable FindFunctions.&firstByFieldsMatchOne + } + + @Test + @DisplayName('firstByFields retrieves a matching document among many') + void firstByFieldsMatchMany() { + new PgDB().withCloseable FindFunctions.&firstByFieldsMatchMany + } + + @Test + @DisplayName('firstByFields retrieves a matching document among many (ordered)') + void firstByFieldsMatchOrdered() { + new PgDB().withCloseable FindFunctions.&firstByFieldsMatchOrdered + } + + @Test + @DisplayName('firstByFields returns null when no document matches') + void firstByFieldsNoMatch() { + new PgDB().withCloseable FindFunctions.&firstByFieldsNoMatch + } + + @Test + @DisplayName('firstByContains retrieves a matching document') + void firstByContainsMatchOne() { + new PgDB().withCloseable FindFunctions.&firstByContainsMatchOne + } + + @Test + @DisplayName('firstByContains retrieves a matching document among many') + void firstByContainsMatchMany() { + new PgDB().withCloseable FindFunctions.&firstByContainsMatchMany + } + + @Test + @DisplayName('firstByContains retrieves a matching document among many (ordered)') + void firstByContainsMatchOrdered() { + new PgDB().withCloseable FindFunctions.&firstByContainsMatchOrdered + } + + @Test + @DisplayName('firstByContains returns null when no document matches') + void firstByContainsNoMatch() { + new PgDB().withCloseable FindFunctions.&firstByContainsNoMatch + } + + @Test + @DisplayName('firstByJsonPath retrieves a matching document') + void firstByJsonPathMatchOne() { + new PgDB().withCloseable FindFunctions.&firstByJsonPathMatchOne + } + + @Test + @DisplayName('firstByJsonPath retrieves a matching document among many') + void firstByJsonPathMatchMany() { + new PgDB().withCloseable FindFunctions.&firstByJsonPathMatchMany + } + + @Test + @DisplayName('firstByJsonPath retrieves a matching document among many (ordered)') + void firstByJsonPathMatchOrdered() { + new PgDB().withCloseable FindFunctions.&firstByJsonPathMatchOrdered + } + + @Test + @DisplayName('firstByJsonPath returns null when no document matches') + void firstByJsonPathNoMatch() { + new PgDB().withCloseable FindFunctions.&firstByJsonPathNoMatch + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLPatchIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLPatchIT.groovy new file mode 100644 index 0000000..312e6dc --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLPatchIT.groovy @@ -0,0 +1,59 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions + */ +@DisplayName('Groovy | PostgreSQL: Patch') +final class PostgreSQLPatchIT { + + @Test + @DisplayName('byId patches an existing document') + void byIdMatch() { + new PgDB().withCloseable PatchFunctions.&byIdMatch + } + + @Test + @DisplayName('byId succeeds for a non-existent document') + void byIdNoMatch() { + new PgDB().withCloseable PatchFunctions.&byIdNoMatch + } + + @Test + @DisplayName('byFields patches matching document') + void byFieldsMatch() { + new PgDB().withCloseable PatchFunctions.&byFieldsMatch + } + + @Test + @DisplayName('byFields succeeds when no documents match') + void byFieldsNoMatch() { + new PgDB().withCloseable PatchFunctions.&byFieldsNoMatch + } + + @Test + @DisplayName('byContains patches matching document') + void byContainsMatch() { + new PgDB().withCloseable PatchFunctions.&byContainsMatch + } + + @Test + @DisplayName('byContains succeeds when no documents match') + void byContainsNoMatch() { + new PgDB().withCloseable PatchFunctions.&byContainsNoMatch + } + + @Test + @DisplayName('byJsonPath patches matching document') + void byJsonPathMatch() { + new PgDB().withCloseable PatchFunctions.&byJsonPathMatch + } + + @Test + @DisplayName('byJsonPath succeeds when no documents match') + void byJsonPathNoMatch() { + new PgDB().withCloseable PatchFunctions.&byJsonPathNoMatch + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLRemoveFieldsIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLRemoveFieldsIT.groovy new file mode 100644 index 0000000..6a4dac3 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/PostgreSQLRemoveFieldsIT.groovy @@ -0,0 +1,83 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions + */ +@DisplayName('Groovy | PostgreSQL: RemoveFields') +final class PostgreSQLRemoveFieldsIT { + + @Test + @DisplayName('byId removes fields from an existing document') + void byIdMatchFields() { + new PgDB().withCloseable RemoveFieldsFunctions.&byIdMatchFields + } + + @Test + @DisplayName('byId succeeds when fields do not exist on an existing document') + void byIdMatchNoFields() { + new PgDB().withCloseable RemoveFieldsFunctions.&byIdMatchNoFields + } + + @Test + @DisplayName('byId succeeds when no document exists') + void byIdNoMatch() { + new PgDB().withCloseable RemoveFieldsFunctions.&byIdNoMatch + } + + @Test + @DisplayName('byFields removes fields from matching documents') + void byFieldsMatchFields() { + new PgDB().withCloseable RemoveFieldsFunctions.&byFieldsMatchFields + } + + @Test + @DisplayName('byFields succeeds when fields do not exist on matching documents') + void byFieldsMatchNoFields() { + new PgDB().withCloseable RemoveFieldsFunctions.&byFieldsMatchNoFields + } + + @Test + @DisplayName('byFields succeeds when no matching documents exist') + void byFieldsNoMatch() { + new PgDB().withCloseable RemoveFieldsFunctions.&byFieldsNoMatch + } + + @Test + @DisplayName('byContains removes fields from matching documents') + void byContainsMatchFields() { + new PgDB().withCloseable RemoveFieldsFunctions.&byContainsMatchFields + } + + @Test + @DisplayName('byContains succeeds when fields do not exist on matching documents') + void byContainsMatchNoFields() { + new PgDB().withCloseable RemoveFieldsFunctions.&byContainsMatchNoFields + } + + @Test + @DisplayName('byContains succeeds when no matching documents exist') + void byContainsNoMatch() { + new PgDB().withCloseable RemoveFieldsFunctions.&byContainsNoMatch + } + + @Test + @DisplayName('byJsonPath removes fields from matching documents') + void byJsonPathMatchFields() { + new PgDB().withCloseable RemoveFieldsFunctions.&byJsonPathMatchFields + } + + @Test + @DisplayName('byJsonPath succeeds when fields do not exist on matching documents') + void byJsonPathMatchNoFields() { + new PgDB().withCloseable RemoveFieldsFunctions.&byJsonPathMatchNoFields + } + + @Test + @DisplayName('byJsonPath succeeds when no matching documents exist') + void byJsonPathNoMatch() { + new PgDB().withCloseable RemoveFieldsFunctions.&byJsonPathNoMatch + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/RemoveFieldsFunctions.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/RemoveFieldsFunctions.groovy new file mode 100644 index 0000000..8182d62 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/RemoveFieldsFunctions.groovy @@ -0,0 +1,104 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.Field + +import static org.junit.jupiter.api.Assertions.* +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +final class RemoveFieldsFunctions { + + static void byIdMatchFields(ThrowawayDatabase db) { + JsonDocument.load db + db.conn.removeFieldsById TEST_TABLE, 'two', List.of('sub', 'value') + def doc = db.conn.findById TEST_TABLE, 'two', JsonDocument + assertTrue doc.isPresent(), 'There should have been a document returned' + assertEquals '', doc.get().value, 'The value should have been empty' + assertNull doc.get().sub, 'The sub-document should have been removed' + } + + static void byIdMatchNoFields(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse db.conn.existsByFields(TEST_TABLE, List.of(Field.exists('a_field_that_does_not_exist'))) + db.conn.removeFieldsById TEST_TABLE, 'one', List.of('a_field_that_does_not_exist') // no exception = pass + } + + static void byIdNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse db.conn.existsById(TEST_TABLE, 'fifty') + db.conn.removeFieldsById TEST_TABLE, 'fifty', List.of('sub') // no exception = pass + } + + static void byFieldsMatchFields(ThrowawayDatabase db) { + JsonDocument.load db + def fields = List.of Field.equal('numValue', 17) + db.conn.removeFieldsByFields TEST_TABLE, fields, List.of('sub') + def doc = db.conn.findFirstByFields TEST_TABLE, fields, JsonDocument + assertTrue doc.isPresent(), 'The document should have been returned' + assertEquals 'four', doc.get().id, 'An incorrect document was returned' + assertNull doc.get().sub, 'The sub-document should have been removed' + } + + static void byFieldsMatchNoFields(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse db.conn.existsByFields(TEST_TABLE, List.of(Field.exists('nada'))) + db.conn.removeFieldsByFields TEST_TABLE, List.of(Field.equal('numValue', 17)), List.of('nada') // no exn = pass + } + + static void byFieldsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + def fields = List.of Field.notEqual('missing', 'nope') + assertFalse db.conn.existsByFields(TEST_TABLE, fields) + db.conn.removeFieldsByFields TEST_TABLE, fields, List.of('value') // no exception = pass + } + + static void byContainsMatchFields(ThrowawayDatabase db) { + JsonDocument.load db + def criteria = Map.of('sub', Map.of('foo', 'green')) + db.conn.removeFieldsByContains TEST_TABLE, criteria, List.of('value') + def docs = db.conn.findByContains TEST_TABLE, criteria, JsonDocument + assertEquals 2, docs.size(), 'There should have been 2 documents returned' + docs.forEach { + assertTrue List.of('two', 'four').contains(it.id), "An incorrect document was returned (${it.id})" + assertEquals '', it.value, 'The value should have been empty' + } + } + + static void byContainsMatchNoFields(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse db.conn.existsByFields(TEST_TABLE, List.of(Field.exists('invalid_field'))) + db.conn.removeFieldsByContains TEST_TABLE, Map.of('sub', Map.of('foo', 'green')), List.of('invalid_field') + // no exception = pass + } + + static void byContainsNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + def contains = Map.of 'value', 'substantial' + assertFalse db.conn.existsByContains(TEST_TABLE, contains) + db.conn.removeFieldsByContains TEST_TABLE, contains, List.of('numValue') + } + + static void byJsonPathMatchFields(ThrowawayDatabase db) { + JsonDocument.load db + def path = '$.value ? (@ == "purple")' + db.conn.removeFieldsByJsonPath TEST_TABLE, path, List.of('sub') + def docs = db.conn.findByJsonPath TEST_TABLE, path, JsonDocument + assertEquals 2, docs.size(), 'There should have been 2 documents returned' + docs.forEach { + assertTrue List.of('four', 'five').contains(it.id), "An incorrect document was returned (${it.id})" + assertNull it.sub, 'The sub-document should have been removed' + } + } + + static void byJsonPathMatchNoFields(ThrowawayDatabase db) { + JsonDocument.load db + assertFalse db.conn.existsByFields(TEST_TABLE, List.of(Field.exists('submarine'))) + db.conn.removeFieldsByJsonPath TEST_TABLE, '$.value ? (@ == "purple")', List.of('submarine') // no exn = pass + } + + static void byJsonPathNoMatch(ThrowawayDatabase db) { + JsonDocument.load db + def path = '$.value ? (@ == "mauve")' + assertFalse db.conn.existsByJsonPath(TEST_TABLE, path) + db.conn.removeFieldsByJsonPath TEST_TABLE, path, List.of('value') // no exception = pass + } +} diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/sqlite/CountIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteCountIT.groovy similarity index 61% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/sqlite/CountIT.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteCountIT.groovy index 0dd4dad..8afab08 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/sqlite/CountIT.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteCountIT.groovy @@ -1,50 +1,48 @@ -package solutions.bitbadger.documents.groovy.jvm.integration.sqlite +package solutions.bitbadger.documents.groovy.tests.integration import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import solutions.bitbadger.documents.DocumentException -import solutions.bitbadger.documents.groovy.jvm.integration.common.CountFunctions -import solutions.bitbadger.documents.jvm.integration.sqlite.SQLiteDB import static org.junit.jupiter.api.Assertions.assertThrows /** * SQLite integration tests for the `Count` object / `count*` connection extension functions */ -@DisplayName("JVM | Groovy | SQLite: Count") -class CountIT { +@DisplayName("Groovy | SQLite: Count") +final class SQLiteCountIT { @Test @DisplayName("all counts all documents") void all() { - new SQLiteDB().withCloseable { CountFunctions.all(it) } + new SQLiteDB().withCloseable CountFunctions.&all } @Test @DisplayName("byFields counts documents by a numeric value") void byFieldsNumeric() { - new SQLiteDB().withCloseable { CountFunctions.byFieldsNumeric(it) } + new SQLiteDB().withCloseable CountFunctions.&byFieldsNumeric } @Test @DisplayName("byFields counts documents by a alphanumeric value") void byFieldsAlpha() { - new SQLiteDB().withCloseable { CountFunctions.byFieldsAlpha(it) } + new SQLiteDB().withCloseable CountFunctions.&byFieldsAlpha } @Test @DisplayName("byContains fails") - void byContainsMatch() { + void byContainsFails() { new SQLiteDB().withCloseable { db -> - assertThrows(DocumentException) { CountFunctions.byContainsMatch(db) } + assertThrows(DocumentException) { CountFunctions.byContainsMatch db } } } @Test @DisplayName("byJsonPath fails") - void byJsonPathMatch() { + void byJsonPathFails() { new SQLiteDB().withCloseable { db -> - assertThrows(DocumentException) { CountFunctions.byJsonPathMatch(db) } + assertThrows(DocumentException) { CountFunctions.byJsonPathMatch db } } } } diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteCustomIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteCustomIT.groovy new file mode 100644 index 0000000..05d9f24 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteCustomIT.groovy @@ -0,0 +1,53 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * SQLite integration tests for the `Custom` object / `custom*` connection extension functions + */ +@DisplayName('Groovy | SQLite: Custom') +final class SQLiteCustomIT { + + @Test + @DisplayName('list succeeds with empty list') + void listEmpty() { + new SQLiteDB().withCloseable CustomFunctions.&listEmpty + } + + @Test + @DisplayName('list succeeds with a non-empty list') + void listAll() { + new SQLiteDB().withCloseable CustomFunctions.&listAll + } + + @Test + @DisplayName('single succeeds when document not found') + void singleNone() { + new SQLiteDB().withCloseable CustomFunctions.&singleNone + } + + @Test + @DisplayName('single succeeds when a document is found') + void singleOne() { + new SQLiteDB().withCloseable CustomFunctions.&singleOne + } + + @Test + @DisplayName('nonQuery makes changes') + void nonQueryChanges() { + new SQLiteDB().withCloseable CustomFunctions.&nonQueryChanges + } + + @Test + @DisplayName('nonQuery makes no changes when where clause matches nothing') + void nonQueryNoChanges() { + new SQLiteDB().withCloseable CustomFunctions.&nonQueryNoChanges + } + + @Test + @DisplayName('scalar succeeds') + void scalar() { + new SQLiteDB().withCloseable CustomFunctions.&scalar + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDB.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDB.groovy new file mode 100644 index 0000000..580f3fc --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDB.groovy @@ -0,0 +1,35 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.Configuration +import solutions.bitbadger.documents.Parameter +import solutions.bitbadger.documents.ParameterType +import solutions.bitbadger.documents.java.DocumentConfig +import solutions.bitbadger.documents.java.Results + +import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE + +/** + * A wrapper for a throwaway SQLite database + */ +class SQLiteDB implements ThrowawayDatabase { + + SQLiteDB() { + Configuration.setConnectionString "jdbc:sqlite:${dbName}.db" + conn = Configuration.dbConn() + conn.ensureTable TEST_TABLE + + // Use a Jackson-based document serializer for testing + DocumentConfig.serializer = new JacksonDocumentSerializer() + } + + void close() { + conn.close() + new File("${dbName}.db").delete() + Configuration.setConnectionString null + } + + boolean dbObjectExists(String name) { + conn.customScalar("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE name = :name) AS it", + List.of(new Parameter(":name", ParameterType.STRING, name)), Boolean, Results.&toExists) + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDefinitionIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDefinitionIT.groovy new file mode 100644 index 0000000..1e03d79 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDefinitionIT.groovy @@ -0,0 +1,42 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.DocumentException + +import static org.junit.jupiter.api.Assertions.assertThrows + +/** + * SQLite integration tests for the `Definition` object / `ensure*` connection extension functions + */ +@DisplayName('Groovy | SQLite: Definition') +final class SQLiteDefinitionIT { + + @Test + @DisplayName('ensureTable creates table and index') + void ensureTable() { + new SQLiteDB().withCloseable DefinitionFunctions.&ensureTable + } + + @Test + @DisplayName('ensureFieldIndex creates an index') + void ensureFieldIndex() { + new SQLiteDB().withCloseable DefinitionFunctions.&ensureFieldIndex + } + + @Test + @DisplayName('ensureDocumentIndex fails for full index') + void ensureDocumentIndexFull() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { DefinitionFunctions::ensureDocumentIndexFull db } + } + } + + @Test + @DisplayName('ensureDocumentIndex fails for optimized index') + void ensureDocumentIndexOptimized() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { DefinitionFunctions::ensureDocumentIndexOptimized db } + } + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDeleteIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDeleteIT.groovy new file mode 100644 index 0000000..eb08f3a --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDeleteIT.groovy @@ -0,0 +1,54 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.DocumentException + +import static org.junit.jupiter.api.Assertions.assertThrows + +/** + * SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions + */ +@DisplayName('Groovy | SQLite: Delete') +final class SQLiteDeleteIT { + + @Test + @DisplayName('byId deletes a matching ID') + void byIdMatch() { + new SQLiteDB().withCloseable DeleteFunctions.&byIdMatch + } + + @Test + @DisplayName('byId succeeds when no ID matches') + void byIdNoMatch() { + new SQLiteDB().withCloseable DeleteFunctions.&byIdNoMatch + } + + @Test + @DisplayName('byFields deletes matching documents') + void byFieldsMatch() { + new SQLiteDB().withCloseable DeleteFunctions.&byFieldsMatch + } + + @Test + @DisplayName('byFields succeeds when no documents match') + void byFieldsNoMatch() { + new SQLiteDB().withCloseable DeleteFunctions.&byFieldsNoMatch + } + + @Test + @DisplayName('byContains fails') + void byContainsFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { DeleteFunctions.byContainsMatch db } + } + } + + @Test + @DisplayName('byJsonPath fails') + void byJsonPathFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { DeleteFunctions.byJsonPathMatch db } + } + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDocumentIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDocumentIT.groovy new file mode 100644 index 0000000..2d919c3 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteDocumentIT.groovy @@ -0,0 +1,65 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions + */ +@DisplayName('Groovy | SQLite: Document') +final class SQLiteDocumentIT { + + @Test + @DisplayName('insert works with default values') + void insertDefault() { + new SQLiteDB().withCloseable DocumentFunctions.&insertDefault + } + + @Test + @DisplayName('insert fails with duplicate key') + void insertDupe() { + new SQLiteDB().withCloseable DocumentFunctions.&insertDupe + } + + @Test + @DisplayName('insert succeeds with numeric auto IDs') + void insertNumAutoId() { + new SQLiteDB().withCloseable DocumentFunctions.&insertNumAutoId + } + + @Test + @DisplayName('insert succeeds with UUID auto ID') + void insertUUIDAutoId() { + new SQLiteDB().withCloseable DocumentFunctions.&insertUUIDAutoId + } + + @Test + @DisplayName('insert succeeds with random string auto ID') + void insertStringAutoId() { + new SQLiteDB().withCloseable DocumentFunctions.&insertStringAutoId + } + + @Test + @DisplayName('save updates an existing document') + void saveMatch() { + new SQLiteDB().withCloseable DocumentFunctions.&saveMatch + } + + @Test + @DisplayName('save inserts a new document') + void saveNoMatch() { + new SQLiteDB().withCloseable DocumentFunctions.&saveNoMatch + } + + @Test + @DisplayName('update replaces an existing document') + void updateMatch() { + new SQLiteDB().withCloseable DocumentFunctions.&updateMatch + } + + @Test + @DisplayName('update succeeds when no document exists') + void updateNoMatch() { + new SQLiteDB().withCloseable DocumentFunctions.&updateNoMatch + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteExistsIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteExistsIT.groovy new file mode 100644 index 0000000..9915afb --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteExistsIT.groovy @@ -0,0 +1,54 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.DocumentException + +import static org.junit.jupiter.api.Assertions.assertThrows + +/** + * SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions + */ +@DisplayName('Groovy | SQLite: Exists') +final class SQLiteExistsIT { + + @Test + @DisplayName('byId returns true when a document matches the ID') + void byIdMatch() { + new SQLiteDB().withCloseable ExistsFunctions.&byIdMatch + } + + @Test + @DisplayName('byId returns false when no document matches the ID') + void byIdNoMatch() { + new SQLiteDB().withCloseable ExistsFunctions.&byIdNoMatch + } + + @Test + @DisplayName('byFields returns true when documents match') + void byFieldsMatch() { + new SQLiteDB().withCloseable ExistsFunctions.&byFieldsMatch + } + + @Test + @DisplayName('byFields returns false when no documents match') + void byFieldsNoMatch() { + new SQLiteDB().withCloseable ExistsFunctions.&byFieldsNoMatch + } + + @Test + @DisplayName('byContains fails') + void byContainsFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { ExistsFunctions.byContainsMatch db } + } + } + + @Test + @DisplayName('byJsonPath fails') + void byJsonPathFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { ExistsFunctions.byJsonPathMatch db } + } + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteFindIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteFindIT.groovy new file mode 100644 index 0000000..1c4da1c --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteFindIT.groovy @@ -0,0 +1,154 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.DocumentException + +import static org.junit.jupiter.api.Assertions.assertThrows + +/** + * SQLite integration tests for the `Find` object / `find*` connection extension functions + */ +@DisplayName('Groovy | SQLite: Find') +final class SQLiteFindIT { + + @Test + @DisplayName('all retrieves all documents') + void allDefault() { + new SQLiteDB().withCloseable FindFunctions.&allDefault + } + + @Test + @DisplayName('all sorts data ascending') + void allAscending() { + new SQLiteDB().withCloseable FindFunctions.&allAscending + } + + @Test + @DisplayName('all sorts data descending') + void allDescending() { + new SQLiteDB().withCloseable FindFunctions.&allDescending + } + + @Test + @DisplayName('all sorts data numerically') + void allNumOrder() { + new SQLiteDB().withCloseable FindFunctions.&allNumOrder + } + + @Test + @DisplayName('all succeeds with an empty table') + void allEmpty() { + new SQLiteDB().withCloseable FindFunctions.&allEmpty + } + + @Test + @DisplayName('byId retrieves a document via a string ID') + void byIdString() { + new SQLiteDB().withCloseable FindFunctions.&byIdString + } + + @Test + @DisplayName('byId retrieves a document via a numeric ID') + void byIdNumber() { + new SQLiteDB().withCloseable FindFunctions.&byIdNumber + } + + @Test + @DisplayName('byId returns null when a matching ID is not found') + void byIdNotFound() { + new SQLiteDB().withCloseable FindFunctions.&byIdNotFound + } + + @Test + @DisplayName('byFields retrieves matching documents') + void byFieldsMatch() { + new SQLiteDB().withCloseable FindFunctions.&byFieldsMatch + } + + @Test + @DisplayName('byFields retrieves ordered matching documents') + void byFieldsMatchOrdered() { + new SQLiteDB().withCloseable FindFunctions.&byFieldsMatchOrdered + } + + @Test + @DisplayName('byFields retrieves matching documents with a numeric IN clause') + void byFieldsMatchNumIn() { + new SQLiteDB().withCloseable FindFunctions.&byFieldsMatchNumIn + } + + @Test + @DisplayName('byFields succeeds when no documents match') + void byFieldsNoMatch() { + new SQLiteDB().withCloseable FindFunctions.&byFieldsNoMatch + } + + @Test + @DisplayName('byFields retrieves matching documents with an IN_ARRAY comparison') + void byFieldsMatchInArray() { + new SQLiteDB().withCloseable FindFunctions.&byFieldsMatchInArray + } + + @Test + @DisplayName('byFields succeeds when no documents match an IN_ARRAY comparison') + void byFieldsNoMatchInArray() { + new SQLiteDB().withCloseable FindFunctions.&byFieldsNoMatchInArray + } + + @Test + @DisplayName('byContains fails') + void byContainsFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { FindFunctions.byContainsMatch db } + } + } + + @Test + @DisplayName('byJsonPath fails') + void byJsonPathFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { FindFunctions.byJsonPathMatch db } + } + } + + @Test + @DisplayName('firstByFields retrieves a matching document') + void firstByFieldsMatchOne() { + new SQLiteDB().withCloseable FindFunctions.&firstByFieldsMatchOne + } + + @Test + @DisplayName('firstByFields retrieves a matching document among many') + void firstByFieldsMatchMany() { + new SQLiteDB().withCloseable FindFunctions.&firstByFieldsMatchMany + } + + @Test + @DisplayName('firstByFields retrieves a matching document among many (ordered)') + void firstByFieldsMatchOrdered() { + new SQLiteDB().withCloseable FindFunctions.&firstByFieldsMatchOrdered + } + + @Test + @DisplayName('firstByFields returns null when no document matches') + void firstByFieldsNoMatch() { + new SQLiteDB().withCloseable FindFunctions.&firstByFieldsNoMatch + } + + @Test + @DisplayName('firstByContains fails') + void firstByContainsFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { FindFunctions.firstByContainsMatchOne db } + } + } + + @Test + @DisplayName('firstByJsonPath fails') + void firstByJsonPathFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { FindFunctions.firstByJsonPathMatchOne db } + } + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLitePatchIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLitePatchIT.groovy new file mode 100644 index 0000000..9638cc3 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLitePatchIT.groovy @@ -0,0 +1,54 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.DocumentException + +import static org.junit.jupiter.api.Assertions.assertThrows + +/** + * SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions + */ +@DisplayName('Groovy | SQLite: Patch') +final class SQLitePatchIT { + + @Test + @DisplayName('byId patches an existing document') + void byIdMatch() { + new SQLiteDB().withCloseable PatchFunctions.&byIdMatch + } + + @Test + @DisplayName('byId succeeds for a non-existent document') + void byIdNoMatch() { + new SQLiteDB().withCloseable PatchFunctions.&byIdNoMatch + } + + @Test + @DisplayName('byFields patches matching document') + void byFieldsMatch() { + new SQLiteDB().withCloseable PatchFunctions.&byFieldsMatch + } + + @Test + @DisplayName('byFields succeeds when no documents match') + void byFieldsNoMatch() { + new SQLiteDB().withCloseable PatchFunctions.&byFieldsNoMatch + } + + @Test + @DisplayName('byContains fails') + void byContainsFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { PatchFunctions.byContainsMatch db } + } + } + + @Test + @DisplayName('byJsonPath fails') + void byJsonPathFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { PatchFunctions.byJsonPathMatch db } + } + } +} diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteRemoveFieldsIT.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteRemoveFieldsIT.groovy new file mode 100644 index 0000000..2f0fa65 --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SQLiteRemoveFieldsIT.groovy @@ -0,0 +1,66 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.DocumentException + +import static org.junit.jupiter.api.Assertions.assertThrows + +/** + * SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions + */ +@DisplayName('Groovy | SQLite: RemoveFields') +final class SQLiteRemoveFieldsIT { + + @Test + @DisplayName('byId removes fields from an existing document') + void byIdMatchFields() { + new SQLiteDB().withCloseable RemoveFieldsFunctions.&byIdMatchFields + } + + @Test + @DisplayName('byId succeeds when fields do not exist on an existing document') + void byIdMatchNoFields() { + new SQLiteDB().withCloseable RemoveFieldsFunctions.&byIdMatchNoFields + } + + @Test + @DisplayName('byId succeeds when no document exists') + void byIdNoMatch() { + new SQLiteDB().withCloseable RemoveFieldsFunctions.&byIdNoMatch + } + + @Test + @DisplayName('byFields removes fields from matching documents') + void byFieldsMatchFields() { + new SQLiteDB().withCloseable RemoveFieldsFunctions.&byFieldsMatchFields + } + + @Test + @DisplayName('byFields succeeds when fields do not exist on matching documents') + void byFieldsMatchNoFields() { + new SQLiteDB().withCloseable RemoveFieldsFunctions.&byFieldsMatchNoFields + } + + @Test + @DisplayName('byFields succeeds when no matching documents exist') + void byFieldsNoMatch() { + new SQLiteDB().withCloseable RemoveFieldsFunctions.&byFieldsNoMatch + } + + @Test + @DisplayName('byContains fails') + void byContainsFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { RemoveFieldsFunctions.byContainsMatchFields db } + } + } + + @Test + @DisplayName('byJsonPath fails') + void byJsonPathFails() { + new SQLiteDB().withCloseable { db -> + assertThrows(DocumentException) { RemoveFieldsFunctions.byJsonPathMatchFields db } + } + } +} diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/SubDocument.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SubDocument.groovy similarity index 71% rename from src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/SubDocument.groovy rename to src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SubDocument.groovy index 744e498..a35722c 100644 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/support/SubDocument.groovy +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/SubDocument.groovy @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.groovy.support +package solutions.bitbadger.documents.groovy.tests.integration class SubDocument { String foo diff --git a/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/ThrowawayDatabase.groovy b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/ThrowawayDatabase.groovy new file mode 100644 index 0000000..0aaf20b --- /dev/null +++ b/src/groovy/src/test/groovy/solutions/bitbadger/documents/groovy/tests/integration/ThrowawayDatabase.groovy @@ -0,0 +1,24 @@ +package solutions.bitbadger.documents.groovy.tests.integration + +import solutions.bitbadger.documents.AutoId +import java.sql.Connection + +/** + * Common trait for PostgreSQL and SQLite throwaway databases + */ +trait ThrowawayDatabase implements AutoCloseable { + + /** The database connection for the throwaway database */ + Connection conn + + /** + * Determine if a database object exists + * + * @param name The name of the object whose existence should be checked + * @return True if the object exists, false if not + */ + abstract boolean dbObjectExists(String name) + + /** The name for the throwaway database */ + String dbName = "throwaway_${AutoId.generateRandomString(8)}" +} diff --git a/src/groovy/src/test/java/module-info.java b/src/groovy/src/test/java/module-info.java new file mode 100644 index 0000000..ec115cb --- /dev/null +++ b/src/groovy/src/test/java/module-info.java @@ -0,0 +1,15 @@ +module solutions.bitbadger.documents.groovy.tests { + requires solutions.bitbadger.documents.core; + requires solutions.bitbadger.documents.groovy; + requires com.fasterxml.jackson.databind; + requires java.desktop; + requires java.sql; + requires org.apache.groovy; + requires org.junit.jupiter.api; + + exports solutions.bitbadger.documents.groovy.tests; + exports solutions.bitbadger.documents.groovy.tests.integration; + + opens solutions.bitbadger.documents.groovy.tests; + opens solutions.bitbadger.documents.groovy.tests.integration; +} diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/common/CountFunctions.groovy b/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/common/CountFunctions.groovy deleted file mode 100644 index 673a7e4..0000000 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/common/CountFunctions.groovy +++ /dev/null @@ -1,53 +0,0 @@ -package solutions.bitbadger.documents.groovy.jvm.integration.common - -import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.groovy.support.JsonDocument -import solutions.bitbadger.documents.support.ThrowawayDatabase - -import static solutions.bitbadger.documents.extensions.ConnExt.* -import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE -import static org.junit.jupiter.api.Assertions.* - -class CountFunctions { - - static void all(ThrowawayDatabase db) { - JsonDocument.load(db) - assertEquals 5L, countAll(db.conn, TEST_TABLE), 'There should have been 5 documents in the table' - } - - static void byFieldsNumeric(ThrowawayDatabase db) { - JsonDocument.load(db) - assertEquals(3L, countByFields(db.conn, TEST_TABLE, List.of(Field.between('numValue', 10, 20))), - 'There should have been 3 matching documents') - } - - static void byFieldsAlpha(ThrowawayDatabase db) { - JsonDocument.load(db) - assertEquals(1L, countByFields(db.conn, TEST_TABLE, List.of(Field.between('value', 'aardvark', 'apple'))), - 'There should have been 1 matching document') - } - - static void byContainsMatch(ThrowawayDatabase db) { - JsonDocument.load(db) - assertEquals(2L, countByContains(db.conn, TEST_TABLE, Map.of('value', 'purple')), - 'There should have been 2 matching documents') - } - - static void byContainsNoMatch(ThrowawayDatabase db) { - JsonDocument.load(db) - assertEquals(0L, countByContains(db.conn, TEST_TABLE, Map.of('value', 'magenta')), - 'There should have been no matching documents') - } - - static void byJsonPathMatch(ThrowawayDatabase db) { - JsonDocument.load(db) - assertEquals(2L, countByJsonPath(db.conn, TEST_TABLE, '$.numValue ? (@ < 5)'), - 'There should have been 2 matching documents') - } - - static void byJsonPathNoMatch(ThrowawayDatabase db) { - JsonDocument.load(db) - assertEquals(0L, countByJsonPath(db.conn, TEST_TABLE, '$.numValue ? (@ > 100)'), - 'There should have been no matching documents') - } -} diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/common/CustomFunctions.groovy b/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/common/CustomFunctions.groovy deleted file mode 100644 index 7147a8c..0000000 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/common/CustomFunctions.groovy +++ /dev/null @@ -1,72 +0,0 @@ -package solutions.bitbadger.documents.groovy.jvm.integration.common - -import solutions.bitbadger.documents.Configuration -import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.Parameter -import solutions.bitbadger.documents.ParameterType -import solutions.bitbadger.documents.groovy.support.JsonDocument -import solutions.bitbadger.documents.jvm.Results -import solutions.bitbadger.documents.query.CountQuery -import solutions.bitbadger.documents.query.DeleteQuery -import solutions.bitbadger.documents.query.FindQuery -import solutions.bitbadger.documents.support.ThrowawayDatabase - -import static org.junit.jupiter.api.Assertions.* -import static solutions.bitbadger.documents.extensions.ConnExt.* -import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE - -class CustomFunctions { - - static void listEmpty(ThrowawayDatabase db) { - JsonDocument.load(db) - deleteByFields(db.conn, TEST_TABLE, List.of(Field.exists(Configuration.idField))) - def result = customList(db.conn, FindQuery.all(TEST_TABLE), List.of(), JsonDocument.class, Results.&fromData) - assertEquals(0, result.size(), "There should have been no results") - } - - static void listAll(ThrowawayDatabase db) { - JsonDocument.load(db) - def result = customList(db.conn, FindQuery.all(TEST_TABLE), List.of(), JsonDocument.class, Results.&fromData) - assertEquals(5, result.size(), "There should have been 5 results") - } - - static void singleNone(ThrowawayDatabase db) { - assertNull(customSingle(db.conn, FindQuery.all(TEST_TABLE), List.of(), JsonDocument.class, Results.&fromData), - "There should not have been a document returned") - - } - - static void singleOne(ThrowawayDatabase db) { - JsonDocument.load(db) - assertNotNull( - customSingle(db.conn, FindQuery.all(TEST_TABLE), List.of(), JsonDocument.class, Results.&fromData), - "There should not have been a document returned") - } - - static void nonQueryChanges(ThrowawayDatabase db) { - JsonDocument.load(db) - assertEquals(5L, customScalar(db.conn, CountQuery.all(TEST_TABLE), List.of(), Long.class, Results.&toCount), - "There should have been 5 documents in the table") - customNonQuery(db.conn, "DELETE FROM $TEST_TABLE") - assertEquals(0L, customScalar(db.conn, CountQuery.all(TEST_TABLE), List.of(), Long.class, Results.&toCount), - "There should have been no documents in the table") - } - - static void nonQueryNoChanges(ThrowawayDatabase db) { - JsonDocument.load(db) - assertEquals(5L, customScalar(db.conn, CountQuery.all(TEST_TABLE), List.of(), Long.class, Results.&toCount), - "There should have been 5 documents in the table") - customNonQuery(db.conn, DeleteQuery.byId(TEST_TABLE, "eighty-two"), - List.of(new Parameter(":id", ParameterType.STRING, "eighty-two"))) - assertEquals(5L, customScalar(db.conn, CountQuery.all(TEST_TABLE), List.of(), Long.class, Results.&toCount), - "There should still have been 5 documents in the table") - } - - static void scalar(ThrowawayDatabase db) { - JsonDocument.load(db) - assertEquals(3L, - customScalar(db.conn, "SELECT 3 AS it FROM $TEST_TABLE LIMIT 1", List.of(), Long.class, - Results.&toCount), - "The number 3 should have been returned") - } -} diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/postgresql/CountIT.groovy b/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/postgresql/CountIT.groovy deleted file mode 100644 index 0763644..0000000 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/postgresql/CountIT.groovy +++ /dev/null @@ -1,55 +0,0 @@ -package solutions.bitbadger.documents.groovy.jvm.integration.postgresql - -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import solutions.bitbadger.documents.groovy.jvm.integration.common.CountFunctions -import solutions.bitbadger.documents.jvm.integration.postgresql.PgDB - -/** - * PostgreSQL integration tests for the `Count` object / `count*` connection extension functions - */ -@DisplayName("JVM | Groovy | PostgreSQL: Count") -class CountIT { - - @Test - @DisplayName("all counts all documents") - void all() { - new PgDB().withCloseable(CountFunctions.&all) - } - - @Test - @DisplayName("byFields counts documents by a numeric value") - void byFieldsNumeric() { - new PgDB().withCloseable(CountFunctions.&byFieldsNumeric) - } - - @Test - @DisplayName("byFields counts documents by a alphanumeric value") - void byFieldsAlpha() { - new PgDB().withCloseable(CountFunctions.&byFieldsAlpha) - } - - @Test - @DisplayName("byContains counts documents when matches are found") - void byContainsMatch() { - new PgDB().withCloseable(CountFunctions.&byContainsMatch) - } - - @Test - @DisplayName("byContains counts documents when no matches are found") - void byContainsNoMatch() { - new PgDB().withCloseable(CountFunctions.&byContainsNoMatch) - } - - @Test - @DisplayName("byJsonPath counts documents when matches are found") - void byJsonPathMatch() { - new PgDB().withCloseable(CountFunctions.&byJsonPathMatch) - } - - @Test - @DisplayName("byJsonPath counts documents when no matches are found") - void byJsonPathNoMatch() { - new PgDB().withCloseable(CountFunctions.&byJsonPathNoMatch) - } -} diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/postgresql/CustomIT.groovy b/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/postgresql/CustomIT.groovy deleted file mode 100644 index 2250f01..0000000 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/postgresql/CustomIT.groovy +++ /dev/null @@ -1,55 +0,0 @@ -package solutions.bitbadger.documents.groovy.jvm.integration.postgresql - -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import solutions.bitbadger.documents.groovy.jvm.integration.common.CustomFunctions -import solutions.bitbadger.documents.jvm.integration.postgresql.PgDB - -/** - * PostgreSQL integration tests for the `Custom` object / `custom*` connection extension functions - */ -@DisplayName("JVM | Groovy | PostgreSQL: Custom") -class CustomIT { - - @Test - @DisplayName("list succeeds with empty list") - void listEmpty() { - new PgDB().withCloseable(CustomFunctions.&listEmpty) - } - - @Test - @DisplayName("list succeeds with a non-empty list") - void listAll() { - new PgDB().withCloseable(CustomFunctions.&listAll) - } - - @Test - @DisplayName("single succeeds when document not found") - void singleNone() { - new PgDB().withCloseable(CustomFunctions.&singleNone) - } - - @Test - @DisplayName("single succeeds when a document is found") - void singleOne() { - new PgDB().withCloseable(CustomFunctions.&singleOne) - } - - @Test - @DisplayName("nonQuery makes changes") - void nonQueryChanges() { - new PgDB().withCloseable(CustomFunctions.&nonQueryChanges) - } - - @Test - @DisplayName("nonQuery makes no changes when where clause matches nothing") - void nonQueryNoChanges() { - new PgDB().withCloseable(CustomFunctions.&nonQueryNoChanges) - } - - @Test - @DisplayName("scalar succeeds") - void scalar() { - new PgDB().withCloseable(CustomFunctions.&scalar) - } -} diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/sqlite/CustomIT.groovy b/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/sqlite/CustomIT.groovy deleted file mode 100644 index 8c3d155..0000000 --- a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/jvm/integration/sqlite/CustomIT.groovy +++ /dev/null @@ -1,55 +0,0 @@ -package solutions.bitbadger.documents.groovy.jvm.integration.sqlite - -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import solutions.bitbadger.documents.groovy.jvm.integration.common.CustomFunctions -import solutions.bitbadger.documents.jvm.integration.sqlite.SQLiteDB - -/** - * SQLite integration tests for the `Custom` object / `custom*` connection extension functions - */ -@DisplayName("JVM | Groovy | SQLite: Custom") -class CustomIT { - - @Test - @DisplayName("list succeeds with empty list") - void listEmpty() { - new SQLiteDB().withCloseable(CustomFunctions.&listEmpty) - } - - @Test - @DisplayName("list succeeds with a non-empty list") - void listAll() { - new SQLiteDB().withCloseable(CustomFunctions.&listAll) - } - - @Test - @DisplayName("single succeeds when document not found") - void singleNone() { - new SQLiteDB().withCloseable(CustomFunctions.&singleNone) - } - - @Test - @DisplayName("single succeeds when a document is found") - void singleOne() { - new SQLiteDB().withCloseable(CustomFunctions.&singleOne) - } - - @Test - @DisplayName("nonQuery makes changes") - void nonQueryChanges() { - new SQLiteDB().withCloseable(CustomFunctions.&nonQueryChanges) - } - - @Test - @DisplayName("nonQuery makes no changes when where clause matches nothing") - void nonQueryNoChanges() { - new SQLiteDB().withCloseable(CustomFunctions.&nonQueryNoChanges) - } - - @Test - @DisplayName("scalar succeeds") - void scalar() { - new SQLiteDB().withCloseable(CustomFunctions.&scalar) - } -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/common/CountFunctions.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/common/CountFunctions.scala deleted file mode 100644 index 553a425..0000000 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/common/CountFunctions.scala +++ /dev/null @@ -1,47 +0,0 @@ -package solutions.bitbadger.documents.scala.jvm.integration.common - -import org.junit.jupiter.api.Assertions.* -import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.extensions.ConnExt.* -import solutions.bitbadger.documents.scala.support.JsonDocument -import solutions.bitbadger.documents.support.ThrowawayDatabase -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE - -import scala.jdk.CollectionConverters.* - -object CountFunctions { - - def all(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertEquals(5L, countAll(db.getConn, TEST_TABLE), "There should have been 5 documents in the table") - - def byFieldsNumeric(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertEquals(3L, countByFields(db.getConn, TEST_TABLE, (Field.between("numValue", 10, 20) :: Nil).asJava), - "There should have been 3 matching documents") - - def byFieldsAlpha(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertEquals(1L, countByFields(db.getConn, TEST_TABLE, (Field.between("value", "aardvark", "apple") :: Nil).asJava), - "There should have been 1 matching document") - - def byContainsMatch(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertEquals(2L, countByContains(db.getConn, TEST_TABLE, Map.Map1("value", "purple")), - "There should have been 2 matching documents") - - def byContainsNoMatch(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertEquals(0L, countByContains(db.getConn, TEST_TABLE, Map.Map1("value", "magenta")), - "There should have been no matching documents") - - def byJsonPathMatch(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertEquals(2L, countByJsonPath(db.getConn, TEST_TABLE, "$.numValue ? (@ < 5)"), - "There should have been 2 matching documents") - - def byJsonPathNoMatch(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertEquals(0L, countByJsonPath(db.getConn, TEST_TABLE, "$.numValue ? (@ > 100)"), - "There should have been no matching documents") -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/common/CustomFunctions.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/common/CustomFunctions.scala deleted file mode 100644 index 0d7e5a0..0000000 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/common/CustomFunctions.scala +++ /dev/null @@ -1,75 +0,0 @@ -package solutions.bitbadger.documents.scala.jvm.integration.common - -import org.junit.jupiter.api.Assertions.* -import solutions.bitbadger.documents.{Configuration, Field, Parameter, ParameterType} -import solutions.bitbadger.documents.extensions.ConnExt.* -import solutions.bitbadger.documents.jvm.Results -import solutions.bitbadger.documents.query.{CountQuery, DeleteQuery, FindQuery} -import solutions.bitbadger.documents.scala.support.JsonDocument -import solutions.bitbadger.documents.support.ThrowawayDatabase -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE - -import scala.annotation.nowarn -import scala.jdk.CollectionConverters.* - -object CustomFunctions { - - def listEmpty(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - deleteByFields(db.getConn, TEST_TABLE, (Field.exists(Configuration.idField) :: Nil).asJava) - @nowarn - val result = customList(db.getConn, FindQuery.all(TEST_TABLE), List().asJava, classOf[JsonDocument], - Results.fromData) - assertEquals(0, result.size, "There should have been no results") - - def listAll(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - @nowarn - val result = customList(db.getConn, FindQuery.all(TEST_TABLE), List().asJava, classOf[JsonDocument], - Results.fromData) - assertEquals(5, result.size, "There should have been 5 results") - - @nowarn - def singleNone(db: ThrowawayDatabase): Unit = - assertNull( - customSingle(db.getConn, FindQuery.all(TEST_TABLE), List().asJava, classOf[JsonDocument], Results.fromData), - "There should not have been a document returned") - - @nowarn - def singleOne(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertNotNull( - customSingle(db.getConn, FindQuery.all(TEST_TABLE), List().asJava, classOf[JsonDocument], Results.fromData), - "There should not have been a document returned") - - @nowarn - def nonQueryChanges(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertEquals(5L, - customScalar(db.getConn, CountQuery.all(TEST_TABLE), List().asJava, classOf[Long], Results.toCount), - "There should have been 5 documents in the table") - customNonQuery(db.getConn, s"DELETE FROM $TEST_TABLE") - assertEquals(0L, - customScalar(db.getConn, CountQuery.all(TEST_TABLE), List().asJava, classOf[Long], Results.toCount), - "There should have been no documents in the table") - - @nowarn - def nonQueryNoChanges(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertEquals(5L, - customScalar(db.getConn, CountQuery.all(TEST_TABLE), List().asJava, classOf[Long], Results.toCount), - "There should have been 5 documents in the table") - customNonQuery(db.getConn, DeleteQuery.byId(TEST_TABLE, "eighty-two"), - (Parameter(":id", ParameterType.STRING, "eighty-two") :: Nil).asJava) - assertEquals(5L, - customScalar(db.getConn, CountQuery.all(TEST_TABLE), List().asJava, classOf[Long], Results.toCount), - "There should still have been 5 documents in the table") - - @nowarn - def scalar(db: ThrowawayDatabase): Unit = - JsonDocument.load(db) - assertEquals(3L, - customScalar(db.getConn, s"SELECT 3 AS it FROM $TEST_TABLE LIMIT 1", List().asJava, classOf[Long], - Results.toCount), - "The number 3 should have been returned") -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/ByteIdClass.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/ByteIdClass.scala deleted file mode 100644 index 611fd75..0000000 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/ByteIdClass.scala +++ /dev/null @@ -1,3 +0,0 @@ -package solutions.bitbadger.documents.scala.support - -class ByteIdClass(var id: Byte) diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/IntIdClass.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/IntIdClass.scala deleted file mode 100644 index 2c4e6f1..0000000 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/IntIdClass.scala +++ /dev/null @@ -1,3 +0,0 @@ -package solutions.bitbadger.documents.scala.support - -class IntIdClass(var id: Int) diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/LongIdClass.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/LongIdClass.scala deleted file mode 100644 index a66d738..0000000 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/LongIdClass.scala +++ /dev/null @@ -1,3 +0,0 @@ -package solutions.bitbadger.documents.scala.support - -class LongIdClass(var id: Long) diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/ShortIdClass.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/ShortIdClass.scala deleted file mode 100644 index a81dfc4..0000000 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/ShortIdClass.scala +++ /dev/null @@ -1,3 +0,0 @@ -package solutions.bitbadger.documents.scala.support - -class ShortIdClass(var id: Short) diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/StringIdClass.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/StringIdClass.scala deleted file mode 100644 index 11976a4..0000000 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/StringIdClass.scala +++ /dev/null @@ -1,3 +0,0 @@ -package solutions.bitbadger.documents.scala.support - -class StringIdClass(var id: String) diff --git a/src/kotlin/pom.xml b/src/kotlinx/pom.xml similarity index 73% rename from src/kotlin/pom.xml rename to src/kotlinx/pom.xml index 5549ee3..c0d6f73 100644 --- a/src/kotlin/pom.xml +++ b/src/kotlinx/pom.xml @@ -3,20 +3,18 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - - solutions.bitbadger.documents - kotlin - 4.0.0-alpha1-SNAPSHOT - jar - solutions.bitbadger documents 4.0.0-alpha1-SNAPSHOT + ../../pom.xml + solutions.bitbadger.documents + kotlinx + ${project.groupId}:${project.artifactId} - Expose a document store interface for PostgreSQL and SQLite (Kotlin Library) + Expose a document store interface for PostgreSQL and SQLite (KotlinX Serialization Library) https://bitbadger.solutions/open-source/relational-documents/jvm/ @@ -28,15 +26,17 @@ solutions.bitbadger.documents - jvm - 4.0.0-alpha1-SNAPSHOT - jar + core + ${project.version} + + + org.jetbrains.kotlinx + kotlinx-serialization-json-jvm + ${serialization.version} - src/main/kotlin - src/test/kotlin org.jetbrains.kotlin @@ -49,6 +49,12 @@ compile + + + ${project.basedir}/src/main/java + ${project.basedir}/src/main/kotlin + + test-compile @@ -56,6 +62,12 @@ test-compile + + + ${project.basedir}/src/test/java + ${project.basedir}/src/test/kotlin + + @@ -73,11 +85,11 @@ maven-surefire-plugin - 2.22.2 + ${surefire.version} maven-failsafe-plugin - 2.22.2 + ${failsafe.version} @@ -90,6 +102,7 @@ org.apache.maven.plugins maven-compiler-plugin + 3.13.0 ${java.version} ${java.version} diff --git a/src/kotlinx/src/main/java/module-info.java b/src/kotlinx/src/main/java/module-info.java new file mode 100644 index 0000000..220592d --- /dev/null +++ b/src/kotlinx/src/main/java/module-info.java @@ -0,0 +1,10 @@ +module solutions.bitbadger.documents.kotlinx { + requires solutions.bitbadger.documents.core; + requires kotlin.stdlib; + requires kotlin.reflect; + requires kotlinx.serialization.json; + requires java.sql; + + exports solutions.bitbadger.documents.kotlinx; + exports solutions.bitbadger.documents.kotlinx.extensions; +} diff --git a/src/kotlin/src/main/kotlin/Count.kt b/src/kotlinx/src/main/kotlin/Count.kt similarity index 93% rename from src/kotlin/src/main/kotlin/Count.kt rename to src/kotlinx/src/main/kotlin/Count.kt index c3b3ef5..0ebcfca 100644 --- a/src/kotlin/src/main/kotlin/Count.kt +++ b/src/kotlinx/src/main/kotlin/Count.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.kotlin.extensions.* +import solutions.bitbadger.documents.kotlinx.extensions.* import solutions.bitbadger.documents.query.CountQuery import java.sql.Connection @@ -74,7 +74,11 @@ object Count { * @throws DocumentException If called on a SQLite connection */ inline fun byContains(tableName: String, criteria: TContains, conn: Connection) = - conn.customScalar(CountQuery.byContains(tableName), listOf(Parameters.json(":criteria", criteria)), Results::toCount) + conn.customScalar( + CountQuery.byContains(tableName), + listOf(Parameters.json(":criteria", criteria)), + Results::toCount + ) /** * Count documents using a JSON containment query (PostgreSQL only) diff --git a/src/kotlin/src/main/kotlin/Custom.kt b/src/kotlinx/src/main/kotlin/Custom.kt similarity index 97% rename from src/kotlin/src/main/kotlin/Custom.kt rename to src/kotlinx/src/main/kotlin/Custom.kt index 4d77ee5..c5d5e7e 100644 --- a/src/kotlin/src/main/kotlin/Custom.kt +++ b/src/kotlinx/src/main/kotlin/Custom.kt @@ -1,8 +1,8 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import solutions.bitbadger.documents.* import solutions.bitbadger.documents.Configuration -import solutions.bitbadger.documents.jvm.Custom as JvmCustom +import solutions.bitbadger.documents.java.Custom as JvmCustom import java.sql.Connection import java.sql.ResultSet diff --git a/src/kotlin/src/main/kotlin/Definition.kt b/src/kotlinx/src/main/kotlin/Definition.kt similarity index 95% rename from src/kotlin/src/main/kotlin/Definition.kt rename to src/kotlinx/src/main/kotlin/Definition.kt index 23fa87e..508b011 100644 --- a/src/kotlin/src/main/kotlin/Definition.kt +++ b/src/kotlinx/src/main/kotlin/Definition.kt @@ -1,8 +1,8 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentIndex -import solutions.bitbadger.documents.jvm.Definition as JvmDefinition +import solutions.bitbadger.documents.java.Definition as JvmDefinition import java.sql.Connection /** diff --git a/src/kotlin/src/main/kotlin/Delete.kt b/src/kotlinx/src/main/kotlin/Delete.kt similarity index 94% rename from src/kotlin/src/main/kotlin/Delete.kt rename to src/kotlinx/src/main/kotlin/Delete.kt index 9cdaa42..159a1a7 100644 --- a/src/kotlin/src/main/kotlin/Delete.kt +++ b/src/kotlinx/src/main/kotlin/Delete.kt @@ -1,8 +1,8 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.jvm.Delete as JvmDelete -import solutions.bitbadger.documents.kotlin.extensions.* +import solutions.bitbadger.documents.java.Delete as JvmDelete +import solutions.bitbadger.documents.kotlinx.extensions.* import solutions.bitbadger.documents.query.DeleteQuery import java.sql.Connection @@ -60,7 +60,7 @@ object Delete { * @throws DocumentException If called on a SQLite connection */ inline fun byContains(tableName: String, criteria: TContains, conn: Connection) = - conn.customNonQuery(DeleteQuery.byContains(tableName), listOf(Parameters.json(":criteria", criteria))) + conn.customNonQuery(DeleteQuery.byContains(tableName), listOf(Parameters.json(":criteria", criteria))) /** * Delete documents using a JSON containment query (PostgreSQL only) diff --git a/src/kotlin/src/main/kotlin/Document.kt b/src/kotlinx/src/main/kotlin/Document.kt similarity index 97% rename from src/kotlin/src/main/kotlin/Document.kt rename to src/kotlinx/src/main/kotlin/Document.kt index 4b391b3..1667a49 100644 --- a/src/kotlin/src/main/kotlin/Document.kt +++ b/src/kotlinx/src/main/kotlin/Document.kt @@ -1,10 +1,10 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import solutions.bitbadger.documents.AutoId import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.extensions.customNonQuery +import solutions.bitbadger.documents.kotlinx.extensions.customNonQuery import solutions.bitbadger.documents.query.DocumentQuery import solutions.bitbadger.documents.query.Where import solutions.bitbadger.documents.query.statementWhere diff --git a/src/kotlin/src/main/kotlin/DocumentConfig.kt b/src/kotlinx/src/main/kotlin/DocumentConfig.kt similarity index 94% rename from src/kotlin/src/main/kotlin/DocumentConfig.kt rename to src/kotlinx/src/main/kotlin/DocumentConfig.kt index cb42341..f1b4de9 100644 --- a/src/kotlin/src/main/kotlin/DocumentConfig.kt +++ b/src/kotlinx/src/main/kotlin/DocumentConfig.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import kotlinx.serialization.json.Json diff --git a/src/kotlin/src/main/kotlin/Exists.kt b/src/kotlinx/src/main/kotlin/Exists.kt similarity index 96% rename from src/kotlin/src/main/kotlin/Exists.kt rename to src/kotlinx/src/main/kotlin/Exists.kt index 4cbc05b..2bb12dc 100644 --- a/src/kotlin/src/main/kotlin/Exists.kt +++ b/src/kotlinx/src/main/kotlin/Exists.kt @@ -1,8 +1,8 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.jvm.Exists as JvmExists -import solutions.bitbadger.documents.kotlin.extensions.* +import solutions.bitbadger.documents.java.Exists as JvmExists +import solutions.bitbadger.documents.kotlinx.extensions.* import solutions.bitbadger.documents.query.ExistsQuery import java.sql.Connection diff --git a/src/kotlin/src/main/kotlin/Find.kt b/src/kotlinx/src/main/kotlin/Find.kt similarity index 99% rename from src/kotlin/src/main/kotlin/Find.kt rename to src/kotlinx/src/main/kotlin/Find.kt index 37af42a..3e90428 100644 --- a/src/kotlin/src/main/kotlin/Find.kt +++ b/src/kotlinx/src/main/kotlin/Find.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.kotlin.extensions.* +import solutions.bitbadger.documents.kotlinx.extensions.* import solutions.bitbadger.documents.query.FindQuery import solutions.bitbadger.documents.query.orderBy import java.sql.Connection diff --git a/src/kotlin/src/main/kotlin/Parameters.kt b/src/kotlinx/src/main/kotlin/Parameters.kt similarity index 91% rename from src/kotlin/src/main/kotlin/Parameters.kt rename to src/kotlinx/src/main/kotlin/Parameters.kt index 74bf62c..4e71c9d 100644 --- a/src/kotlin/src/main/kotlin/Parameters.kt +++ b/src/kotlinx/src/main/kotlin/Parameters.kt @@ -1,7 +1,9 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx -import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.jvm.Parameters as JvmParameters +import solutions.bitbadger.documents.Field +import solutions.bitbadger.documents.Parameter +import solutions.bitbadger.documents.ParameterType +import solutions.bitbadger.documents.java.Parameters as JvmParameters import java.sql.Connection /** @@ -28,7 +30,7 @@ object Parameters { * @return A parameter with the value encoded */ inline fun json(name: String, value: T) = - Parameter(name, ParameterType.JSON, DocumentConfig.serialize(value)) + Parameter(name, ParameterType.JSON, DocumentConfig.serialize(value)) /** * Add field parameters to the given set of parameters diff --git a/src/kotlin/src/main/kotlin/Patch.kt b/src/kotlinx/src/main/kotlin/Patch.kt similarity index 98% rename from src/kotlin/src/main/kotlin/Patch.kt rename to src/kotlinx/src/main/kotlin/Patch.kt index e4e4041..730d069 100644 --- a/src/kotlin/src/main/kotlin/Patch.kt +++ b/src/kotlinx/src/main/kotlin/Patch.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.kotlin.extensions.* +import solutions.bitbadger.documents.kotlinx.extensions.* import solutions.bitbadger.documents.query.PatchQuery import java.sql.Connection diff --git a/src/kotlin/src/main/kotlin/RemoveFields.kt b/src/kotlinx/src/main/kotlin/RemoveFields.kt similarity index 96% rename from src/kotlin/src/main/kotlin/RemoveFields.kt rename to src/kotlinx/src/main/kotlin/RemoveFields.kt index f24d855..b5f8b0f 100644 --- a/src/kotlin/src/main/kotlin/RemoveFields.kt +++ b/src/kotlinx/src/main/kotlin/RemoveFields.kt @@ -1,8 +1,8 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.jvm.RemoveFields as JvmRemoveFields -import solutions.bitbadger.documents.kotlin.extensions.* +import solutions.bitbadger.documents.java.RemoveFields as JvmRemoveFields +import solutions.bitbadger.documents.kotlinx.extensions.* import solutions.bitbadger.documents.query.RemoveFieldsQuery import java.sql.Connection diff --git a/src/kotlin/src/main/kotlin/Results.kt b/src/kotlinx/src/main/kotlin/Results.kt similarity index 95% rename from src/kotlin/src/main/kotlin/Results.kt rename to src/kotlinx/src/main/kotlin/Results.kt index 791fb60..d63a363 100644 --- a/src/kotlin/src/main/kotlin/Results.kt +++ b/src/kotlinx/src/main/kotlin/Results.kt @@ -1,4 +1,4 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx import solutions.bitbadger.documents.Configuration import solutions.bitbadger.documents.Dialect @@ -19,7 +19,7 @@ object Results { * @return A function to create the constructed domain item */ inline fun fromDocument(field: String): (ResultSet) -> TDoc = - { rs -> DocumentConfig.deserialize(rs.getString(field)) } + { rs -> DocumentConfig.deserialize(rs.getString(field)) } /** * Create a domain item from a document diff --git a/src/kotlin/src/main/kotlin/extensions/Connection.kt b/src/kotlinx/src/main/kotlin/extensions/Connection.kt similarity index 99% rename from src/kotlin/src/main/kotlin/extensions/Connection.kt rename to src/kotlinx/src/main/kotlin/extensions/Connection.kt index d02e85b..784bb95 100644 --- a/src/kotlin/src/main/kotlin/extensions/Connection.kt +++ b/src/kotlinx/src/main/kotlin/extensions/Connection.kt @@ -1,7 +1,7 @@ -package solutions.bitbadger.documents.kotlin.extensions +package solutions.bitbadger.documents.kotlinx.extensions import solutions.bitbadger.documents.* -import solutions.bitbadger.documents.kotlin.* +import solutions.bitbadger.documents.kotlinx.* import java.sql.Connection import java.sql.ResultSet diff --git a/src/kotlinx/src/test/java/module-info.java b/src/kotlinx/src/test/java/module-info.java new file mode 100644 index 0000000..959ffd5 --- /dev/null +++ b/src/kotlinx/src/test/java/module-info.java @@ -0,0 +1,14 @@ +module solutions.bitbadger.documents.kotlinx.tests { + requires solutions.bitbadger.documents.core; + requires solutions.bitbadger.documents.kotlinx; + requires java.sql; + requires kotlin.stdlib; + requires kotlinx.serialization.json; + requires kotlin.test.junit5; + + exports solutions.bitbadger.documents.kotlinx.tests; + exports solutions.bitbadger.documents.kotlinx.tests.integration; + + opens solutions.bitbadger.documents.kotlinx.tests; + opens solutions.bitbadger.documents.kotlinx.tests.integration; +} diff --git a/src/kotlin/src/test/kotlin/DocumentConfigTest.kt b/src/kotlinx/src/test/kotlin/DocumentConfigTest.kt similarity index 81% rename from src/kotlin/src/test/kotlin/DocumentConfigTest.kt rename to src/kotlinx/src/test/kotlin/DocumentConfigTest.kt index 9b90f19..882ed3b 100644 --- a/src/kotlin/src/test/kotlin/DocumentConfigTest.kt +++ b/src/kotlinx/src/test/kotlin/DocumentConfigTest.kt @@ -1,14 +1,15 @@ -package solutions.bitbadger.documents.kotlin +package solutions.bitbadger.documents.kotlinx.tests import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import solutions.bitbadger.documents.kotlinx.DocumentConfig import kotlin.test.assertFalse import kotlin.test.assertTrue /** * Unit tests for the `Configuration` object */ -@DisplayName("Kotlin | DocumentConfig") +@DisplayName("KotlinX | DocumentConfig") class DocumentConfigTest { @Test diff --git a/src/jvm/src/test/kotlin/support/Types.kt b/src/kotlinx/src/test/kotlin/Types.kt similarity index 81% rename from src/jvm/src/test/kotlin/support/Types.kt rename to src/kotlinx/src/test/kotlin/Types.kt index d56f23b..ab3754f 100644 --- a/src/jvm/src/test/kotlin/support/Types.kt +++ b/src/kotlinx/src/test/kotlin/Types.kt @@ -1,18 +1,23 @@ -package solutions.bitbadger.documents.support +package solutions.bitbadger.documents.kotlinx.tests -import solutions.bitbadger.documents.extensions.insert +import kotlinx.serialization.Serializable +import solutions.bitbadger.documents.kotlinx.extensions.insert +import solutions.bitbadger.documents.kotlinx.tests.integration.ThrowawayDatabase /** The test table name to use for integration tests */ const val TEST_TABLE = "test_table" +@Serializable data class NumIdDocument(val key: Int, val text: String) { constructor() : this(0, "") } +@Serializable data class SubDocument(val foo: String, val bar: String) { constructor() : this("", "") } +@Serializable data class ArrayDocument(val id: String, val values: List) { constructor() : this("", listOf()) @@ -27,6 +32,7 @@ data class ArrayDocument(val id: String, val values: List) { } } +@Serializable data class JsonDocument(val id: String, val value: String = "", val numValue: Int = 0, val sub: SubDocument? = null) { constructor() : this("") @@ -45,11 +51,3 @@ data class JsonDocument(val id: String, val value: String = "", val numValue: In testDocuments.forEach { db.conn.insert(tableName, it) } } } - -// Test classes for AutoId generation - -data class ByteIdClass(val id: Byte) -data class ShortIdClass(val id: Short) -data class IntIdClass(val id: Int) -data class LongIdClass(val id: Long) -data class StringIdClass(val id: String) diff --git a/src/kotlinx/src/test/kotlin/integration/CountFunctions.kt b/src/kotlinx/src/test/kotlin/integration/CountFunctions.kt new file mode 100644 index 0000000..2f31b3d --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/CountFunctions.kt @@ -0,0 +1,72 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.JsonDocument +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE +import kotlin.test.assertEquals + +/** + * Integration tests for the `Count` object + */ +object CountFunctions { + + fun all(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should have been 5 documents in the table") + } + + fun byFieldsNumeric(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 3L, + db.conn.countByFields(TEST_TABLE, listOf(Field.between("numValue", 10, 20))), + "There should have been 3 matching documents" + ) + } + + fun byFieldsAlpha(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 1L, + db.conn.countByFields(TEST_TABLE, listOf(Field.between("value", "aardvark", "apple"))), + "There should have been 1 matching document" + ) + } + + fun byContainsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 2L, + db.conn.countByContains(TEST_TABLE, mapOf("value" to "purple")), + "There should have been 2 matching documents" + ) + } + + fun byContainsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 0L, + db.conn.countByContains(TEST_TABLE, mapOf("value" to "magenta")), + "There should have been no matching documents" + ) + } + + fun byJsonPathMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 2L, + db.conn.countByJsonPath(TEST_TABLE, "$.numValue ? (@ < 5)"), + "There should have been 2 matching documents" + ) + } + + fun byJsonPathNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 0L, + db.conn.countByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)"), + "There should have been no matching documents" + ) + } +} diff --git a/src/kotlinx/src/test/kotlin/integration/CustomFunctions.kt b/src/kotlinx/src/test/kotlin/integration/CustomFunctions.kt new file mode 100644 index 0000000..d7ababc --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/CustomFunctions.kt @@ -0,0 +1,83 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.Results +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.JsonDocument +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE +import solutions.bitbadger.documents.query.* +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +/** + * Integration tests for the `Custom` object + */ +object CustomFunctions { + + fun listEmpty(db: ThrowawayDatabase) { + JsonDocument.load(db) + db.conn.deleteByFields(TEST_TABLE, listOf(Field.exists(Configuration.idField))) + val result = db.conn.customList(FindQuery.all(TEST_TABLE), mapFunc = Results::fromData) + assertEquals(0, result.size, "There should have been no results") + } + + fun listAll(db: ThrowawayDatabase) { + JsonDocument.load(db) + val result = db.conn.customList(FindQuery.all(TEST_TABLE), mapFunc = Results::fromData) + assertEquals(5, result.size, "There should have been 5 results") + } + + fun singleNone(db: ThrowawayDatabase) = + assertNull( + db.conn.customSingle(FindQuery.all(TEST_TABLE), mapFunc = Results::fromData), + "There should not have been a document returned" + ) + + fun singleOne(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertNotNull( + db.conn.customSingle(FindQuery.all(TEST_TABLE), mapFunc = Results::fromData), + "There should not have been a document returned" + ) + } + + fun nonQueryChanges(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), mapFunc = Results::toCount), + "There should have been 5 documents in the table" + ) + db.conn.customNonQuery("DELETE FROM $TEST_TABLE") + assertEquals( + 0L, db.conn.customScalar(CountQuery.all(TEST_TABLE), mapFunc = Results::toCount), + "There should have been no documents in the table" + ) + } + + fun nonQueryNoChanges(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), mapFunc = Results::toCount), + "There should have been 5 documents in the table" + ) + db.conn.customNonQuery( + DeleteQuery.byId(TEST_TABLE, "eighty-two"), + listOf(Parameter(":id", ParameterType.STRING, "eighty-two")) + ) + assertEquals( + 5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), mapFunc = Results::toCount), + "There should still have been 5 documents in the table" + ) + } + + fun scalar(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 3L, + db.conn.customScalar("SELECT 3 AS it FROM $TEST_TABLE LIMIT 1", mapFunc = Results::toCount), + "The number 3 should have been returned" + ) + } + +} \ No newline at end of file diff --git a/src/kotlinx/src/test/kotlin/integration/DefinitionFunctions.kt b/src/kotlinx/src/test/kotlin/integration/DefinitionFunctions.kt new file mode 100644 index 0000000..3e05234 --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/DefinitionFunctions.kt @@ -0,0 +1,45 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/** + * Integration tests for the `Definition` object / `ensure*` connection extension functions + */ +object DefinitionFunctions { + + fun ensureTable(db: ThrowawayDatabase) { + assertFalse(db.dbObjectExists("ensured"), "The 'ensured' table should not exist") + assertFalse(db.dbObjectExists("idx_ensured_key"), "The PK index for the 'ensured' table should not exist") + db.conn.ensureTable("ensured") + assertTrue(db.dbObjectExists("ensured"), "The 'ensured' table should exist") + assertTrue(db.dbObjectExists("idx_ensured_key"), "The PK index for the 'ensured' table should now exist") + } + + fun ensureFieldIndex(db: ThrowawayDatabase) { + assertFalse(db.dbObjectExists("idx_${TEST_TABLE}_test"), "The test index should not exist") + db.conn.ensureFieldIndex(TEST_TABLE, "test", listOf("id", "category")) + assertTrue(db.dbObjectExists("idx_${TEST_TABLE}_test"), "The test index should now exist") + } + + fun ensureDocumentIndexFull(db: ThrowawayDatabase) { + assertFalse(db.dbObjectExists("doc_table"), "The 'doc_table' table should not exist") + db.conn.ensureTable("doc_table") + assertTrue(db.dbObjectExists("doc_table"), "The 'doc_table' table should exist") + assertFalse(db.dbObjectExists("idx_doc_table_document"), "The document index should not exist") + db.conn.ensureDocumentIndex("doc_table", DocumentIndex.FULL) + assertTrue(db.dbObjectExists("idx_doc_table_document"), "The document index should exist") + } + + fun ensureDocumentIndexOptimized(db: ThrowawayDatabase) { + assertFalse(db.dbObjectExists("doc_table"), "The 'doc_table' table should not exist") + db.conn.ensureTable("doc_table") + assertTrue(db.dbObjectExists("doc_table"), "The 'doc_table' table should exist") + assertFalse(db.dbObjectExists("idx_doc_table_document"), "The document index should not exist") + db.conn.ensureDocumentIndex("doc_table", DocumentIndex.OPTIMIZED) + assertTrue(db.dbObjectExists("idx_doc_table_document"), "The document index should exist") + } +} diff --git a/src/kotlinx/src/test/kotlin/integration/DeleteFunctions.kt b/src/kotlinx/src/test/kotlin/integration/DeleteFunctions.kt new file mode 100644 index 0000000..13eaf04 --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/DeleteFunctions.kt @@ -0,0 +1,69 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.JsonDocument +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE +import kotlin.test.assertEquals + +/** + * Integration tests for the `Delete` object + */ +object DeleteFunctions { + + fun byIdMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteById(TEST_TABLE, "four") + assertEquals(4, db.conn.countAll(TEST_TABLE), "There should now be 4 documents in the table") + } + + fun byIdNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteById(TEST_TABLE, "negative four") + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + } + + fun byFieldsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByFields(TEST_TABLE, listOf(Field.notEqual("value", "purple"))) + assertEquals(2, db.conn.countAll(TEST_TABLE), "There should now be 2 documents in the table") + } + + fun byFieldsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByFields(TEST_TABLE, listOf(Field.equal("value", "crimson"))) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + } + + fun byContainsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByContains(TEST_TABLE, mapOf("value" to "purple")) + assertEquals(3, db.conn.countAll(TEST_TABLE), "There should now be 3 documents in the table") + } + + fun byContainsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByContains(TEST_TABLE, mapOf("target" to "acquired")) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + } + + fun byJsonPathMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByJsonPath(TEST_TABLE, "$.value ? (@ == \"purple\")") + assertEquals(3, db.conn.countAll(TEST_TABLE), "There should now be 3 documents in the table") + } + + fun byJsonPathNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)") + assertEquals(5, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + } +} diff --git a/src/kotlinx/src/test/kotlin/integration/DocumentFunctions.kt b/src/kotlinx/src/test/kotlin/integration/DocumentFunctions.kt new file mode 100644 index 0000000..4715998 --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/DocumentFunctions.kt @@ -0,0 +1,130 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.JsonDocument +import solutions.bitbadger.documents.kotlinx.tests.NumIdDocument +import solutions.bitbadger.documents.kotlinx.tests.SubDocument +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE +import kotlin.test.* + +/** + * Integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions + */ +object DocumentFunctions { + + fun insertDefault(db: ThrowawayDatabase) { + assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table") + val doc = JsonDocument("turkey", "", 0, SubDocument("gobble", "gobble")) + db.conn.insert(TEST_TABLE, doc) + val after = db.conn.findAll(TEST_TABLE) + assertEquals(1, after.size, "There should be one document in the table") + assertEquals(doc, after[0], "The document should be what was inserted") + } + + fun insertDupe(db: ThrowawayDatabase) { + db.conn.insert(TEST_TABLE, JsonDocument("a", "", 0, null)) + assertThrows("Inserting a document with a duplicate key should have thrown an exception") { + db.conn.insert(TEST_TABLE, JsonDocument("a", "b", 22, null)) + } + } + + fun insertNumAutoId(db: ThrowawayDatabase) { + try { + Configuration.autoIdStrategy = AutoId.NUMBER + Configuration.idField = "key" + assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table") + + db.conn.insert(TEST_TABLE, NumIdDocument(0, "one")) + db.conn.insert(TEST_TABLE, NumIdDocument(0, "two")) + db.conn.insert(TEST_TABLE, NumIdDocument(77, "three")) + db.conn.insert(TEST_TABLE, NumIdDocument(0, "four")) + + val after = db.conn.findAll(TEST_TABLE, listOf(Field.named("key"))) + assertEquals(4, after.size, "There should have been 4 documents returned") + assertEquals( + "1|2|77|78", after.joinToString("|") { it.key.toString() }, + "The IDs were not generated correctly" + ) + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED + Configuration.idField = "id" + } + } + + fun insertUUIDAutoId(db: ThrowawayDatabase) { + try { + Configuration.autoIdStrategy = AutoId.UUID + assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table") + + db.conn.insert(TEST_TABLE, JsonDocument("")) + + val after = db.conn.findAll(TEST_TABLE) + assertEquals(1, after.size, "There should have been 1 document returned") + assertEquals(32, after[0].id.length, "The ID was not generated correctly") + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED + } + } + + fun insertStringAutoId(db: ThrowawayDatabase) { + try { + Configuration.autoIdStrategy = AutoId.RANDOM_STRING + assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table") + + db.conn.insert(TEST_TABLE, JsonDocument("")) + + Configuration.idStringLength = 21 + db.conn.insert(TEST_TABLE, JsonDocument("")) + + val after = db.conn.findAll(TEST_TABLE) + assertEquals(2, after.size, "There should have been 2 documents returned") + assertEquals(16, after[0].id.length, "The first document's ID was not generated correctly") + assertEquals(21, after[1].id.length, "The second document's ID was not generated correctly") + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED + Configuration.idStringLength = 16 + } + } + + fun saveMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + db.conn.save(TEST_TABLE, JsonDocument("two", numValue = 44)) + val doc = db.conn.findById(TEST_TABLE, "two") + assertNotNull(doc, "There should have been a document returned") + assertEquals("two", doc.id, "An incorrect document was returned") + assertEquals("", doc.value, "The \"value\" field was not updated") + assertEquals(44, doc.numValue, "The \"numValue\" field was not updated") + assertNull(doc.sub, "The \"sub\" field was not updated") + } + + fun saveNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + db.conn.save(TEST_TABLE, JsonDocument("test", sub = SubDocument("a", "b"))) + assertNotNull( + db.conn.findById(TEST_TABLE, "test"), + "The test document should have been saved" + ) + } + + fun updateMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + db.conn.update(TEST_TABLE, "one", JsonDocument("one", "howdy", 8, SubDocument("y", "z"))) + val doc = db.conn.findById(TEST_TABLE, "one") + assertNotNull(doc, "There should have been a document returned") + assertEquals("one", doc.id, "An incorrect document was returned") + assertEquals("howdy", doc.value, "The \"value\" field was not updated") + assertEquals(8, doc.numValue, "The \"numValue\" field was not updated") + assertNotNull(doc.sub, "The sub-document should not be null") + assertEquals("y", doc.sub.foo, "The sub-document \"foo\" field was not updated") + assertEquals("z", doc.sub.bar, "The sub-document \"bar\" field was not updated") + } + + fun updateNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse { db.conn.existsById(TEST_TABLE, "two-hundred") } + db.conn.update(TEST_TABLE, "two-hundred", JsonDocument("two-hundred", numValue = 200)) + assertFalse { db.conn.existsById(TEST_TABLE, "two-hundred") } + } +} diff --git a/src/kotlinx/src/test/kotlin/integration/ExistsFunctions.kt b/src/kotlinx/src/test/kotlin/integration/ExistsFunctions.kt new file mode 100644 index 0000000..800c60c --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/ExistsFunctions.kt @@ -0,0 +1,66 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.JsonDocument +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/** + * Integration tests for the `Exists` object + */ +object ExistsFunctions { + + fun byIdMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertTrue("The document with ID \"three\" should exist") { db.conn.existsById(TEST_TABLE, "three") } + } + + fun byIdNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse("The document with ID \"seven\" should not exist") { db.conn.existsById(TEST_TABLE, "seven") } + } + + fun byFieldsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertTrue("Matching documents should have been found") { + db.conn.existsByFields(TEST_TABLE, listOf(Field.equal("numValue", 10))) + } + } + + fun byFieldsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse("No matching documents should have been found") { + db.conn.existsByFields(TEST_TABLE, listOf(Field.equal("nothing", "none"))) + } + } + + fun byContainsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertTrue("Matching documents should have been found") { + db.conn.existsByContains(TEST_TABLE, mapOf("value" to "purple")) + } + } + + fun byContainsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse("Matching documents should not have been found") { + db.conn.existsByContains(TEST_TABLE, mapOf("value" to "violet")) + } + } + + fun byJsonPathMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertTrue("Matching documents should have been found") { + db.conn.existsByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10)") + } + } + + fun byJsonPathNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertFalse("Matching documents should not have been found") { + db.conn.existsByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10.1)") + } + } +} diff --git a/src/kotlinx/src/test/kotlin/integration/FindFunctions.kt b/src/kotlinx/src/test/kotlin/integration/FindFunctions.kt new file mode 100644 index 0000000..58b8e87 --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/FindFunctions.kt @@ -0,0 +1,300 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.ArrayDocument +import solutions.bitbadger.documents.kotlinx.tests.JsonDocument +import solutions.bitbadger.documents.kotlinx.tests.NumIdDocument +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue + +/** + * Integration tests for the `Find` object + */ +object FindFunctions { + + fun allDefault(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals(5, db.conn.findAll(TEST_TABLE).size, "There should have been 5 documents returned") + } + + fun allAscending(db: ThrowawayDatabase) { + JsonDocument.load(db) + val docs = db.conn.findAll(TEST_TABLE, listOf(Field.named("id"))) + assertEquals(5, docs.size, "There should have been 5 documents returned") + assertEquals( + "five|four|one|three|two", + docs.joinToString("|") { it.id }, + "The documents were not ordered correctly" + ) + } + + fun allDescending(db: ThrowawayDatabase) { + JsonDocument.load(db) + val docs = db.conn.findAll(TEST_TABLE, listOf(Field.named("id DESC"))) + assertEquals(5, docs.size, "There should have been 5 documents returned") + assertEquals( + "two|three|one|four|five", + docs.joinToString("|") { it.id }, + "The documents were not ordered correctly" + ) + } + + fun allNumOrder(db: ThrowawayDatabase) { + JsonDocument.load(db) + val docs = db.conn.findAll( + TEST_TABLE, + listOf(Field.named("sub.foo NULLS LAST"), Field.named("n:numValue")) + ) + assertEquals(5, docs.size, "There should have been 5 documents returned") + assertEquals( + "two|four|one|three|five", + docs.joinToString("|") { it.id }, + "The documents were not ordered correctly" + ) + } + + fun allEmpty(db: ThrowawayDatabase) = + assertEquals(0, db.conn.findAll(TEST_TABLE).size, "There should have been no documents returned") + + fun byIdString(db: ThrowawayDatabase) { + JsonDocument.load(db) + val doc = db.conn.findById(TEST_TABLE, "two") + assertNotNull(doc, "The document should have been returned") + assertEquals("two", doc.id, "An incorrect document was returned") + } + + fun byIdNumber(db: ThrowawayDatabase) { + Configuration.idField = "key" + try { + db.conn.insert(TEST_TABLE, NumIdDocument(18, "howdy")) + val doc = db.conn.findById(TEST_TABLE, 18) + assertNotNull(doc, "The document should have been returned") + } finally { + Configuration.idField = "id" + } + } + + fun byIdNotFound(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertNull( + db.conn.findById(TEST_TABLE, "x"), + "There should have been no document returned" + ) + } + + fun byFieldsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val docs = db.conn.findByFields( + TEST_TABLE, + listOf(Field.any("value", listOf("blue", "purple")), Field.exists("sub")), + FieldMatch.ALL + ) + assertEquals(1, docs.size, "There should have been a document returned") + assertEquals("four", docs[0].id, "The incorrect document was returned") + } + + fun byFieldsMatchOrdered(db: ThrowawayDatabase) { + JsonDocument.load(db) + val docs = db.conn.findByFields( + TEST_TABLE, + listOf(Field.equal("value", "purple")), + orderBy = listOf(Field.named("id")) + ) + assertEquals(2, docs.size, "There should have been 2 documents returned") + assertEquals("five|four", docs.joinToString("|") { it.id }, "The documents were not ordered correctly") + } + + fun byFieldsMatchNumIn(db: ThrowawayDatabase) { + JsonDocument.load(db) + val docs = db.conn.findByFields(TEST_TABLE, listOf(Field.any("numValue", listOf(2, 4, 6, 8)))) + assertEquals(1, docs.size, "There should have been a document returned") + assertEquals("three", docs[0].id, "The incorrect document was returned") + } + + fun byFieldsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 0, + db.conn.findByFields(TEST_TABLE, listOf(Field.greater("numValue", 100))).size, + "There should have been no documents returned" + ) + } + + fun byFieldsMatchInArray(db: ThrowawayDatabase) { + ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) } + val docs = + db.conn.findByFields(TEST_TABLE, listOf(Field.inArray("values", TEST_TABLE, listOf("c")))) + assertEquals(2, docs.size, "There should have been two documents returned") + assertTrue(listOf("first", "second").contains(docs[0].id), "An incorrect document was returned (${docs[0].id})") + assertTrue(listOf("first", "second").contains(docs[1].id), "An incorrect document was returned (${docs[1].id})") + } + + fun byFieldsNoMatchInArray(db: ThrowawayDatabase) { + ArrayDocument.testDocuments.forEach { db.conn.insert(TEST_TABLE, it) } + assertEquals( + 0, + db.conn.findByFields( + TEST_TABLE, + listOf(Field.inArray("values", TEST_TABLE, listOf("j"))) + ).size, + "There should have been no documents returned" + ) + } + + fun byContainsMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val docs = db.conn.findByContains>(TEST_TABLE, mapOf("value" to "purple")) + assertEquals(2, docs.size, "There should have been 2 documents returned") + assertTrue(listOf("four", "five").contains(docs[0].id), "An incorrect document was returned (${docs[0].id})") + assertTrue(listOf("four", "five").contains(docs[1].id), "An incorrect document was returned (${docs[1].id})") + } + + fun byContainsMatchOrdered(db: ThrowawayDatabase) { + JsonDocument.load(db) + val docs = db.conn.findByContains>>( + TEST_TABLE, + mapOf("sub" to mapOf("foo" to "green")), + listOf(Field.named("value")) + ) + assertEquals(2, docs.size, "There should have been 2 documents returned") + assertEquals("two|four", docs.joinToString("|") { it.id }, "The documents were not ordered correctly") + } + + fun byContainsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 0, + db.conn.findByContains>(TEST_TABLE, mapOf("value" to "indigo")).size, + "There should have been no documents returned" + ) + } + + fun byJsonPathMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + val docs = db.conn.findByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)") + assertEquals(2, docs.size, "There should have been 2 documents returned") + assertTrue(listOf("four", "five").contains(docs[0].id), "An incorrect document was returned (${docs[0].id})") + assertTrue(listOf("four", "five").contains(docs[1].id), "An incorrect document was returned (${docs[1].id})") + } + + fun byJsonPathMatchOrdered(db: ThrowawayDatabase) { + JsonDocument.load(db) + val docs = db.conn.findByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)", listOf(Field.named("id"))) + assertEquals(2, docs.size, "There should have been 2 documents returned") + assertEquals("five|four", docs.joinToString("|") { it.id }, "The documents were not ordered correctly") + } + + fun byJsonPathNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertEquals( + 0, + db.conn.findByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)").size, + "There should have been no documents returned" + ) + } + + fun firstByFieldsMatchOne(db: ThrowawayDatabase) { + JsonDocument.load(db) + val doc = db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("value", "another"))) + assertNotNull(doc, "There should have been a document returned") + assertEquals("two", doc.id, "The incorrect document was returned") + } + + fun firstByFieldsMatchMany(db: ThrowawayDatabase) { + JsonDocument.load(db) + val doc = db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("sub.foo", "green"))) + assertNotNull(doc, "There should have been a document returned") + assertTrue(listOf("two", "four").contains(doc.id), "An incorrect document was returned (${doc.id})") + } + + fun firstByFieldsMatchOrdered(db: ThrowawayDatabase) { + JsonDocument.load(db) + val doc = db.conn.findFirstByFields( + TEST_TABLE, + listOf(Field.equal("sub.foo", "green")), + orderBy = listOf(Field.named("n:numValue DESC")) + ) + assertNotNull(doc, "There should have been a document returned") + assertEquals("four", doc.id, "An incorrect document was returned") + } + + fun firstByFieldsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertNull( + db.conn.findFirstByFields(TEST_TABLE, listOf(Field.equal("value", "absent"))), + "There should have been no document returned" + ) + } + + fun firstByContainsMatchOne(db: ThrowawayDatabase) { + JsonDocument.load(db) + val doc = db.conn.findFirstByContains>(TEST_TABLE, mapOf("value" to "FIRST!")) + assertNotNull(doc, "There should have been a document returned") + assertEquals("one", doc.id, "An incorrect document was returned") + } + + fun firstByContainsMatchMany(db: ThrowawayDatabase) { + JsonDocument.load(db) + val doc = db.conn.findFirstByContains>(TEST_TABLE, mapOf("value" to "purple")) + assertNotNull(doc, "There should have been a document returned") + assertTrue(listOf("four", "five").contains(doc.id), "An incorrect document was returned (${doc.id})") + } + + fun firstByContainsMatchOrdered(db: ThrowawayDatabase) { + JsonDocument.load(db) + val doc = db.conn.findFirstByContains>( + TEST_TABLE, + mapOf("value" to "purple"), + listOf(Field.named("sub.bar NULLS FIRST")) + ) + assertNotNull(doc, "There should have been a document returned") + assertEquals("five", doc.id, "An incorrect document was returned") + } + + fun firstByContainsNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertNull( + db.conn.findFirstByContains>( + TEST_TABLE, + mapOf("value" to "indigo") + ), "There should have been no document returned" + ) + } + + fun firstByJsonPathMatchOne(db: ThrowawayDatabase) { + JsonDocument.load(db) + val doc = db.conn.findFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10)") + assertNotNull(doc, "There should have been a document returned") + assertEquals("two", doc.id, "An incorrect document was returned") + } + + fun firstByJsonPathMatchMany(db: ThrowawayDatabase) { + JsonDocument.load(db) + val doc = db.conn.findFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ > 10)") + assertNotNull(doc, "There should have been a document returned") + assertTrue(listOf("four", "five").contains(doc.id), "An incorrect document was returned (${doc.id})") + } + + fun firstByJsonPathMatchOrdered(db: ThrowawayDatabase) { + JsonDocument.load(db) + val doc = db.conn.findFirstByJsonPath( + TEST_TABLE, + "$.numValue ? (@ > 10)", + listOf(Field.named("id DESC")) + ) + assertNotNull(doc, "There should have been a document returned") + assertEquals("four", doc.id, "An incorrect document was returned") + } + + fun firstByJsonPathNoMatch(db: ThrowawayDatabase) { + JsonDocument.load(db) + assertNull( + db.conn.findFirstByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)"), + "There should have been no document returned" + ) + } +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/common/Patch.kt b/src/kotlinx/src/test/kotlin/integration/PatchFunctions.kt similarity index 85% rename from src/jvm/src/test/kotlin/jvm/integration/common/Patch.kt rename to src/kotlinx/src/test/kotlin/integration/PatchFunctions.kt index 30c0731..d8e81ab 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/common/Patch.kt +++ b/src/kotlinx/src/test/kotlin/integration/PatchFunctions.kt @@ -1,8 +1,9 @@ -package solutions.bitbadger.documents.jvm.integration.common +package solutions.bitbadger.documents.kotlinx.tests.integration -import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.extensions.* -import solutions.bitbadger.documents.support.* +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.JsonDocument +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotNull @@ -11,12 +12,12 @@ import kotlin.test.assertTrue /** * Integration tests for the `Patch` object */ -object Patch { +object PatchFunctions { fun byIdMatch(db: ThrowawayDatabase) { JsonDocument.load(db) db.conn.patchById(TEST_TABLE, "one", mapOf("numValue" to 44)) - val doc = db.conn.findById(TEST_TABLE, "one", JsonDocument::class.java) + val doc = db.conn.findById(TEST_TABLE, "one") assertNotNull(doc, "There should have been a document returned") assertEquals("one", doc.id, "An incorrect document was returned") assertEquals(44, doc.numValue, "The document was not patched") @@ -53,7 +54,7 @@ object Patch { JsonDocument.load(db) val contains = mapOf("value" to "another") db.conn.patchByContains(TEST_TABLE, contains, mapOf("numValue" to 12)) - val doc = db.conn.findFirstByContains(TEST_TABLE, contains, JsonDocument::class.java) + val doc = db.conn.findFirstByContains>(TEST_TABLE, contains) assertNotNull(doc, "There should have been a document returned") assertEquals("two", doc.id, "The incorrect document was returned") assertEquals(12, doc.numValue, "The document was not updated") @@ -70,7 +71,7 @@ object Patch { JsonDocument.load(db) val path = "$.numValue ? (@ > 10)" db.conn.patchByJsonPath(TEST_TABLE, path, mapOf("value" to "blue")) - val docs = db.conn.findByJsonPath(TEST_TABLE, path, JsonDocument::class.java) + val docs = db.conn.findByJsonPath(TEST_TABLE, path) assertEquals(2, docs.size, "There should have been two documents returned") docs.forEach { assertTrue(listOf("four", "five").contains(it.id), "An incorrect document was returned (${it.id})") diff --git a/src/kotlinx/src/test/kotlin/integration/PgDB.kt b/src/kotlinx/src/test/kotlin/integration/PgDB.kt new file mode 100644 index 0000000..1256d1e --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/PgDB.kt @@ -0,0 +1,54 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.Results +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE + +/** + * A wrapper for a throwaway PostgreSQL database + */ +class PgDB : ThrowawayDatabase { + + private var dbName = "" + + init { + dbName = "throwaway_${AutoId.generateRandomString(8)}" + Configuration.connectionString = connString("postgres") + Configuration.dbConn().use { + it.customNonQuery("CREATE DATABASE $dbName") + } + Configuration.connectionString = connString(dbName) + } + + override val conn = Configuration.dbConn() + + init { + conn.ensureTable(TEST_TABLE) + } + + override fun close() { + conn.close() + Configuration.connectionString = connString("postgres") + Configuration.dbConn().use { + it.customNonQuery("DROP DATABASE $dbName") + } + Configuration.connectionString = null + } + + override fun dbObjectExists(name: String) = + conn.customScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = :name) AS it", + listOf(Parameter(":name", ParameterType.STRING, name)), Results::toExists) + + companion object { + + /** + * Create a connection string for the given database + * + * @param database The database to which the library should connect + * @return The connection string for the database + */ + private fun connString(database: String) = + "jdbc:postgresql://localhost/$database?user=postgres&password=postgres" + } +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/postgresql/CountIT.kt b/src/kotlinx/src/test/kotlin/integration/PostgreSQLCountIT.kt similarity index 65% rename from src/jvm/src/test/kotlin/jvm/integration/postgresql/CountIT.kt rename to src/kotlinx/src/test/kotlin/integration/PostgreSQLCountIT.kt index a80c2a7..93f56d7 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/postgresql/CountIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/PostgreSQLCountIT.kt @@ -1,47 +1,46 @@ -package solutions.bitbadger.documents.jvm.integration.postgresql +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.Count import kotlin.test.Test /** * PostgreSQL integration tests for the `Count` object / `count*` connection extension functions */ -@DisplayName("JVM | Kotlin | PostgreSQL: Count") -class CountIT { +@DisplayName("KotlinX | PostgreSQL: Count") +class PostgreSQLCountIT { @Test @DisplayName("all counts all documents") fun all() = - PgDB().use(Count::all) + PgDB().use(CountFunctions::all) @Test @DisplayName("byFields counts documents by a numeric value") fun byFieldsNumeric() = - PgDB().use(Count::byFieldsNumeric) + PgDB().use(CountFunctions::byFieldsNumeric) @Test @DisplayName("byFields counts documents by a alphanumeric value") fun byFieldsAlpha() = - PgDB().use(Count::byFieldsAlpha) + PgDB().use(CountFunctions::byFieldsAlpha) @Test @DisplayName("byContains counts documents when matches are found") fun byContainsMatch() = - PgDB().use(Count::byContainsMatch) + PgDB().use(CountFunctions::byContainsMatch) @Test @DisplayName("byContains counts documents when no matches are found") fun byContainsNoMatch() = - PgDB().use(Count::byContainsNoMatch) + PgDB().use(CountFunctions::byContainsNoMatch) @Test @DisplayName("byJsonPath counts documents when matches are found") fun byJsonPathMatch() = - PgDB().use(Count::byJsonPathMatch) + PgDB().use(CountFunctions::byJsonPathMatch) @Test @DisplayName("byJsonPath counts documents when no matches are found") fun byJsonPathNoMatch() = - PgDB().use(Count::byJsonPathNoMatch) + PgDB().use(CountFunctions::byJsonPathNoMatch) } diff --git a/src/jvm/src/test/kotlin/jvm/integration/postgresql/CustomIT.kt b/src/kotlinx/src/test/kotlin/integration/PostgreSQLCustomIT.kt similarity index 62% rename from src/jvm/src/test/kotlin/jvm/integration/postgresql/CustomIT.kt rename to src/kotlinx/src/test/kotlin/integration/PostgreSQLCustomIT.kt index 25272f1..e1ed60c 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/postgresql/CustomIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/PostgreSQLCustomIT.kt @@ -1,48 +1,47 @@ -package solutions.bitbadger.documents.jvm.integration.postgresql +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.Custom import kotlin.test.Test /** * PostgreSQL integration tests for the `Custom` object / `custom*` connection extension functions */ -@DisplayName("JVM | Kotlin | PostgreSQL: Custom") -class CustomIT { +@DisplayName("KotlinX | PostgreSQL: Custom") +class PostgreSQLCustomIT { @Test @DisplayName("list succeeds with empty list") fun listEmpty() = - PgDB().use(Custom::listEmpty) + PgDB().use(CustomFunctions::listEmpty) @Test @DisplayName("list succeeds with a non-empty list") fun listAll() = - PgDB().use(Custom::listAll) + PgDB().use(CustomFunctions::listAll) @Test @DisplayName("single succeeds when document not found") fun singleNone() = - PgDB().use(Custom::singleNone) + PgDB().use(CustomFunctions::singleNone) @Test @DisplayName("single succeeds when a document is found") fun singleOne() = - PgDB().use(Custom::singleOne) + PgDB().use(CustomFunctions::singleOne) @Test @DisplayName("nonQuery makes changes") fun nonQueryChanges() = - PgDB().use(Custom::nonQueryChanges) + PgDB().use(CustomFunctions::nonQueryChanges) @Test @DisplayName("nonQuery makes no changes when where clause matches nothing") fun nonQueryNoChanges() = - PgDB().use(Custom::nonQueryNoChanges) + PgDB().use(CustomFunctions::nonQueryNoChanges) @Test @DisplayName("scalar succeeds") fun scalar() = - PgDB().use(Custom::scalar) + PgDB().use(CustomFunctions::scalar) } diff --git a/src/kotlinx/src/test/kotlin/integration/PostgreSQLDefinitionIT.kt b/src/kotlinx/src/test/kotlin/integration/PostgreSQLDefinitionIT.kt new file mode 100644 index 0000000..4b898f0 --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/PostgreSQLDefinitionIT.kt @@ -0,0 +1,31 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import org.junit.jupiter.api.DisplayName +import kotlin.test.Test + +/** + * PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions + */ +@DisplayName("KotlinX | PostgreSQL: Definition") +class PostgreSQLDefinitionIT { + + @Test + @DisplayName("ensureTable creates table and index") + fun ensureTable() = + PgDB().use(DefinitionFunctions::ensureTable) + + @Test + @DisplayName("ensureFieldIndex creates an index") + fun ensureFieldIndex() = + PgDB().use(DefinitionFunctions::ensureFieldIndex) + + @Test + @DisplayName("ensureDocumentIndex creates a full index") + fun ensureDocumentIndexFull() = + PgDB().use(DefinitionFunctions::ensureDocumentIndexFull) + + @Test + @DisplayName("ensureDocumentIndex creates an optimized index") + fun ensureDocumentIndexOptimized() = + PgDB().use(DefinitionFunctions::ensureDocumentIndexOptimized) +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/postgresql/DeleteIT.kt b/src/kotlinx/src/test/kotlin/integration/PostgreSQLDeleteIT.kt similarity index 63% rename from src/jvm/src/test/kotlin/jvm/integration/postgresql/DeleteIT.kt rename to src/kotlinx/src/test/kotlin/integration/PostgreSQLDeleteIT.kt index 8b322d0..280acbf 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/postgresql/DeleteIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/PostgreSQLDeleteIT.kt @@ -1,52 +1,51 @@ -package solutions.bitbadger.documents.jvm.integration.postgresql +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.Delete import kotlin.test.Test /** * PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions */ -@DisplayName("JVM | Kotlin | PostgreSQL: Delete") -class DeleteIT { +@DisplayName("KotlinX | PostgreSQL: Delete") +class PostgreSQLDeleteIT { @Test @DisplayName("byId deletes a matching ID") fun byIdMatch() = - PgDB().use(Delete::byIdMatch) + PgDB().use(DeleteFunctions::byIdMatch) @Test @DisplayName("byId succeeds when no ID matches") fun byIdNoMatch() = - PgDB().use(Delete::byIdNoMatch) + PgDB().use(DeleteFunctions::byIdNoMatch) @Test @DisplayName("byFields deletes matching documents") fun byFieldsMatch() = - PgDB().use(Delete::byFieldsMatch) + PgDB().use(DeleteFunctions::byFieldsMatch) @Test @DisplayName("byFields succeeds when no documents match") fun byFieldsNoMatch() = - PgDB().use(Delete::byFieldsNoMatch) + PgDB().use(DeleteFunctions::byFieldsNoMatch) @Test @DisplayName("byContains deletes matching documents") fun byContainsMatch() = - PgDB().use(Delete::byContainsMatch) + PgDB().use(DeleteFunctions::byContainsMatch) @Test @DisplayName("byContains succeeds when no documents match") fun byContainsNoMatch() = - PgDB().use(Delete::byContainsNoMatch) + PgDB().use(DeleteFunctions::byContainsNoMatch) @Test @DisplayName("byJsonPath deletes matching documents") fun byJsonPathMatch() = - PgDB().use(Delete::byJsonPathMatch) + PgDB().use(DeleteFunctions::byJsonPathMatch) @Test @DisplayName("byJsonPath succeeds when no documents match") fun byJsonPathNoMatch() = - PgDB().use(Delete::byJsonPathNoMatch) + PgDB().use(DeleteFunctions::byJsonPathNoMatch) } diff --git a/src/jvm/src/test/kotlin/jvm/integration/postgresql/DocumentIT.kt b/src/kotlinx/src/test/kotlin/integration/PostgreSQLDocumentIT.kt similarity index 62% rename from src/jvm/src/test/kotlin/jvm/integration/postgresql/DocumentIT.kt rename to src/kotlinx/src/test/kotlin/integration/PostgreSQLDocumentIT.kt index ab3955c..6292f7f 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/postgresql/DocumentIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/PostgreSQLDocumentIT.kt @@ -1,57 +1,56 @@ -package solutions.bitbadger.documents.jvm.integration.postgresql +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.Document import kotlin.test.Test /** * PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions */ -@DisplayName("JVM | Kotlin | PostgreSQL: Document") -class DocumentIT { +@DisplayName("KotlinX | PostgreSQL: Document") +class PostgreSQLDocumentIT { @Test @DisplayName("insert works with default values") fun insertDefault() = - PgDB().use(Document::insertDefault) + PgDB().use(DocumentFunctions::insertDefault) @Test @DisplayName("insert fails with duplicate key") fun insertDupe() = - PgDB().use(Document::insertDupe) + PgDB().use(DocumentFunctions::insertDupe) @Test @DisplayName("insert succeeds with numeric auto IDs") fun insertNumAutoId() = - PgDB().use(Document::insertNumAutoId) + PgDB().use(DocumentFunctions::insertNumAutoId) @Test @DisplayName("insert succeeds with UUID auto ID") fun insertUUIDAutoId() = - PgDB().use(Document::insertUUIDAutoId) + PgDB().use(DocumentFunctions::insertUUIDAutoId) @Test @DisplayName("insert succeeds with random string auto ID") fun insertStringAutoId() = - PgDB().use(Document::insertStringAutoId) + PgDB().use(DocumentFunctions::insertStringAutoId) @Test @DisplayName("save updates an existing document") fun saveMatch() = - PgDB().use(Document::saveMatch) + PgDB().use(DocumentFunctions::saveMatch) @Test @DisplayName("save inserts a new document") fun saveNoMatch() = - PgDB().use(Document::saveNoMatch) + PgDB().use(DocumentFunctions::saveNoMatch) @Test @DisplayName("update replaces an existing document") fun updateMatch() = - PgDB().use(Document::updateMatch) + PgDB().use(DocumentFunctions::updateMatch) @Test @DisplayName("update succeeds when no document exists") fun updateNoMatch() = - PgDB().use(Document::updateNoMatch) + PgDB().use(DocumentFunctions::updateNoMatch) } diff --git a/src/jvm/src/test/kotlin/jvm/integration/postgresql/ExistsIT.kt b/src/kotlinx/src/test/kotlin/integration/PostgreSQLExistsIT.kt similarity index 64% rename from src/jvm/src/test/kotlin/jvm/integration/postgresql/ExistsIT.kt rename to src/kotlinx/src/test/kotlin/integration/PostgreSQLExistsIT.kt index c41b14e..db868e0 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/postgresql/ExistsIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/PostgreSQLExistsIT.kt @@ -1,52 +1,51 @@ -package solutions.bitbadger.documents.jvm.integration.postgresql +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.Exists import kotlin.test.Test /** * PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions */ -@DisplayName("JVM | Kotlin | PostgreSQL: Exists") -class ExistsIT { +@DisplayName("KotlinX | PostgreSQL: Exists") +class PostgreSQLExistsIT { @Test @DisplayName("byId returns true when a document matches the ID") fun byIdMatch() = - PgDB().use(Exists::byIdMatch) + PgDB().use(ExistsFunctions::byIdMatch) @Test @DisplayName("byId returns false when no document matches the ID") fun byIdNoMatch() = - PgDB().use(Exists::byIdNoMatch) + PgDB().use(ExistsFunctions::byIdNoMatch) @Test @DisplayName("byFields returns true when documents match") fun byFieldsMatch() = - PgDB().use(Exists::byFieldsMatch) + PgDB().use(ExistsFunctions::byFieldsMatch) @Test @DisplayName("byFields returns false when no documents match") fun byFieldsNoMatch() = - PgDB().use(Exists::byFieldsNoMatch) + PgDB().use(ExistsFunctions::byFieldsNoMatch) @Test @DisplayName("byContains returns true when documents match") fun byContainsMatch() = - PgDB().use(Exists::byContainsMatch) + PgDB().use(ExistsFunctions::byContainsMatch) @Test @DisplayName("byContains returns false when no documents match") fun byContainsNoMatch() = - PgDB().use(Exists::byContainsNoMatch) + PgDB().use(ExistsFunctions::byContainsNoMatch) @Test @DisplayName("byJsonPath returns true when documents match") fun byJsonPathMatch() = - PgDB().use(Exists::byJsonPathMatch) + PgDB().use(ExistsFunctions::byJsonPathMatch) @Test @DisplayName("byJsonPath returns false when no documents match") fun byJsonPathNoMatch() = - PgDB().use(Exists::byJsonPathNoMatch) + PgDB().use(ExistsFunctions::byJsonPathNoMatch) } diff --git a/src/jvm/src/test/kotlin/jvm/integration/postgresql/FindIT.kt b/src/kotlinx/src/test/kotlin/integration/PostgreSQLFindIT.kt similarity index 66% rename from src/jvm/src/test/kotlin/jvm/integration/postgresql/FindIT.kt rename to src/kotlinx/src/test/kotlin/integration/PostgreSQLFindIT.kt index 32bf564..c6eedea 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/postgresql/FindIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/PostgreSQLFindIT.kt @@ -1,172 +1,171 @@ -package solutions.bitbadger.documents.jvm.integration.postgresql +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.Find import kotlin.test.Test /** * PostgreSQL integration tests for the `Find` object / `find*` connection extension functions */ -@DisplayName("JVM | Kotlin | PostgreSQL: Find") -class FindIT { +@DisplayName("KotlinX | PostgreSQL: Find") +class PostgreSQLFindIT { @Test @DisplayName("all retrieves all documents") fun allDefault() = - PgDB().use(Find::allDefault) + PgDB().use(FindFunctions::allDefault) @Test @DisplayName("all sorts data ascending") fun allAscending() = - PgDB().use(Find::allAscending) + PgDB().use(FindFunctions::allAscending) @Test @DisplayName("all sorts data descending") fun allDescending() = - PgDB().use(Find::allDescending) + PgDB().use(FindFunctions::allDescending) @Test @DisplayName("all sorts data numerically") fun allNumOrder() = - PgDB().use(Find::allNumOrder) + PgDB().use(FindFunctions::allNumOrder) @Test @DisplayName("all succeeds with an empty table") fun allEmpty() = - PgDB().use(Find::allEmpty) + PgDB().use(FindFunctions::allEmpty) @Test @DisplayName("byId retrieves a document via a string ID") fun byIdString() = - PgDB().use(Find::byIdString) + PgDB().use(FindFunctions::byIdString) @Test @DisplayName("byId retrieves a document via a numeric ID") fun byIdNumber() = - PgDB().use(Find::byIdNumber) + PgDB().use(FindFunctions::byIdNumber) @Test @DisplayName("byId returns null when a matching ID is not found") fun byIdNotFound() = - PgDB().use(Find::byIdNotFound) + PgDB().use(FindFunctions::byIdNotFound) @Test @DisplayName("byFields retrieves matching documents") fun byFieldsMatch() = - PgDB().use(Find::byFieldsMatch) + PgDB().use(FindFunctions::byFieldsMatch) @Test @DisplayName("byFields retrieves ordered matching documents") fun byFieldsMatchOrdered() = - PgDB().use(Find::byFieldsMatchOrdered) + PgDB().use(FindFunctions::byFieldsMatchOrdered) @Test @DisplayName("byFields retrieves matching documents with a numeric IN clause") fun byFieldsMatchNumIn() = - PgDB().use(Find::byFieldsMatchNumIn) + PgDB().use(FindFunctions::byFieldsMatchNumIn) @Test @DisplayName("byFields succeeds when no documents match") fun byFieldsNoMatch() = - PgDB().use(Find::byFieldsNoMatch) + PgDB().use(FindFunctions::byFieldsNoMatch) @Test @DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison") fun byFieldsMatchInArray() = - PgDB().use(Find::byFieldsMatchInArray) + PgDB().use(FindFunctions::byFieldsMatchInArray) @Test @DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison") fun byFieldsNoMatchInArray() = - PgDB().use(Find::byFieldsNoMatchInArray) + PgDB().use(FindFunctions::byFieldsNoMatchInArray) @Test @DisplayName("byContains retrieves matching documents") fun byContainsMatch() = - PgDB().use(Find::byContainsMatch) + PgDB().use(FindFunctions::byContainsMatch) @Test @DisplayName("byContains retrieves ordered matching documents") fun byContainsMatchOrdered() = - PgDB().use(Find::byContainsMatchOrdered) + PgDB().use(FindFunctions::byContainsMatchOrdered) @Test @DisplayName("byContains succeeds when no documents match") fun byContainsNoMatch() = - PgDB().use(Find::byContainsNoMatch) + PgDB().use(FindFunctions::byContainsNoMatch) @Test @DisplayName("byJsonPath retrieves matching documents") fun byJsonPathMatch() = - PgDB().use(Find::byJsonPathMatch) + PgDB().use(FindFunctions::byJsonPathMatch) @Test @DisplayName("byJsonPath retrieves ordered matching documents") fun byJsonPathMatchOrdered() = - PgDB().use(Find::byJsonPathMatchOrdered) + PgDB().use(FindFunctions::byJsonPathMatchOrdered) @Test @DisplayName("byJsonPath succeeds when no documents match") fun byJsonPathNoMatch() = - PgDB().use(Find::byJsonPathNoMatch) + PgDB().use(FindFunctions::byJsonPathNoMatch) @Test @DisplayName("firstByFields retrieves a matching document") fun firstByFieldsMatchOne() = - PgDB().use(Find::firstByFieldsMatchOne) + PgDB().use(FindFunctions::firstByFieldsMatchOne) @Test @DisplayName("firstByFields retrieves a matching document among many") fun firstByFieldsMatchMany() = - PgDB().use(Find::firstByFieldsMatchMany) + PgDB().use(FindFunctions::firstByFieldsMatchMany) @Test @DisplayName("firstByFields retrieves a matching document among many (ordered)") fun firstByFieldsMatchOrdered() = - PgDB().use(Find::firstByFieldsMatchOrdered) + PgDB().use(FindFunctions::firstByFieldsMatchOrdered) @Test @DisplayName("firstByFields returns null when no document matches") fun firstByFieldsNoMatch() = - PgDB().use(Find::firstByFieldsNoMatch) + PgDB().use(FindFunctions::firstByFieldsNoMatch) @Test @DisplayName("firstByContains retrieves a matching document") fun firstByContainsMatchOne() = - PgDB().use(Find::firstByContainsMatchOne) + PgDB().use(FindFunctions::firstByContainsMatchOne) @Test @DisplayName("firstByContains retrieves a matching document among many") fun firstByContainsMatchMany() = - PgDB().use(Find::firstByContainsMatchMany) + PgDB().use(FindFunctions::firstByContainsMatchMany) @Test @DisplayName("firstByContains retrieves a matching document among many (ordered)") fun firstByContainsMatchOrdered() = - PgDB().use(Find::firstByContainsMatchOrdered) + PgDB().use(FindFunctions::firstByContainsMatchOrdered) @Test @DisplayName("firstByContains returns null when no document matches") fun firstByContainsNoMatch() = - PgDB().use(Find::firstByContainsNoMatch) + PgDB().use(FindFunctions::firstByContainsNoMatch) @Test @DisplayName("firstByJsonPath retrieves a matching document") fun firstByJsonPathMatchOne() = - PgDB().use(Find::firstByJsonPathMatchOne) + PgDB().use(FindFunctions::firstByJsonPathMatchOne) @Test @DisplayName("firstByJsonPath retrieves a matching document among many") fun firstByJsonPathMatchMany() = - PgDB().use(Find::firstByJsonPathMatchMany) + PgDB().use(FindFunctions::firstByJsonPathMatchMany) @Test @DisplayName("firstByJsonPath retrieves a matching document among many (ordered)") fun firstByJsonPathMatchOrdered() = - PgDB().use(Find::firstByJsonPathMatchOrdered) + PgDB().use(FindFunctions::firstByJsonPathMatchOrdered) @Test @DisplayName("firstByJsonPath returns null when no document matches") fun firstByJsonPathNoMatch() = - PgDB().use(Find::firstByJsonPathNoMatch) + PgDB().use(FindFunctions::firstByJsonPathNoMatch) } diff --git a/src/jvm/src/test/kotlin/jvm/integration/postgresql/PatchIT.kt b/src/kotlinx/src/test/kotlin/integration/PostgreSQLPatchIT.kt similarity index 63% rename from src/jvm/src/test/kotlin/jvm/integration/postgresql/PatchIT.kt rename to src/kotlinx/src/test/kotlin/integration/PostgreSQLPatchIT.kt index 4a9c2ee..afb7066 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/postgresql/PatchIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/PostgreSQLPatchIT.kt @@ -1,52 +1,51 @@ -package solutions.bitbadger.documents.jvm.integration.postgresql +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.Patch import kotlin.test.Test /** * PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions */ -@DisplayName("JVM | Kotlin | PostgreSQL: Patch") -class PatchIT { +@DisplayName("KotlinX | PostgreSQL: Patch") +class PostgreSQLPatchIT { @Test @DisplayName("byId patches an existing document") fun byIdMatch() = - PgDB().use(Patch::byIdMatch) + PgDB().use(PatchFunctions::byIdMatch) @Test @DisplayName("byId succeeds for a non-existent document") fun byIdNoMatch() = - PgDB().use(Patch::byIdNoMatch) + PgDB().use(PatchFunctions::byIdNoMatch) @Test @DisplayName("byFields patches matching document") fun byFieldsMatch() = - PgDB().use(Patch::byFieldsMatch) + PgDB().use(PatchFunctions::byFieldsMatch) @Test @DisplayName("byFields succeeds when no documents match") fun byFieldsNoMatch() = - PgDB().use(Patch::byFieldsNoMatch) + PgDB().use(PatchFunctions::byFieldsNoMatch) @Test @DisplayName("byContains patches matching document") fun byContainsMatch() = - PgDB().use(Patch::byContainsMatch) + PgDB().use(PatchFunctions::byContainsMatch) @Test @DisplayName("byContains succeeds when no documents match") fun byContainsNoMatch() = - PgDB().use(Patch::byContainsNoMatch) + PgDB().use(PatchFunctions::byContainsNoMatch) @Test @DisplayName("byJsonPath patches matching document") fun byJsonPathMatch() = - PgDB().use(Patch::byJsonPathMatch) + PgDB().use(PatchFunctions::byJsonPathMatch) @Test @DisplayName("byJsonPath succeeds when no documents match") fun byJsonPathNoMatch() = - PgDB().use(Patch::byJsonPathNoMatch) + PgDB().use(PatchFunctions::byJsonPathNoMatch) } diff --git a/src/jvm/src/test/kotlin/jvm/integration/postgresql/RemoveFieldsIT.kt b/src/kotlinx/src/test/kotlin/integration/PostgreSQLRemoveFieldsIT.kt similarity index 63% rename from src/jvm/src/test/kotlin/jvm/integration/postgresql/RemoveFieldsIT.kt rename to src/kotlinx/src/test/kotlin/integration/PostgreSQLRemoveFieldsIT.kt index 968106f..13f07ec 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/postgresql/RemoveFieldsIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/PostgreSQLRemoveFieldsIT.kt @@ -1,72 +1,71 @@ -package solutions.bitbadger.documents.jvm.integration.postgresql +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.RemoveFields import kotlin.test.Test /** * PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions */ -@DisplayName("JVM | Kotlin | PostgreSQL: RemoveFields") -class RemoveFieldsIT { +@DisplayName("KotlinX | PostgreSQL: RemoveFields") +class PostgreSQLRemoveFieldsIT { @Test @DisplayName("byId removes fields from an existing document") fun byIdMatchFields() = - PgDB().use(RemoveFields::byIdMatchFields) + PgDB().use(RemoveFieldsFunctions::byIdMatchFields) @Test @DisplayName("byId succeeds when fields do not exist on an existing document") fun byIdMatchNoFields() = - PgDB().use(RemoveFields::byIdMatchNoFields) + PgDB().use(RemoveFieldsFunctions::byIdMatchNoFields) @Test @DisplayName("byId succeeds when no document exists") fun byIdNoMatch() = - PgDB().use(RemoveFields::byIdNoMatch) + PgDB().use(RemoveFieldsFunctions::byIdNoMatch) @Test @DisplayName("byFields removes fields from matching documents") fun byFieldsMatchFields() = - PgDB().use(RemoveFields::byFieldsMatchFields) + PgDB().use(RemoveFieldsFunctions::byFieldsMatchFields) @Test @DisplayName("byFields succeeds when fields do not exist on matching documents") fun byFieldsMatchNoFields() = - PgDB().use(RemoveFields::byFieldsMatchNoFields) + PgDB().use(RemoveFieldsFunctions::byFieldsMatchNoFields) @Test @DisplayName("byFields succeeds when no matching documents exist") fun byFieldsNoMatch() = - PgDB().use(RemoveFields::byFieldsNoMatch) + PgDB().use(RemoveFieldsFunctions::byFieldsNoMatch) @Test @DisplayName("byContains removes fields from matching documents") fun byContainsMatchFields() = - PgDB().use(RemoveFields::byContainsMatchFields) + PgDB().use(RemoveFieldsFunctions::byContainsMatchFields) @Test @DisplayName("byContains succeeds when fields do not exist on matching documents") fun byContainsMatchNoFields() = - PgDB().use(RemoveFields::byContainsMatchNoFields) + PgDB().use(RemoveFieldsFunctions::byContainsMatchNoFields) @Test @DisplayName("byContains succeeds when no matching documents exist") fun byContainsNoMatch() = - PgDB().use(RemoveFields::byContainsNoMatch) + PgDB().use(RemoveFieldsFunctions::byContainsNoMatch) @Test @DisplayName("byJsonPath removes fields from matching documents") fun byJsonPathMatchFields() = - PgDB().use(RemoveFields::byJsonPathMatchFields) + PgDB().use(RemoveFieldsFunctions::byJsonPathMatchFields) @Test @DisplayName("byJsonPath succeeds when fields do not exist on matching documents") fun byJsonPathMatchNoFields() = - PgDB().use(RemoveFields::byJsonPathMatchNoFields) + PgDB().use(RemoveFieldsFunctions::byJsonPathMatchNoFields) @Test @DisplayName("byJsonPath succeeds when no matching documents exist") fun byJsonPathNoMatch() = - PgDB().use(RemoveFields::byJsonPathNoMatch) + PgDB().use(RemoveFieldsFunctions::byJsonPathNoMatch) } diff --git a/src/jvm/src/test/kotlin/jvm/integration/common/RemoveFields.kt b/src/kotlinx/src/test/kotlin/integration/RemoveFieldsFunctions.kt similarity index 86% rename from src/jvm/src/test/kotlin/jvm/integration/common/RemoveFields.kt rename to src/kotlinx/src/test/kotlin/integration/RemoveFieldsFunctions.kt index 2c90f1b..068ed52 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/common/RemoveFields.kt +++ b/src/kotlinx/src/test/kotlin/integration/RemoveFieldsFunctions.kt @@ -1,20 +1,20 @@ -package solutions.bitbadger.documents.jvm.integration.common +package solutions.bitbadger.documents.kotlinx.tests.integration -import solutions.bitbadger.documents.Field -import solutions.bitbadger.documents.extensions.* -import solutions.bitbadger.documents.support.* +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.JsonDocument +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE import kotlin.test.* - /** * Integration tests for the `RemoveFields` object */ -object RemoveFields { +object RemoveFieldsFunctions { fun byIdMatchFields(db: ThrowawayDatabase) { JsonDocument.load(db) db.conn.removeFieldsById(TEST_TABLE, "two", listOf("sub", "value")) - val doc = db.conn.findById(TEST_TABLE, "two", JsonDocument::class.java) + val doc = db.conn.findById(TEST_TABLE, "two") assertNotNull(doc, "There should have been a document returned") assertEquals("", doc.value, "The value should have been empty") assertNull(doc.sub, "The sub-document should have been removed") @@ -36,7 +36,7 @@ object RemoveFields { JsonDocument.load(db) val fields = listOf(Field.equal("numValue", 17)) db.conn.removeFieldsByFields(TEST_TABLE, fields, listOf("sub")) - val doc = db.conn.findFirstByFields(TEST_TABLE, fields, JsonDocument::class.java) + val doc = db.conn.findFirstByFields(TEST_TABLE, fields) assertNotNull(doc, "The document should have been returned") assertEquals("four", doc.id, "An incorrect document was returned") assertNull(doc.sub, "The sub-document should have been removed") @@ -59,7 +59,7 @@ object RemoveFields { JsonDocument.load(db) val criteria = mapOf("sub" to mapOf("foo" to "green")) db.conn.removeFieldsByContains(TEST_TABLE, criteria, listOf("value")) - val docs = db.conn.findByContains(TEST_TABLE, criteria, JsonDocument::class.java) + val docs = db.conn.findByContains>>(TEST_TABLE, criteria) assertEquals(2, docs.size, "There should have been 2 documents returned") docs.forEach { assertTrue(listOf("two", "four").contains(it.id), "An incorrect document was returned (${it.id})") @@ -85,7 +85,7 @@ object RemoveFields { JsonDocument.load(db) val path = "$.value ? (@ == \"purple\")" db.conn.removeFieldsByJsonPath(TEST_TABLE, path, listOf("sub")) - val docs = db.conn.findByJsonPath(TEST_TABLE, path, JsonDocument::class.java) + val docs = db.conn.findByJsonPath(TEST_TABLE, path) assertEquals(2, docs.size, "There should have been 2 documents returned") docs.forEach { assertTrue(listOf("four", "five").contains(it.id), "An incorrect document was returned (${it.id})") diff --git a/src/kotlinx/src/test/kotlin/integration/SQLiteCountIT.kt b/src/kotlinx/src/test/kotlin/integration/SQLiteCountIT.kt new file mode 100644 index 0000000..32b256e --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/SQLiteCountIT.kt @@ -0,0 +1,40 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.DocumentException +import kotlin.test.Test + +/** + * SQLite integration tests for the `Count` object / `count*` connection extension functions + */ +@DisplayName("KotlinX | SQLite: Count") +class SQLiteCountIT { + + @Test + @DisplayName("all counts all documents") + fun all() = + SQLiteDB().use(CountFunctions::all) + + @Test + @DisplayName("byFields counts documents by a numeric value") + fun byFieldsNumeric() = + SQLiteDB().use(CountFunctions::byFieldsNumeric) + + @Test + @DisplayName("byFields counts documents by a alphanumeric value") + fun byFieldsAlpha() = + SQLiteDB().use(CountFunctions::byFieldsAlpha) + + @Test + @DisplayName("byContains fails") + fun byContainsMatch() { + assertThrows { SQLiteDB().use(CountFunctions::byContainsMatch) } + } + + @Test + @DisplayName("byJsonPath fails") + fun byJsonPathMatch() { + assertThrows { SQLiteDB().use(CountFunctions::byJsonPathMatch) } + } +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/sqlite/CustomIT.kt b/src/kotlinx/src/test/kotlin/integration/SQLiteCustomIT.kt similarity index 61% rename from src/jvm/src/test/kotlin/jvm/integration/sqlite/CustomIT.kt rename to src/kotlinx/src/test/kotlin/integration/SQLiteCustomIT.kt index 03ef8ee..ce06f4c 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/sqlite/CustomIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/SQLiteCustomIT.kt @@ -1,47 +1,46 @@ -package solutions.bitbadger.documents.jvm.integration.sqlite +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.Custom import kotlin.test.Test /** * SQLite integration tests for the `Custom` object / `custom*` connection extension functions */ -@DisplayName("JVM | Kotlin | SQLite: Custom") -class CustomIT { +@DisplayName("KotlinX | SQLite: Custom") +class SQLiteCustomIT { @Test @DisplayName("list succeeds with empty list") fun listEmpty() = - SQLiteDB().use(Custom::listEmpty) + SQLiteDB().use(CustomFunctions::listEmpty) @Test @DisplayName("list succeeds with a non-empty list") fun listAll() = - SQLiteDB().use(Custom::listAll) + SQLiteDB().use(CustomFunctions::listAll) @Test @DisplayName("single succeeds when document not found") fun singleNone() = - SQLiteDB().use(Custom::singleNone) + SQLiteDB().use(CustomFunctions::singleNone) @Test @DisplayName("single succeeds when a document is found") fun singleOne() = - SQLiteDB().use(Custom::singleOne) + SQLiteDB().use(CustomFunctions::singleOne) @Test @DisplayName("nonQuery makes changes") fun nonQueryChanges() = - SQLiteDB().use(Custom::nonQueryChanges) + SQLiteDB().use(CustomFunctions::nonQueryChanges) @Test @DisplayName("nonQuery makes no changes when where clause matches nothing") fun nonQueryNoChanges() = - SQLiteDB().use(Custom::nonQueryNoChanges) + SQLiteDB().use(CustomFunctions::nonQueryNoChanges) @Test @DisplayName("scalar succeeds") fun scalar() = - SQLiteDB().use(Custom::scalar) + SQLiteDB().use(CustomFunctions::scalar) } diff --git a/src/kotlinx/src/test/kotlin/integration/SQLiteDB.kt b/src/kotlinx/src/test/kotlin/integration/SQLiteDB.kt new file mode 100644 index 0000000..8ea554d --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/SQLiteDB.kt @@ -0,0 +1,35 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import solutions.bitbadger.documents.* +import solutions.bitbadger.documents.kotlinx.extensions.* +import solutions.bitbadger.documents.kotlinx.tests.TEST_TABLE +import solutions.bitbadger.documents.kotlinx.Results +import java.io.File + +/** + * A wrapper for a throwaway SQLite database + */ +class SQLiteDB : ThrowawayDatabase { + + private var dbName = "" + + init { + dbName = "test-db-${AutoId.generateRandomString(8)}.db" + Configuration.connectionString = "jdbc:sqlite:$dbName" + } + + override val conn = Configuration.dbConn() + + init { + conn.ensureTable(TEST_TABLE) + } + + override fun close() { + conn.close() + File(dbName).delete() + } + + override fun dbObjectExists(name: String) = + conn.customScalar("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE name = :name) AS it", + listOf(Parameter(":name", ParameterType.STRING, name)), Results::toExists) +} diff --git a/src/kotlinx/src/test/kotlin/integration/SQLiteDefinitionIT.kt b/src/kotlinx/src/test/kotlin/integration/SQLiteDefinitionIT.kt new file mode 100644 index 0000000..2793631 --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/SQLiteDefinitionIT.kt @@ -0,0 +1,35 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.DocumentException +import kotlin.test.Test + +/** + * SQLite integration tests for the `Definition` object / `ensure*` connection extension functions + */ +@DisplayName("KotlinX | SQLite: Definition") +class SQLiteDefinitionIT { + + @Test + @DisplayName("ensureTable creates table and index") + fun ensureTable() = + SQLiteDB().use(DefinitionFunctions::ensureTable) + + @Test + @DisplayName("ensureFieldIndex creates an index") + fun ensureFieldIndex() = + SQLiteDB().use(DefinitionFunctions::ensureFieldIndex) + + @Test + @DisplayName("ensureDocumentIndex fails for full index") + fun ensureDocumentIndexFull() { + assertThrows { SQLiteDB().use(DefinitionFunctions::ensureDocumentIndexFull) } + } + + @Test + @DisplayName("ensureDocumentIndex fails for optimized index") + fun ensureDocumentIndexOptimized() { + assertThrows { SQLiteDB().use(DefinitionFunctions::ensureDocumentIndexOptimized) } + } +} diff --git a/src/kotlinx/src/test/kotlin/integration/SQLiteDeleteIT.kt b/src/kotlinx/src/test/kotlin/integration/SQLiteDeleteIT.kt new file mode 100644 index 0000000..0480e4a --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/SQLiteDeleteIT.kt @@ -0,0 +1,45 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.DocumentException +import kotlin.test.Test + +/** + * SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions + */ +@DisplayName("KotlinX | SQLite: Delete") +class SQLiteDeleteIT { + + @Test + @DisplayName("byId deletes a matching ID") + fun byIdMatch() = + SQLiteDB().use(DeleteFunctions::byIdMatch) + + @Test + @DisplayName("byId succeeds when no ID matches") + fun byIdNoMatch() = + SQLiteDB().use(DeleteFunctions::byIdNoMatch) + + @Test + @DisplayName("byFields deletes matching documents") + fun byFieldsMatch() = + SQLiteDB().use(DeleteFunctions::byFieldsMatch) + + @Test + @DisplayName("byFields succeeds when no documents match") + fun byFieldsNoMatch() = + SQLiteDB().use(DeleteFunctions::byFieldsNoMatch) + + @Test + @DisplayName("byContains fails") + fun byContainsFails() { + assertThrows { SQLiteDB().use(DeleteFunctions::byContainsMatch) } + } + + @Test + @DisplayName("byJsonPath fails") + fun byJsonPathFails() { + assertThrows { SQLiteDB().use(DeleteFunctions::byJsonPathMatch) } + } +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/sqlite/DocumentIT.kt b/src/kotlinx/src/test/kotlin/integration/SQLiteDocumentIT.kt similarity index 61% rename from src/jvm/src/test/kotlin/jvm/integration/sqlite/DocumentIT.kt rename to src/kotlinx/src/test/kotlin/integration/SQLiteDocumentIT.kt index 1531117..c220dc1 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/sqlite/DocumentIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/SQLiteDocumentIT.kt @@ -1,57 +1,56 @@ -package solutions.bitbadger.documents.jvm.integration.sqlite +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName -import solutions.bitbadger.documents.jvm.integration.common.Document import kotlin.test.Test /** * SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions */ -@DisplayName("JVM | Kotlin | SQLite: Document") -class DocumentIT { +@DisplayName("KotlinX | SQLite: Document") +class SQLiteDocumentIT { @Test @DisplayName("insert works with default values") fun insertDefault() = - SQLiteDB().use(Document::insertDefault) + SQLiteDB().use(DocumentFunctions::insertDefault) @Test @DisplayName("insert fails with duplicate key") fun insertDupe() = - SQLiteDB().use(Document::insertDupe) + SQLiteDB().use(DocumentFunctions::insertDupe) @Test @DisplayName("insert succeeds with numeric auto IDs") fun insertNumAutoId() = - SQLiteDB().use(Document::insertNumAutoId) + SQLiteDB().use(DocumentFunctions::insertNumAutoId) @Test @DisplayName("insert succeeds with UUID auto ID") fun insertUUIDAutoId() = - SQLiteDB().use(Document::insertUUIDAutoId) + SQLiteDB().use(DocumentFunctions::insertUUIDAutoId) @Test @DisplayName("insert succeeds with random string auto ID") fun insertStringAutoId() = - SQLiteDB().use(Document::insertStringAutoId) + SQLiteDB().use(DocumentFunctions::insertStringAutoId) @Test @DisplayName("save updates an existing document") fun saveMatch() = - SQLiteDB().use(Document::saveMatch) + SQLiteDB().use(DocumentFunctions::saveMatch) @Test @DisplayName("save inserts a new document") fun saveNoMatch() = - SQLiteDB().use(Document::saveNoMatch) + SQLiteDB().use(DocumentFunctions::saveNoMatch) @Test @DisplayName("update replaces an existing document") fun updateMatch() = - SQLiteDB().use(Document::updateMatch) + SQLiteDB().use(DocumentFunctions::updateMatch) @Test @DisplayName("update succeeds when no document exists") fun updateNoMatch() = - SQLiteDB().use(Document::updateNoMatch) + SQLiteDB().use(DocumentFunctions::updateNoMatch) } diff --git a/src/kotlinx/src/test/kotlin/integration/SQLiteExistsIT.kt b/src/kotlinx/src/test/kotlin/integration/SQLiteExistsIT.kt new file mode 100644 index 0000000..843ee2a --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/SQLiteExistsIT.kt @@ -0,0 +1,45 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.DocumentException +import kotlin.test.Test + +/** + * SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions + */ +@DisplayName("KotlinX | SQLite: Exists") +class SQLiteExistsIT { + + @Test + @DisplayName("byId returns true when a document matches the ID") + fun byIdMatch() = + SQLiteDB().use(ExistsFunctions::byIdMatch) + + @Test + @DisplayName("byId returns false when no document matches the ID") + fun byIdNoMatch() = + SQLiteDB().use(ExistsFunctions::byIdNoMatch) + + @Test + @DisplayName("byFields returns true when documents match") + fun byFieldsMatch() = + SQLiteDB().use(ExistsFunctions::byFieldsMatch) + + @Test + @DisplayName("byFields returns false when no documents match") + fun byFieldsNoMatch() = + SQLiteDB().use(ExistsFunctions::byFieldsNoMatch) + + @Test + @DisplayName("byContains fails") + fun byContainsFails() { + assertThrows { SQLiteDB().use(ExistsFunctions::byContainsMatch) } + } + + @Test + @DisplayName("byJsonPath fails") + fun byJsonPathFails() { + assertThrows { SQLiteDB().use(ExistsFunctions::byJsonPathMatch) } + } +} diff --git a/src/jvm/src/test/kotlin/jvm/integration/sqlite/FindIT.kt b/src/kotlinx/src/test/kotlin/integration/SQLiteFindIT.kt similarity index 62% rename from src/jvm/src/test/kotlin/jvm/integration/sqlite/FindIT.kt rename to src/kotlinx/src/test/kotlin/integration/SQLiteFindIT.kt index 8dab546..9350620 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/sqlite/FindIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/SQLiteFindIT.kt @@ -1,128 +1,127 @@ -package solutions.bitbadger.documents.jvm.integration.sqlite +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException -import solutions.bitbadger.documents.jvm.integration.common.Find import kotlin.test.Test /** * SQLite integration tests for the `Find` object / `find*` connection extension functions */ -@DisplayName("JVM | Kotlin | SQLite: Find") -class FindIT { +@DisplayName("KotlinX | SQLite: Find") +class SQLiteFindIT { @Test @DisplayName("all retrieves all documents") fun allDefault() = - SQLiteDB().use(Find::allDefault) + SQLiteDB().use(FindFunctions::allDefault) @Test @DisplayName("all sorts data ascending") fun allAscending() = - SQLiteDB().use(Find::allAscending) + SQLiteDB().use(FindFunctions::allAscending) @Test @DisplayName("all sorts data descending") fun allDescending() = - SQLiteDB().use(Find::allDescending) + SQLiteDB().use(FindFunctions::allDescending) @Test @DisplayName("all sorts data numerically") fun allNumOrder() = - SQLiteDB().use(Find::allNumOrder) + SQLiteDB().use(FindFunctions::allNumOrder) @Test @DisplayName("all succeeds with an empty table") fun allEmpty() = - SQLiteDB().use(Find::allEmpty) + SQLiteDB().use(FindFunctions::allEmpty) @Test @DisplayName("byId retrieves a document via a string ID") fun byIdString() = - SQLiteDB().use(Find::byIdString) + SQLiteDB().use(FindFunctions::byIdString) @Test @DisplayName("byId retrieves a document via a numeric ID") fun byIdNumber() = - SQLiteDB().use(Find::byIdNumber) + SQLiteDB().use(FindFunctions::byIdNumber) @Test @DisplayName("byId returns null when a matching ID is not found") fun byIdNotFound() = - SQLiteDB().use(Find::byIdNotFound) + SQLiteDB().use(FindFunctions::byIdNotFound) @Test @DisplayName("byFields retrieves matching documents") fun byFieldsMatch() = - SQLiteDB().use(Find::byFieldsMatch) + SQLiteDB().use(FindFunctions::byFieldsMatch) @Test @DisplayName("byFields retrieves ordered matching documents") fun byFieldsMatchOrdered() = - SQLiteDB().use(Find::byFieldsMatchOrdered) + SQLiteDB().use(FindFunctions::byFieldsMatchOrdered) @Test @DisplayName("byFields retrieves matching documents with a numeric IN clause") fun byFieldsMatchNumIn() = - SQLiteDB().use(Find::byFieldsMatchNumIn) + SQLiteDB().use(FindFunctions::byFieldsMatchNumIn) @Test @DisplayName("byFields succeeds when no documents match") fun byFieldsNoMatch() = - SQLiteDB().use(Find::byFieldsNoMatch) + SQLiteDB().use(FindFunctions::byFieldsNoMatch) @Test @DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison") fun byFieldsMatchInArray() = - SQLiteDB().use(Find::byFieldsMatchInArray) + SQLiteDB().use(FindFunctions::byFieldsMatchInArray) @Test @DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison") fun byFieldsNoMatchInArray() = - SQLiteDB().use(Find::byFieldsNoMatchInArray) + SQLiteDB().use(FindFunctions::byFieldsNoMatchInArray) @Test @DisplayName("byContains fails") fun byContainsFails() { - assertThrows { SQLiteDB().use(Find::byContainsMatch) } + assertThrows { SQLiteDB().use(FindFunctions::byContainsMatch) } } @Test @DisplayName("byJsonPath fails") fun byJsonPathFails() { - assertThrows { SQLiteDB().use(Find::byJsonPathMatch) } + assertThrows { SQLiteDB().use(FindFunctions::byJsonPathMatch) } } @Test @DisplayName("firstByFields retrieves a matching document") fun firstByFieldsMatchOne() = - SQLiteDB().use(Find::firstByFieldsMatchOne) + SQLiteDB().use(FindFunctions::firstByFieldsMatchOne) @Test @DisplayName("firstByFields retrieves a matching document among many") fun firstByFieldsMatchMany() = - SQLiteDB().use(Find::firstByFieldsMatchMany) + SQLiteDB().use(FindFunctions::firstByFieldsMatchMany) @Test @DisplayName("firstByFields retrieves a matching document among many (ordered)") fun firstByFieldsMatchOrdered() = - SQLiteDB().use(Find::firstByFieldsMatchOrdered) + SQLiteDB().use(FindFunctions::firstByFieldsMatchOrdered) @Test @DisplayName("firstByFields returns null when no document matches") fun firstByFieldsNoMatch() = - SQLiteDB().use(Find::firstByFieldsNoMatch) + SQLiteDB().use(FindFunctions::firstByFieldsNoMatch) @Test @DisplayName("firstByContains fails") fun firstByContainsFails() { - assertThrows { SQLiteDB().use(Find::firstByContainsMatchOne) } + assertThrows { SQLiteDB().use(FindFunctions::firstByContainsMatchOne) } } @Test @DisplayName("firstByJsonPath fails") fun firstByJsonPathFails() { - assertThrows { SQLiteDB().use(Find::firstByJsonPathMatchOne) } + assertThrows { SQLiteDB().use(FindFunctions::firstByJsonPathMatchOne) } } } diff --git a/src/kotlinx/src/test/kotlin/integration/SQLitePatchIT.kt b/src/kotlinx/src/test/kotlin/integration/SQLitePatchIT.kt new file mode 100644 index 0000000..e119839 --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/SQLitePatchIT.kt @@ -0,0 +1,45 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.assertThrows +import solutions.bitbadger.documents.DocumentException +import kotlin.test.Test + +/** + * SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions + */ +@DisplayName("KotlinX | SQLite: Patch") +class SQLitePatchIT { + + @Test + @DisplayName("byId patches an existing document") + fun byIdMatch() = + SQLiteDB().use(PatchFunctions::byIdMatch) + + @Test + @DisplayName("byId succeeds for a non-existent document") + fun byIdNoMatch() = + SQLiteDB().use(PatchFunctions::byIdNoMatch) + + @Test + @DisplayName("byFields patches matching document") + fun byFieldsMatch() = + SQLiteDB().use(PatchFunctions::byFieldsMatch) + + @Test + @DisplayName("byFields succeeds when no documents match") + fun byFieldsNoMatch() = + SQLiteDB().use(PatchFunctions::byFieldsNoMatch) + + @Test + @DisplayName("byContains fails") + fun byContainsFails() { + assertThrows { SQLiteDB().use(PatchFunctions::byContainsMatch) } + } + + @Test + @DisplayName("byJsonPath fails") + fun byJsonPathFails() { + assertThrows { SQLiteDB().use(PatchFunctions::byJsonPathMatch) } + } +} \ No newline at end of file diff --git a/src/jvm/src/test/kotlin/jvm/integration/sqlite/RemoveFieldsIT.kt b/src/kotlinx/src/test/kotlin/integration/SQLiteRemoveFieldsIT.kt similarity index 67% rename from src/jvm/src/test/kotlin/jvm/integration/sqlite/RemoveFieldsIT.kt rename to src/kotlinx/src/test/kotlin/integration/SQLiteRemoveFieldsIT.kt index 26e51b6..3bea0b4 100644 --- a/src/jvm/src/test/kotlin/jvm/integration/sqlite/RemoveFieldsIT.kt +++ b/src/kotlinx/src/test/kotlin/integration/SQLiteRemoveFieldsIT.kt @@ -1,56 +1,55 @@ -package solutions.bitbadger.documents.jvm.integration.sqlite +package solutions.bitbadger.documents.kotlinx.tests.integration import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import solutions.bitbadger.documents.DocumentException -import solutions.bitbadger.documents.jvm.integration.common.RemoveFields import kotlin.test.Test /** * SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions */ -@DisplayName("JVM | Kotlin | SQLite: RemoveFields") -class RemoveFieldsIT { +@DisplayName("KotlinX | SQLite: RemoveFields") +class SQLiteRemoveFieldsIT { @Test @DisplayName("byId removes fields from an existing document") fun byIdMatchFields() = - SQLiteDB().use(RemoveFields::byIdMatchFields) + SQLiteDB().use(RemoveFieldsFunctions::byIdMatchFields) @Test @DisplayName("byId succeeds when fields do not exist on an existing document") fun byIdMatchNoFields() = - SQLiteDB().use(RemoveFields::byIdMatchNoFields) + SQLiteDB().use(RemoveFieldsFunctions::byIdMatchNoFields) @Test @DisplayName("byId succeeds when no document exists") fun byIdNoMatch() = - SQLiteDB().use(RemoveFields::byIdNoMatch) + SQLiteDB().use(RemoveFieldsFunctions::byIdNoMatch) @Test @DisplayName("byFields removes fields from matching documents") fun byFieldsMatchFields() = - SQLiteDB().use(RemoveFields::byFieldsMatchFields) + SQLiteDB().use(RemoveFieldsFunctions::byFieldsMatchFields) @Test @DisplayName("byFields succeeds when fields do not exist on matching documents") fun byFieldsMatchNoFields() = - SQLiteDB().use(RemoveFields::byFieldsMatchNoFields) + SQLiteDB().use(RemoveFieldsFunctions::byFieldsMatchNoFields) @Test @DisplayName("byFields succeeds when no matching documents exist") fun byFieldsNoMatch() = - SQLiteDB().use(RemoveFields::byFieldsNoMatch) + SQLiteDB().use(RemoveFieldsFunctions::byFieldsNoMatch) @Test @DisplayName("byContains fails") fun byContainsFails() { - assertThrows { SQLiteDB().use(RemoveFields::byContainsMatchFields) } + assertThrows { SQLiteDB().use(RemoveFieldsFunctions::byContainsMatchFields) } } @Test @DisplayName("byJsonPath fails") fun byJsonPathFails() { - assertThrows { SQLiteDB().use(RemoveFields::byJsonPathMatchFields) } + assertThrows { SQLiteDB().use(RemoveFieldsFunctions::byJsonPathMatchFields) } } } diff --git a/src/kotlinx/src/test/kotlin/integration/ThrowawayDatabase.kt b/src/kotlinx/src/test/kotlin/integration/ThrowawayDatabase.kt new file mode 100644 index 0000000..858ccc5 --- /dev/null +++ b/src/kotlinx/src/test/kotlin/integration/ThrowawayDatabase.kt @@ -0,0 +1,20 @@ +package solutions.bitbadger.documents.kotlinx.tests.integration + +import java.sql.Connection + +/** + * Common interface for PostgreSQL and SQLite throwaway databases + */ +interface ThrowawayDatabase : AutoCloseable { + + /** The database connection for the throwaway database */ + val conn: Connection + + /** + * Determine if a database object exists + * + * @param name The name of the object whose existence should be checked + * @return True if the object exists, false if not + */ + fun dbObjectExists(name: String): Boolean +} diff --git a/src/pom.xml b/src/pom.xml deleted file mode 100644 index 06c5830..0000000 --- a/src/pom.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - 4.0.0 - - solutions.bitbadger - documents - 4.0.0-alpha1-SNAPSHOT - pom - - ${project.groupId}:${project.artifactId} - Expose a document store interface for PostgreSQL and SQLite - https://bitbadger.solutions/open-source/solutions.bitbadger.documents - - - - MIT License - https://www.opensource.org/licenses/mit-license.php - - - - - - Daniel J. Summers - daniel@bitbadger.solutions - Bit Badger Solutions - https://bitbadger.solutions - - - - - scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git - scm:git:https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents.git - https://git.bitbadger.solutions/bit-badger/solutions.bitbadger.documents - - - - 11 - UTF-8 - official - ${java.version} - 2.1.10 - 1.8.0 - 3.5.2 - 4.0.26 - 3.46.1.2 - 42.7.5 - - - - jvm - kotlin - - - - - org.junit.jupiter - junit-jupiter - 5.11.1 - test - - - org.jetbrains.kotlin - kotlin-test-junit5 - ${kotlin.version} - test - - - org.jetbrains.kotlin - kotlin-stdlib - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-reflect - ${kotlin.version} - - - org.jetbrains.kotlinx - kotlinx-serialization-json - ${serialization.version} - - - org.xerial - sqlite-jdbc - ${sqlite.version} - test - - - org.postgresql - postgresql - ${postgresql.version} - test - - - - diff --git a/src/scala/pom.xml b/src/scala/pom.xml new file mode 100644 index 0000000..566eadb --- /dev/null +++ b/src/scala/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + solutions.bitbadger + documents + 4.0.0-alpha1-SNAPSHOT + ../../pom.xml + + + solutions.bitbadger.documents + scala + + ${project.groupId}:${project.artifactId} + Expose a document store interface for PostgreSQL and SQLite (Scala Library) + https://bitbadger.solutions/open-source/relational-documents/jvm/ + + + UTF-8 + official + 1.8 + + + + ${project.basedir}/src/main/scala + ${project.basedir}/src/test/scala + + + net.alchim31.maven + scala-maven-plugin + 4.9.2 + + + + compile + testCompile + + + + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + ${surefire.version} + + + maven-failsafe-plugin + ${failsafe.version} + + + + integration-test + verify + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${java.version} + ${java.version} + + + + + + + + solutions.bitbadger.documents + core + ${project.version} + + + org.scala-lang + scala3-library_3 + ${scala.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + + + + \ No newline at end of file diff --git a/src/jvm/jvm.iml b/src/scala/scala.iml similarity index 76% rename from src/jvm/jvm.iml rename to src/scala/scala.iml index d6f3c7a..6ad0dbf 100644 --- a/src/jvm/jvm.iml +++ b/src/scala/scala.iml @@ -2,7 +2,7 @@ - + diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Count.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Count.scala new file mode 100644 index 0000000..a2a1d3c --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Count.scala @@ -0,0 +1,105 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.{Field, FieldMatch} +import solutions.bitbadger.documents.java.Count as CoreCount + +import java.sql.Connection + +import _root_.scala.jdk.CollectionConverters.* + +/** + * Functions to count documents + */ +object Count: + + /** + * Count all documents in the table + * + * @param tableName The name of the table in which documents should be counted + * @param conn The connection over which documents should be counted + * @return A count of the documents in the table + * @throws DocumentException If any dependent process does + */ + def all(tableName: String, conn: Connection): Long = + CoreCount.all(tableName, conn) + + /** + * Count all documents in the table (creates connection) + * + * @param tableName The name of the table in which documents should be counted + * @return A count of the documents in the table + * @throws DocumentException If no connection string has been set + */ + def all(tableName: String): Long = + CoreCount.all(tableName) + + /** + * Count documents using a field comparison + * + * @param tableName The name of the table in which documents should be counted + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched (optional, default `ALL`) + * @param conn The connection on which the deletion should be executed + * @return A count of the matching documents in the table + * @throws DocumentException If no dialect has been configured + */ + def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], conn: Connection): Long = + CoreCount.byFields(tableName, fields.asJava, howMatched.orNull, conn) + + /** + * Count documents using a field comparison (creates connection) + * + * @param tableName The name of the table in which documents should be counted + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @return A count of the matching documents in the table + * @throws DocumentException If no connection string has been set + */ + def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Long = + CoreCount.byFields(tableName, fields.asJava, howMatched.orNull) + + /** + * Count documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table in which documents should be counted + * @param criteria The object for which JSON containment should be checked + * @param conn The connection on which the count should be executed + * @return A count of the matching documents in the table + * @throws DocumentException If called on a SQLite connection + */ + def byContains[TContains](tableName: String, criteria: TContains, conn: Connection): Long = + CoreCount.byContains(tableName, criteria, conn) + + /** + * Count documents using a JSON containment query (PostgreSQL only; creates connection) + * + * @param tableName The name of the table in which documents should be counted + * @param criteria The object for which JSON containment should be checked + * @return A count of the matching documents in the table + * @throws DocumentException If called on a SQLite connection + */ + def byContains[TContains](tableName: String, criteria: TContains): Long = + CoreCount.byContains(tableName, criteria) + + /** + * Count documents using a JSON Path match query (PostgreSQL only) + * + * @param tableName The name of the table in which documents should be counted + * @param path The JSON path comparison to match + * @param conn The connection on which the count should be executed + * @return A count of the matching documents in the table + * @throws DocumentException If called on a SQLite connection + */ + def byJsonPath(tableName: String, path: String, conn: Connection): Long = + CoreCount.byJsonPath(tableName, path, conn) + + /** + * Count documents using a JSON Path match query (PostgreSQL only; creates connection) + * + * @param tableName The name of the table in which documents should be counted + * @param path The JSON path comparison to match + * @return A count of the matching documents in the table + * @throws DocumentException If called on a SQLite connection + */ + def byJsonPath(tableName: String, path: String): Long = + CoreCount.byJsonPath(tableName, path) diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Custom.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Custom.scala new file mode 100644 index 0000000..b59bee2 --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Custom.scala @@ -0,0 +1,210 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.{Configuration, Parameter} + +import java.sql.{Connection, ResultSet} +import scala.reflect.ClassTag +import scala.util.Using + +object Custom: + + /** + * Execute a query that returns a list of results + * + * @param query The query to retrieve the results + * @param parameters Parameters to use for the query + * @param conn The connection over which the query should be executed + * @param mapFunc The mapping function between the document and the domain item + * @return A list of results for the given query + * @throws DocumentException If parameters are invalid + */ + def list[TDoc](query: String, parameters: Seq[Parameter[?]], conn: Connection, + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] = + Using(Parameters.apply(conn, query, parameters)) { stmt => Results.toCustomList[TDoc](stmt, mapFunc) }.get + + /** + * Execute a query that returns a list of results + * + * @param query The query to retrieve the results + * @param conn The connection over which the query should be executed + * @param mapFunc The mapping function between the document and the domain item + * @return A list of results for the given query + * @throws DocumentException If parameters are invalid + */ + def list[TDoc](query: String, conn: Connection, + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] = + list(query, List(), conn, mapFunc) + + /** + * Execute a query that returns a list of results (creates connection) + * + * @param query The query to retrieve the results + * @param parameters Parameters to use for the query + * @param mapFunc The mapping function between the document and the domain item + * @return A list of results for the given query + * @throws DocumentException If parameters are invalid + */ + def list[TDoc](query: String, parameters: Seq[Parameter[?]], + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] = + Using(Configuration.dbConn()) { conn => list[TDoc](query, parameters, conn, mapFunc) }.get + + /** + * Execute a query that returns a list of results (creates connection) + * + * @param query The query to retrieve the results + * @param mapFunc The mapping function between the document and the domain item + * @return A list of results for the given query + * @throws DocumentException If parameters are invalid + */ + def list[TDoc](query: String, + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] = + list(query, List(), mapFunc) + + /** + * Execute a query that returns one or no results + * + * @param query The query to retrieve the results + * @param parameters Parameters to use for the query + * @param conn The connection over which the query should be executed + * @param mapFunc The mapping function between the document and the domain item + * @return An `Option` value, with the document if one matches the query + * @throws DocumentException If parameters are invalid + */ + def single[TDoc](query: String, parameters: Seq[Parameter[?]], conn: Connection, + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + list[TDoc](s"$query LIMIT 1", parameters, conn, mapFunc).headOption + + /** + * Execute a query that returns one or no results + * + * @param query The query to retrieve the results + * @param conn The connection over which the query should be executed + * @param mapFunc The mapping function between the document and the domain item + * @return An `Option` value, with the document if one matches the query + * @throws DocumentException If parameters are invalid + */ + def single[TDoc](query: String, conn: Connection, + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + list[TDoc](s"$query LIMIT 1", List(), conn, mapFunc).headOption + + /** + * Execute a query that returns one or no results (creates connection) + * + * @param query The query to retrieve the results + * @param parameters Parameters to use for the query + * @param mapFunc The mapping function between the document and the domain item + * @return An `Option` value, with the document if one matches the query + * @throws DocumentException If parameters are invalid + */ + def single[TDoc](query: String, parameters: Seq[Parameter[?]], + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + Using(Configuration.dbConn()) { conn => single[TDoc](query, parameters, conn, mapFunc) }.get + + /** + * Execute a query that returns one or no results (creates connection) + * + * @param query The query to retrieve the results + * @param mapFunc The mapping function between the document and the domain item + * @return An `Option` value, with the document if one matches the query + * @throws DocumentException If parameters are invalid + */ + def single[TDoc](query: String, + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + single[TDoc](query, List(), mapFunc) + + /** + * Execute a query that returns no results + * + * @param query The query to retrieve the results + * @param parameters Parameters to use for the query + * @param conn The connection over which the query should be executed + * @throws DocumentException If parameters are invalid + */ + def nonQuery(query: String, parameters: Seq[Parameter[?]], conn: Connection): Unit = + Using(Parameters.apply(conn, query, parameters)) { stmt => stmt.executeUpdate() } + + /** + * Execute a query that returns no results + * + * @param query The query to retrieve the results + * @param conn The connection over which the query should be executed + * @throws DocumentException If parameters are invalid + */ + def nonQuery(query: String, conn: Connection): Unit = + nonQuery(query, List(), conn) + + /** + * Execute a query that returns no results (creates connection) + * + * @param query The query to retrieve the results + * @param parameters Parameters to use for the query + * @throws DocumentException If parameters are invalid + */ + def nonQuery(query: String, parameters: Seq[Parameter[?]]): Unit = + Using(Configuration.dbConn()) { conn => nonQuery(query, parameters, conn) } + + /** + * Execute a query that returns no results (creates connection) + * + * @param query The query to retrieve the results + * @throws DocumentException If parameters are invalid + */ + def nonQuery(query: String): Unit = + nonQuery(query, List()) + + /** + * Execute a query that returns a scalar result + * + * @param query The query to retrieve the result + * @param parameters Parameters to use for the query + * @param conn The connection over which the query should be executed + * @param mapFunc The mapping function between the document and the domain item + * @return The scalar value from the query + * @throws DocumentException If parameters are invalid + */ + def scalar[T](query: String, parameters: Seq[Parameter[?]], conn: Connection, + mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T = + Using(Parameters.apply(conn, query, parameters)) { stmt => + Using(stmt.executeQuery()) { rs => + rs.next() + mapFunc(rs, tag) + }.get + }.get + + /** + * Execute a query that returns a scalar result + * + * @param query The query to retrieve the result + * @param conn The connection over which the query should be executed + * @param mapFunc The mapping function between the document and the domain item + * @return The scalar value from the query + * @throws DocumentException If parameters are invalid + */ + def scalar[T](query: String, conn: Connection, + mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T = + scalar[T](query, List(), conn, mapFunc) + + /** + * Execute a query that returns a scalar result (creates connection) + * + * @param query The query to retrieve the result + * @param parameters Parameters to use for the query + * @param mapFunc The mapping function between the document and the domain item + * @return The scalar value from the query + * @throws DocumentException If parameters are invalid + */ + def scalar[T](query: String, parameters: Seq[Parameter[?]], + mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T = + Using(Configuration.dbConn()) { conn => scalar[T](query, parameters, conn, mapFunc) }.get + + /** + * Execute a query that returns a scalar result (creates connection) + * + * @param query The query to retrieve the result + * @param mapFunc The mapping function between the document and the domain item + * @return The scalar value from the query + * @throws DocumentException If parameters are invalid + */ + def scalar[T](query: String, + mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T = + scalar[T](query, List(), mapFunc) diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Definition.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Definition.scala new file mode 100644 index 0000000..4e0c2cc --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Definition.scala @@ -0,0 +1,72 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.DocumentIndex +import solutions.bitbadger.documents.java.Definition as CoreDefinition + +import java.sql.Connection +import _root_.scala.jdk.CollectionConverters.* + +object Definition: + + /** + * Create a document table if necessary + * + * @param tableName The table whose existence should be ensured (may include schema) + * @param conn The connection on which the query should be executed + * @throws DocumentException If the dialect is not configured + */ + def ensureTable(tableName: String, conn: Connection): Unit = + CoreDefinition.ensureTable(tableName, conn) + + /** + * Create a document table if necessary + * + * @param tableName The table whose existence should be ensured (may include schema) + * @throws DocumentException If no connection string has been set + */ + def ensureTable(tableName: String): Unit = + CoreDefinition.ensureTable(tableName) + + /** + * Create an index on field(s) within documents in the specified table if necessary + * + * @param tableName The table to be indexed (may include schema) + * @param indexName The name of the index to create + * @param fields One or more fields to be indexed + * @param conn The connection on which the query should be executed + * @throws DocumentException If any dependent process does + */ + def ensureFieldIndex(tableName: String, indexName: String, fields: Seq[String], conn: Connection): Unit = + CoreDefinition.ensureFieldIndex(tableName, indexName, fields.asJava, conn) + + /** + * Create an index on field(s) within documents in the specified table if necessary + * + * @param tableName The table to be indexed (may include schema) + * @param indexName The name of the index to create + * @param fields One or more fields to be indexed + * @throws DocumentException If no connection string has been set, or if any dependent process does + */ + def ensureFieldIndex(tableName: String, indexName: String, fields: Seq[String]): Unit = + CoreDefinition.ensureFieldIndex(tableName, indexName, fields.asJava) + + /** + * Create a document index on a table (PostgreSQL only) + * + * @param tableName The table to be indexed (may include schema) + * @param indexType The type of index to ensure + * @param conn The connection on which the query should be executed + * @throws DocumentException If called on a SQLite connection + */ + def ensureDocumentIndex(tableName: String, indexType: DocumentIndex, conn: Connection): Unit = + CoreDefinition.ensureDocumentIndex(tableName, indexType, conn) + + /** + * Create a document index on a table (PostgreSQL only) + * + * @param tableName The table to be indexed (may include schema) + * @param indexType The type of index to ensure + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def ensureDocumentIndex(tableName: String, indexType: DocumentIndex): Unit = + CoreDefinition.ensureDocumentIndex(tableName, indexType) diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Delete.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Delete.scala new file mode 100644 index 0000000..19729af --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Delete.scala @@ -0,0 +1,98 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.{Field, FieldMatch} +import solutions.bitbadger.documents.java.Delete as CoreDelete + +import java.sql.Connection +import _root_.scala.jdk.CollectionConverters.* + +/** + * Functions to delete documents + */ +object Delete: + + /** + * Delete a document by its ID + * + * @param tableName The name of the table from which documents should be deleted + * @param docId The ID of the document to be deleted + * @param conn The connection on which the deletion should be executed + * @throws DocumentException If no dialect has been configured + */ + def byId[TKey](tableName: String, docId: TKey, conn: Connection): Unit = + CoreDelete.byId(tableName, docId, conn) + + /** + * Delete a document by its ID (creates connection) + * + * @param tableName The name of the table from which documents should be deleted + * @param docId The ID of the document to be deleted + * @throws DocumentException If no connection string has been set + */ + def byId[TKey](tableName: String, docId: TKey): Unit = + CoreDelete.byId(tableName, docId) + + /** + * Delete documents using a field comparison + * + * @param tableName The name of the table from which documents should be deleted + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @param conn The connection on which the deletion should be executed + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], conn: Connection): Unit = + CoreDelete.byFields(tableName, fields.asJava, howMatched.orNull, conn) + + /** + * Delete documents using a field comparison (creates connection) + * + * @param tableName The name of the table from which documents should be deleted + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @throws DocumentException If no connection string has been set, or if parameters are invalid + */ + def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Unit = + CoreDelete.byFields(tableName, fields.asJava, howMatched.orNull) + + /** + * Delete documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table from which documents should be deleted + * @param criteria The object for which JSON containment should be checked + * @param conn The connection on which the deletion should be executed + * @throws DocumentException If called on a SQLite connection + */ + def byContains[TContains](tableName: String, criteria: TContains, conn: Connection): Unit = + CoreDelete.byContains(tableName, criteria, conn) + + /** + * Delete documents using a JSON containment query (PostgreSQL only; creates connection) + * + * @param tableName The name of the table from which documents should be deleted + * @param criteria The object for which JSON containment should be checked + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def byContains[TContains](tableName: String, criteria: TContains): Unit = + CoreDelete.byContains(tableName, criteria) + + /** + * Delete documents using a JSON Path match query (PostgreSQL only) + * + * @param tableName The name of the table from which documents should be deleted + * @param path The JSON path comparison to match + * @param conn The connection on which the deletion should be executed + * @throws DocumentException If called on a SQLite connection + */ + def byJsonPath(tableName: String, path: String, conn: Connection): Unit = + CoreDelete.byJsonPath(tableName, path, conn) + + /** + * Delete documents using a JSON Path match query (PostgreSQL only; creates connection) + * + * @param tableName The name of the table from which documents should be deleted + * @param path The JSON path comparison to match + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def byJsonPath(tableName: String, path: String): Unit = + CoreDelete.byJsonPath(tableName, path) diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Document.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Document.scala new file mode 100644 index 0000000..ca23662 --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Document.scala @@ -0,0 +1,72 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.java.Document as CoreDocument + +import java.sql.Connection + +object Document: + + /** + * Insert a new document + * + * @param tableName The table into which the document should be inserted (may include schema) + * @param document The document to be inserted + * @param conn The connection on which the query should be executed + * @throws DocumentException If IDs are misconfigured, or if the database command fails + */ + def insert[TDoc](tableName: String, document: TDoc, conn: Connection): Unit = + CoreDocument.insert(tableName, document, conn) + + /** + * Insert a new document (creates connection) + * + * @param tableName The table into which the document should be inserted (may include schema) + * @param document The document to be inserted + * @throws DocumentException If IDs are misconfigured, or if the database command fails + */ + def insert[TDoc](tableName: String, document: TDoc): Unit = + CoreDocument.insert(tableName, document) + + /** + * Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert") + * + * @param tableName The table in which the document should be saved (may include schema) + * @param document The document to be saved + * @param conn The connection on which the query should be executed + * @throws DocumentException If the database command fails + */ + def save[TDoc](tableName: String, document: TDoc, conn: Connection): Unit = + CoreDocument.save(tableName, document, conn) + + /** + * Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert"; creates connection) + * + * @param tableName The table in which the document should be saved (may include schema) + * @param document The document to be saved + * @throws DocumentException If the database command fails + */ + def save[TDoc](tableName: String, document: TDoc): Unit = + CoreDocument.save(tableName, document) + + /** + * Update (replace) a document by its ID + * + * @param tableName The table in which the document should be replaced (may include schema) + * @param docId The ID of the document to be replaced + * @param document The document to be replaced + * @param conn The connection on which the query should be executed + * @throws DocumentException If no dialect has been configured, or if the database command fails + */ + def update[TKey, TDoc](tableName: String, docId: TKey, document: TDoc, conn: Connection): Unit = + CoreDocument.update(tableName, docId, document, conn) + + /** + * Update (replace) a document by its ID (creates connection) + * + * @param tableName The table in which the document should be replaced (may include schema) + * @param docId The ID of the document to be replaced + * @param document The document to be replaced + * @throws DocumentException If no dialect has been configured, or if the database command fails + */ + def update[TKey, TDoc](tableName: String, docId: TKey, document: TDoc): Unit = + CoreDocument.update(tableName, docId, document) diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Exists.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Exists.scala new file mode 100644 index 0000000..90883f0 --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Exists.scala @@ -0,0 +1,106 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.{Field, FieldMatch} +import solutions.bitbadger.documents.java.Exists as CoreExists + +import java.sql.Connection +import _root_.scala.jdk.CollectionConverters.* + +/** + * Functions to determine whether documents exist + */ +object Exists: + + /** + * Determine a document's existence by its ID + * + * @param tableName The name of the table in which document existence should be checked + * @param docId The ID of the document to be checked + * @param conn The connection on which the existence check should be executed + * @return True if the document exists, false if not + * @throws DocumentException If no dialect has been configured + */ + def byId[TKey](tableName: String, docId: TKey, conn: Connection): Boolean = + CoreExists.byId(tableName, docId, conn) + + /** + * Determine a document's existence by its ID (creates connection) + * + * @param tableName The name of the table in which document existence should be checked + * @param docId The ID of the document to be checked + * @return True if the document exists, false if not + * @throws DocumentException If no connection string has been set + */ + def byId[TKey](tableName: String, docId: TKey): Boolean = + CoreExists.byId(tableName, docId) + + /** + * Determine document existence using a field comparison + * + * @param tableName The name of the table in which document existence should be checked + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @param conn The connection on which the existence check should be executed + * @return True if any matching documents exist, false if not + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], conn: Connection): Boolean = + CoreExists.byFields(tableName, fields.asJava, howMatched.orNull, conn) + + /** + * Determine document existence using a field comparison (creates connection) + * + * @param tableName The name of the table in which document existence should be checked + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @return True if any matching documents exist, false if not + * @throws DocumentException If no connection string has been set, or if parameters are invalid + */ + def byFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Boolean = + CoreExists.byFields(tableName, fields.asJava, howMatched.orNull) + + /** + * Determine document existence using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table in which document existence should be checked + * @param criteria The object for which JSON containment should be checked + * @param conn The connection on which the existence check should be executed + * @return True if any matching documents exist, false if not + * @throws DocumentException If called on a SQLite connection + */ + def byContains[TContains](tableName: String, criteria: TContains, conn: Connection): Boolean = + CoreExists.byContains(tableName, criteria, conn) + + /** + * Determine document existence using a JSON containment query (PostgreSQL only; creates connection) + * + * @param tableName The name of the table in which document existence should be checked + * @param criteria The object for which JSON containment should be checked + * @return True if any matching documents exist, false if not + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def byContains[TContains](tableName: String, criteria: TContains): Boolean = + CoreExists.byContains(tableName, criteria) + + /** + * Determine document existence using a JSON Path match query (PostgreSQL only) + * + * @param tableName The name of the table in which document existence should be checked + * @param path The JSON path comparison to match + * @param conn The connection on which the existence check should be executed + * @return True if any matching documents exist, false if not + * @throws DocumentException If called on a SQLite connection + */ + def byJsonPath(tableName: String, path: String, conn: Connection): Boolean = + CoreExists.byJsonPath(tableName, path, conn) + + /** + * Determine document existence using a JSON Path match query (PostgreSQL only; creates connection) + * + * @param tableName The name of the table in which document existence should be checked + * @param path The JSON path comparison to match + * @return True if any matching documents exist, false if not + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def byJsonPath(tableName: String, path: String): Boolean = + CoreExists.byJsonPath(tableName, path) diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Find.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Find.scala new file mode 100644 index 0000000..eb6534f --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Find.scala @@ -0,0 +1,372 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.{Configuration, Field, FieldMatch, Parameter, ParameterType} +import solutions.bitbadger.documents.query.{FindQuery, QueryUtils} + +import java.sql.Connection +import scala.reflect.ClassTag +import scala.jdk.CollectionConverters.* +import scala.util.Using + +/** + * Functions to find and retrieve documents + */ +object Find: + + /** + * Retrieve all documents in the given table, ordering results by the optional given fields + * + * @param tableName The table from which documents should be retrieved + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @param conn The connection over which documents should be retrieved + * @return A list of documents from the given table + * @throws DocumentException If query execution fails + */ + def all[TDoc](tableName: String, orderBy: Seq[Field[?]], conn: Connection)(implicit tag: ClassTag[TDoc]): List[TDoc] = + Custom.list[TDoc](FindQuery.all(tableName) + QueryUtils.orderBy(orderBy.asJava), conn, Results.fromData) + + /** + * Retrieve all documents in the given table, ordering results by the optional given fields + * + * @param tableName The table from which documents should be retrieved + * @param conn The connection over which documents should be retrieved + * @return A list of documents from the given table + * @throws DocumentException If query execution fails + */ + def all[TDoc](tableName: String, conn: Connection)(implicit tag: ClassTag[TDoc]): List[TDoc] = + all[TDoc](tableName, List(), conn) + + /** + * Retrieve all documents in the given table (creates connection) + * + * @param tableName The table from which documents should be retrieved + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return A list of documents from the given table + * @throws DocumentException If no connection string has been set, or if query execution fails + */ + def all[TDoc](tableName: String, orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): List[TDoc] = + Using(Configuration.dbConn()) { conn => all[TDoc](tableName, orderBy, conn) }.get + + /** + * Retrieve a document by its ID + * + * @param tableName The table from which the document should be retrieved + * @param docId The ID of the document to retrieve + * @param conn The connection over which documents should be retrieved + * @return An `Option` with the document if it is found + * @throws DocumentException If no dialect has been configured + */ + def byId[TKey, TDoc](tableName: String, docId: TKey, conn: Connection)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + Custom.single[TDoc](FindQuery.byId(tableName, docId), + Parameters.addFields(Field.equal(Configuration.idField, docId, ":id") :: Nil).toSeq, conn, Results.fromData) + + /** + * Retrieve a document by its ID (creates connection + * + * @param tableName The table from which the document should be retrieved + * @param docId The ID of the document to retrieve + * @return An `Option` with the document if it is found + * @throws DocumentException If no connection string has been set + */ + def byId[TKey, TDoc](tableName: String, docId: TKey)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + Using(Configuration.dbConn()) { conn => byId[TKey, TDoc](tableName, docId, conn) }.get + + /** + * Retrieve documents using a field comparison, ordering results by the given fields + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @param conn The connection over which documents should be retrieved + * @return A list of documents matching the field comparison + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def byFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], orderBy: Seq[Field[?]], + conn: Connection)(implicit tag: ClassTag[TDoc]): List[TDoc] = + val named = Parameters.nameFields(fields) + Custom.list[TDoc]( + FindQuery.byFields(tableName, named.asJava, howMatched.orNull) + QueryUtils.orderBy(orderBy.asJava), + Parameters.addFields(named).toSeq, conn, Results.fromData) + + /** + * Retrieve documents using a field comparison + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @param conn The connection over which documents should be retrieved + * @return A list of documents matching the field comparison + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def byFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], conn: Connection) + (implicit tag: ClassTag[TDoc]): List[TDoc] = + byFields[TDoc](tableName, fields, howMatched, List(), conn) + + /** + * Retrieve documents using a field comparison, ordering results by the given fields + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @param conn The connection over which documents should be retrieved + * @return A list of documents matching the field comparison + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def byFields[TDoc](tableName: String, fields: Seq[Field[?]], orderBy: Seq[Field[?]], conn: Connection) + (implicit tag: ClassTag[TDoc]): List[TDoc] = + byFields[TDoc](tableName, fields, None, orderBy, conn) + + /** + * Retrieve documents using a field comparison, ordering results by the given fields (creates connection) + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return A list of documents matching the field comparison + * @throws DocumentException If no connection string has been set, or if parameters are invalid + */ + def byFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None, + orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): List[TDoc] = + Using(Configuration.dbConn()) { conn => byFields[TDoc](tableName, fields, howMatched, orderBy, conn) }.get + + /** + * Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param criteria The object for which JSON containment should be checked + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @param conn The connection over which documents should be retrieved + * @return A list of documents matching the JSON containment query + * @throws DocumentException If called on a SQLite connection + */ + def byContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]], + conn: Connection)(implicit tag: ClassTag[TDoc]): List[TDoc] = + Custom.list[TDoc](FindQuery.byContains(tableName) + QueryUtils.orderBy(orderBy.asJava), + Parameters.json(":criteria", criteria) :: Nil, conn, Results.fromData) + + /** + * Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param criteria The object for which JSON containment should be checked + * @param conn The connection over which documents should be retrieved + * @return A list of documents matching the JSON containment query + * @throws DocumentException If called on a SQLite connection + */ + def byContains[TDoc, TContains](tableName: String, criteria: TContains, conn: Connection) + (implicit tag: ClassTag[TDoc]): List[TDoc] = + byContains[TDoc, TContains](tableName, criteria, List(), conn) + + /** + * Retrieve documents using a JSON containment query, ordering results by the given fields (PostgreSQL only; creates + * connection) + * + * @param tableName The table from which documents should be retrieved + * @param criteria The object for which JSON containment should be checked + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return A list of documents matching the JSON containment query + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def byContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]] = List()) + (implicit tag: ClassTag[TDoc]): List[TDoc] = + Using(Configuration.dbConn()) { conn => byContains[TDoc, TContains](tableName, criteria, orderBy, conn) }.get + + /** + * Retrieve documents using a JSON Path match query, ordering results by the given fields (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param path The JSON path comparison to match + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @param conn The connection over which documents should be retrieved + * @return A list of documents matching the JSON Path match query + * @throws DocumentException If called on a SQLite connection + */ + def byJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]], conn: Connection) + (implicit tag: ClassTag[TDoc]): List[TDoc] = + Custom.list[TDoc](FindQuery.byJsonPath(tableName) + QueryUtils.orderBy(orderBy.asJava), + Parameter(":path", ParameterType.STRING, path) :: Nil, conn, Results.fromData) + + /** + * Retrieve documents using a JSON Path match query (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param path The JSON path comparison to match + * @param conn The connection over which documents should be retrieved + * @return A list of documents matching the JSON Path match query + * @throws DocumentException If called on a SQLite connection + */ + def byJsonPath[TDoc](tableName: String, path: String, conn: Connection)(implicit tag: ClassTag[TDoc]): List[TDoc] = + byJsonPath[TDoc](tableName, path, List(), conn) + + /** + * Retrieve documents using a JSON Path match query, ordering results by the given fields (PostgreSQL only; creates + * connection) + * + * @param tableName The table from which documents should be retrieved + * @param path The JSON path comparison to match + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return A list of documents matching the JSON Path match query + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def byJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]] = List()) + (implicit tag: ClassTag[TDoc]): List[TDoc] = + Using(Configuration.dbConn()) { conn => byJsonPath[TDoc](tableName, path, orderBy, conn) }.get + + /** + * Retrieve the first document using a field comparison and ordering fields + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`) + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @param conn The connection over which documents should be retrieved + * @return An `Option` with the first document matching the field comparison if found + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def firstByFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], + orderBy: Seq[Field[?]], conn: Connection)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + val named = Parameters.nameFields(fields) + Custom.single[TDoc]( + FindQuery.byFields(tableName, named.asJava, howMatched.orNull) + QueryUtils.orderBy(orderBy.asJava), + Parameters.addFields(named).toSeq, conn, Results.fromData) + + /** + * Retrieve the first document using a field comparison + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`) + * @param conn The connection over which documents should be retrieved + * @return An `Option` with the first document matching the field comparison if found + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def firstByFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch], conn: Connection) + (implicit tag: ClassTag[TDoc]): Option[TDoc] = + firstByFields[TDoc](tableName, fields, howMatched, List(), conn) + + /** + * Retrieve the first document using a field comparison and ordering fields + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @param conn The connection over which documents should be retrieved + * @return An `Option` with the first document matching the field comparison if found + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def firstByFields[TDoc](tableName: String, fields: Seq[Field[?]], orderBy: Seq[Field[?]], conn: Connection) + (implicit tag: ClassTag[TDoc]): Option[TDoc] = + firstByFields[TDoc](tableName, fields, None, orderBy, conn) + + /** + * Retrieve the first document using a field comparison + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param conn The connection over which documents should be retrieved + * @return An `Option` with the first document matching the field comparison if found + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def firstByFields[TDoc](tableName: String, fields: Seq[Field[?]], conn: Connection) + (implicit tag: ClassTag[TDoc]): Option[TDoc] = + firstByFields[TDoc](tableName, fields, None, List(), conn) + + /** + * Retrieve the first document using a field comparison and optional ordering fields (creates connection) + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`) + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return An `Option` with the first document matching the field comparison if found + * @throws DocumentException If no connection string has been set, or if parameters are invalid + */ + def firstByFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None, + orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): Option[TDoc] = + Using(Configuration.dbConn()) { conn => firstByFields[TDoc](tableName, fields, howMatched, orderBy, conn) }.get + + /** + * Retrieve the first document using a JSON containment query and ordering fields (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param criteria The object for which JSON containment should be checked + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @param conn The connection over which documents should be retrieved + * @return An `Option` with the first document matching the JSON containment query if found + * @throws DocumentException If called on a SQLite connection + */ + def firstByContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]], + conn: Connection)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + Custom.single[TDoc](FindQuery.byContains(tableName) + QueryUtils.orderBy(orderBy.asJava), + Parameters.json(":criteria", criteria) :: Nil, conn, Results.fromData) + + /** + * Retrieve the first document using a JSON containment query (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param criteria The object for which JSON containment should be checked + * @param conn The connection over which documents should be retrieved + * @return An `Option` with the first document matching the JSON containment query if found + * @throws DocumentException If called on a SQLite connection + */ + def firstByContains[TDoc, TContains](tableName: String, criteria: TContains, conn: Connection) + (implicit tag: ClassTag[TDoc]): Option[TDoc] = + firstByContains[TDoc, TContains](tableName, criteria, List(), conn) + + /** + * Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only; creates + * connection) + * + * @param tableName The table from which documents should be retrieved + * @param criteria The object for which JSON containment should be checked + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return An `Option` with the first document matching the JSON containment query if found + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def firstByContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]] = List()) + (implicit tag: ClassTag[TDoc]): Option[TDoc] = + Using(Configuration.dbConn()) { conn => firstByContains[TDoc, TContains](tableName, criteria, orderBy, conn) }.get + + /** + * Retrieve the first document using a JSON Path match query and ordering fields (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param path The JSON path comparison to match + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @param conn The connection over which documents should be retrieved + * @return An `Optional` item, with the first document matching the JSON Path match query if found + * @throws DocumentException If called on a SQLite connection + */ + def firstByJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]], conn: Connection) + (implicit tag: ClassTag[TDoc]): Option[TDoc] = + Custom.single[TDoc](FindQuery.byJsonPath(tableName) + QueryUtils.orderBy(orderBy.asJava), + Parameter(":path", ParameterType.STRING, path) :: Nil, conn, Results.fromData) + + /** + * Retrieve the first document using a JSON Path match query (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param path The JSON path comparison to match + * @param conn The connection over which documents should be retrieved + * @return An `Option` with the first document matching the JSON Path match query if found + * @throws DocumentException If called on a SQLite connection + */ + def firstByJsonPath[TDoc](tableName: String, path: String, conn: Connection) + (implicit tag: ClassTag[TDoc]): Option[TDoc] = + firstByJsonPath[TDoc](tableName, path, List(), conn) + + /** + * Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only; creates + * connection) + * + * @param tableName The table from which documents should be retrieved + * @param path The JSON path comparison to match + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return An `Optional` item, with the first document matching the JSON Path match query if found + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def firstByJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]] = List()) + (implicit tag: ClassTag[TDoc]): Option[TDoc] = + Using(Configuration.dbConn()) { conn => firstByJsonPath[TDoc](tableName, path, orderBy, conn) }.get diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Parameters.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Parameters.scala new file mode 100644 index 0000000..02b2d5d --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Parameters.scala @@ -0,0 +1,87 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.{Field, Op, Parameter, ParameterName} +import solutions.bitbadger.documents.java.Parameters as CoreParameters + +import java.sql.{Connection, PreparedStatement} +import java.util +import scala.collection.mutable +import scala.collection.mutable.ListBuffer +import _root_.scala.jdk.CollectionConverters.* + +/** + * Functions to assist with the creation and implementation of parameters for SQL queries + */ +object Parameters: + + /** + * Assign parameter names to any fields that do not have them assigned + * + * @param fields The collection of fields to be named + * @return The collection of fields with parameter names assigned + */ + def nameFields(fields: Seq[Field[?]]): Seq[Field[?]] = + val name = ParameterName() + fields.map { it => + if ((it.getParameterName == null || it.getParameterName.isEmpty) + && !(Op.EXISTS :: Op.NOT_EXISTS :: Nil).contains(it.getComparison.getOp)) { + it.withParameterName(name.derive(null)) + } else { + it + } + } + + /** + * Create a parameter by encoding a JSON object + * + * @param name The parameter name + * @param value The object to be encoded as JSON + * @return A parameter with the value encoded + */ + def json[T](name: String, value: T): Parameter[String] = + CoreParameters.json(name, value) + + /** + * Add field parameters to the given set of parameters + * + * @param fields The fields being compared in the query + * @param existing Any existing parameters for the query (optional, defaults to empty collection) + * @return A collection of parameters for the query + */ + def addFields(fields: Seq[Field[?]], + existing: mutable.Buffer[Parameter[?]] = ListBuffer()): mutable.Buffer[Parameter[?]] = + fields.foreach { it => it.appendParameter(new util.ArrayList[Parameter[?]]()).forEach(existing.append) } + existing + + /** + * Replace the parameter names in the query with question marks + * + * @param query The query with named placeholders + * @param parameters The parameters for the query + * @return The query, with name parameters changed to `?`s + */ + def replaceNamesInQuery(query: String, parameters: Seq[Parameter[?]]): String = + CoreParameters.replaceNamesInQuery(query, parameters.asJava) + + /** + * Apply the given parameters to the given query, returning a prepared statement + * + * @param conn The active JDBC connection + * @param query The query + * @param parameters The parameters for the query + * @return A `PreparedStatement` with the parameter names replaced with `?` and parameter values bound + * @throws DocumentException If parameter names are invalid or number value types are invalid + */ + def apply(conn: Connection, query: String, parameters: Seq[Parameter[?]]): PreparedStatement = + CoreParameters.apply(conn, query, parameters.asJava) + + /** + * Create parameters for field names to be removed from a document + * + * @param names The names of the fields to be removed + * @param parameterName The parameter name to use for the query + * @return A list of parameters to use for building the query + * @throws DocumentException If the dialect has not been set + */ + def fieldNames(names: Seq[String], parameterName: String = ":name"): mutable.Buffer[Parameter[?]] = + CoreParameters.fieldNames(names.asJava, parameterName).asScala.toBuffer diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Patch.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Patch.scala new file mode 100644 index 0000000..b53bb83 --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Patch.scala @@ -0,0 +1,120 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.{Field, FieldMatch} +import solutions.bitbadger.documents.java.Patch as CorePatch + +import java.sql.Connection +import _root_.scala.jdk.CollectionConverters.* + +/** + * Functions to patch (partially update) documents + */ +object Patch: + + /** + * Patch a document by its ID + * + * @param tableName The name of the table in which a document should be patched + * @param docId The ID of the document to be patched + * @param patch The object whose properties should be replaced in the document + * @param conn The connection on which the update should be executed + * @throws DocumentException If no dialect has been configured + */ + def byId[TKey, TPatch](tableName: String, docId: TKey, patch: TPatch, conn: Connection): Unit = + CorePatch.byId(tableName, docId, patch, conn) + + /** + * Patch a document by its ID (creates connection) + * + * @param tableName The name of the table in which a document should be patched + * @param docId The ID of the document to be patched + * @param patch The object whose properties should be replaced in the document + * @throws DocumentException If no connection string has been set + */ + def byId[TKey, TPatch](tableName: String, docId: TKey, patch: TPatch) = + CorePatch.byId(tableName, docId, patch) + + /** + * Patch documents using a field comparison + * + * @param tableName The name of the table in which documents should be patched + * @param fields The fields which should be compared + * @param patch The object whose properties should be replaced in the document + * @param howMatched How the fields should be matched + * @param conn The connection on which the update should be executed + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def byFields[TPatch](tableName: String, fields: Seq[Field[?]], patch: TPatch, howMatched: Option[FieldMatch], + conn: Connection): Unit = + CorePatch.byFields(tableName, fields.asJava, patch, howMatched.orNull, conn) + + /** + * Patch documents using a field comparison + * + * @param tableName The name of the table in which documents should be patched + * @param fields The fields which should be compared + * @param patch The object whose properties should be replaced in the document + * @param conn The connection on which the update should be executed + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def byFields[TPatch](tableName: String, fields: Seq[Field[?]], patch: TPatch, conn: Connection): Unit = + byFields(tableName, fields, patch, None, conn) + + /** + * Patch documents using a field comparison (creates connection) + * + * @param tableName The name of the table in which documents should be patched + * @param fields The fields which should be compared + * @param patch The object whose properties should be replaced in the document + * @param howMatched How the fields should be matched + * @throws DocumentException If no connection string has been set, or if parameters are invalid + */ + def byFields[TPatch](tableName: String, fields: Seq[Field[?]], patch: TPatch, + howMatched: Option[FieldMatch] = None): Unit = + CorePatch.byFields(tableName, fields.asJava, patch, howMatched.orNull) + + /** + * Patch documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table in which documents should be patched + * @param criteria The object against which JSON containment should be checked + * @param patch The object whose properties should be replaced in the document + * @param conn The connection on which the update should be executed + * @throws DocumentException If called on a SQLite connection + */ + def byContains[TContains, TPatch](tableName: String, criteria: TContains, patch: TPatch, conn: Connection): Unit = + CorePatch.byContains(tableName, criteria, patch, conn) + + /** + * Patch documents using a JSON containment query (PostgreSQL only; creates connection) + * + * @param tableName The name of the table in which documents should be patched + * @param criteria The object against which JSON containment should be checked + * @param patch The object whose properties should be replaced in the document + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def byContains[TContains, TPatch](tableName: String, criteria: TContains, patch: TPatch): Unit = + CorePatch.byContains(tableName, criteria, patch) + + /** + * Patch documents using a JSON Path match query (PostgreSQL only) + * + * @param tableName The name of the table in which documents should be patched + * @param path The JSON path comparison to match + * @param patch The object whose properties should be replaced in the document + * @param conn The connection on which the update should be executed + * @throws DocumentException If called on a SQLite connection + */ + def byJsonPath[TPatch](tableName: String, path: String, patch: TPatch, conn: Connection): Unit = + CorePatch.byJsonPath(tableName, path, patch, conn) + + /** + * Patch documents using a JSON Path match query (PostgreSQL only; creates connection) + * + * @param tableName The name of the table in which documents should be patched + * @param path The JSON path comparison to match + * @param patch The object whose properties should be replaced in the document + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def byJsonPath[TPatch](tableName: String, path: String, patch: TPatch): Unit = + CorePatch.byJsonPath(tableName, path, patch) diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/RemoveFields.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/RemoveFields.scala new file mode 100644 index 0000000..2701553 --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/RemoveFields.scala @@ -0,0 +1,120 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.{Field, FieldMatch} +import solutions.bitbadger.documents.java.RemoveFields as CoreRemoveFields + +import java.sql.Connection +import scala.jdk.CollectionConverters.* + +/** + * Functions to remove fields from documents + */ +object RemoveFields: + + /** + * Remove fields from a document by its ID + * + * @param tableName The name of the table in which the document's fields should be removed + * @param docId The ID of the document to have fields removed + * @param toRemove The names of the fields to be removed + * @param conn The connection on which the update should be executed + * @throws DocumentException If no dialect has been configured + */ + def byId[TKey](tableName: String, docId: TKey, toRemove: Seq[String], conn: Connection): Unit = + CoreRemoveFields.byId(tableName, docId, toRemove.asJava, conn) + + /** + * Remove fields from a document by its ID (creates connection) + * + * @param tableName The name of the table in which the document's fields should be removed + * @param docId The ID of the document to have fields removed + * @param toRemove The names of the fields to be removed + * @throws DocumentException If no connection string has been set + */ + def byId[TKey](tableName: String, docId: TKey, toRemove: Seq[String]): Unit = + CoreRemoveFields.byId(tableName, docId, toRemove.asJava) + + /** + * Remove fields from documents using a field comparison + * + * @param tableName The name of the table in which document fields should be removed + * @param fields The fields which should be compared + * @param toRemove The names of the fields to be removed + * @param howMatched How the fields should be matched + * @param conn The connection on which the update should be executed + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def byFields(tableName: String, fields: Seq[Field[?]], toRemove: Seq[String], howMatched: Option[FieldMatch], + conn: Connection): Unit = + CoreRemoveFields.byFields(tableName, fields.asJava, toRemove.asJava, howMatched.orNull, conn) + + /** + * Remove fields from documents using a field comparison + * + * @param tableName The name of the table in which document fields should be removed + * @param fields The fields which should be compared + * @param toRemove The names of the fields to be removed + * @param conn The connection on which the update should be executed + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def byFields(tableName: String, fields: Seq[Field[?]], toRemove: Seq[String], conn: Connection): Unit = + byFields(tableName, fields, toRemove, None, conn) + + /** + * Remove fields from documents using a field comparison (creates connection) + * + * @param tableName The name of the table in which document fields should be removed + * @param fields The fields which should be compared + * @param toRemove The names of the fields to be removed + * @param howMatched How the fields should be matched + * @throws DocumentException If no connection string has been set, or if parameters are invalid + */ + def byFields(tableName: String, fields: Seq[Field[?]], toRemove: Seq[String], + howMatched: Option[FieldMatch] = None): Unit = + CoreRemoveFields.byFields(tableName, fields.asJava, toRemove.asJava, howMatched.orNull) + + /** + * Remove fields from documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table in which document fields should be removed + * @param criteria The object against which JSON containment should be checked + * @param toRemove The names of the fields to be removed + * @param conn The connection on which the update should be executed + * @throws DocumentException If called on a SQLite connection + */ + def byContains[TContains](tableName: String, criteria: TContains, toRemove: Seq[String], conn: Connection): Unit = + CoreRemoveFields.byContains(tableName, criteria, toRemove.asJava, conn) + + /** + * Remove fields from documents using a JSON containment query (PostgreSQL only; creates connection) + * + * @param tableName The name of the table in which document fields should be removed + * @param criteria The object against which JSON containment should be checked + * @param toRemove The names of the fields to be removed + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def byContains[TContains](tableName: String, criteria: TContains, toRemove: Seq[String]): Unit = + CoreRemoveFields.byContains(tableName, criteria, toRemove.asJava) + + /** + * Remove fields from documents using a JSON Path match query (PostgreSQL only) + * + * @param tableName The name of the table in which document fields should be removed + * @param path The JSON path comparison to match + * @param toRemove The names of the fields to be removed + * @param conn The connection on which the update should be executed + * @throws DocumentException If called on a SQLite connection + */ + def byJsonPath(tableName: String, path: String, toRemove: Seq[String], conn: Connection): Unit = + CoreRemoveFields.byJsonPath(tableName, path, toRemove.asJava, conn) + + /** + * Remove fields from documents using a JSON Path match query (PostgreSQL only; creates connection) + * + * @param tableName The name of the table in which document fields should be removed + * @param path The JSON path comparison to match + * @param toRemove The names of the fields to be removed + * @throws DocumentException If no connection string has been set, or if called on a SQLite connection + */ + def byJsonPath(tableName: String, path: String, toRemove: Seq[String]): Unit = + CoreRemoveFields.byJsonPath(tableName, path, toRemove.asJava) diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Results.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Results.scala new file mode 100644 index 0000000..bab76e4 --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/Results.scala @@ -0,0 +1,77 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.DocumentException +import solutions.bitbadger.documents.java.Results as CoreResults + +import java.sql.{PreparedStatement, ResultSet, SQLException} +import scala.collection.mutable.ListBuffer +import scala.reflect.ClassTag +import scala.util.Using + +/** + * Functions to manipulate results + */ +object Results: + + /** + * Create a domain item from a document, specifying the field in which the document is found + * + * @param field The field name containing the JSON document + * @param rs A `ResultSet` set to the row with the document to be constructed + * @param ignored The class tag (placeholder used for signature; implicit tag used for serialization) + * @return The constructed domain item + */ + def fromDocument[TDoc](field: String, rs: ResultSet, ignored: ClassTag[TDoc])(implicit tag: ClassTag[TDoc]): TDoc = + CoreResults.fromDocument(field, rs, tag.runtimeClass.asInstanceOf[Class[TDoc]]) + + /** + * Create a domain item from a document + * + * @param rs A `ResultSet` set to the row with the document to be constructed + * @param ignored The class tag (placeholder used for signature; implicit tag used for serialization) + * @return The constructed domain item + */ + def fromData[TDoc](rs: ResultSet, ignored: ClassTag[TDoc])(implicit tag: ClassTag[TDoc]): TDoc = + fromDocument[TDoc]("data", rs, tag) + + /** + * Create a list of items for the results of the given command, using the specified mapping function + * + * @param stmt The prepared statement to execute + * @param mapFunc The mapping function from data reader to domain class instance + * @return A list of items from the query's result + * @throws DocumentException If there is a problem executing the query (unchecked) + */ + def toCustomList[TDoc](stmt: PreparedStatement, + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] = + try + val buffer = ListBuffer[TDoc]() + Using(stmt.executeQuery()) { rs => + while (rs.next()) { + buffer.append(mapFunc(rs, tag)) + } + } + buffer.toList + catch + case ex: SQLException => + throw DocumentException("Error retrieving documents from query: ${ex.message}", ex) + + /** + * Extract a count from the first column + * + * @param rs A `ResultSet` set to the row with the count to retrieve + * @return The count from the row + * @throws DocumentException If the dialect has not been set (unchecked) + */ + def toCount(rs: ResultSet, tag: ClassTag[Long] = ClassTag.Long): Long = + CoreResults.toCount(rs, Long.getClass) + + /** + * Extract a true/false value from the first column + * + * @param rs A `ResultSet` set to the row with the true/false value to retrieve + * @return The true/false value from the row + * @throws DocumentException If the dialect has not been set (unchecked) + */ + def toExists(rs: ResultSet, tag: ClassTag[Boolean] = ClassTag.Boolean): Boolean = + CoreResults.toExists(rs, Boolean.getClass) diff --git a/src/scala/src/main/scala/solutions/bitbadger/documents/scala/extensions/package.scala b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/extensions/package.scala new file mode 100644 index 0000000..142fd26 --- /dev/null +++ b/src/scala/src/main/scala/solutions/bitbadger/documents/scala/extensions/package.scala @@ -0,0 +1,499 @@ +package solutions.bitbadger.documents.scala + +import solutions.bitbadger.documents.{DocumentIndex, Field, FieldMatch, Parameter} + +import java.sql.{Connection, ResultSet} +import scala.reflect.ClassTag + +package object extensions: + + extension (conn: Connection) + + // ~~~ CUSTOM QUERIES ~~~ + + /** + * Execute a query that returns a list of results + * + * @param query The query to retrieve the results + * @param parameters Parameters to use for the query + * @param mapFunc The mapping function between the document and the domain item + * @return A list of results for the given query + * @throws DocumentException If parameters are invalid + */ + def customList[TDoc](query: String, parameters: Seq[Parameter[?]], + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] = + Custom.list[TDoc](query, parameters, conn, mapFunc) + + /** + * Execute a query that returns a list of results + * + * @param query The query to retrieve the results + * @param mapFunc The mapping function between the document and the domain item + * @return A list of results for the given query + * @throws DocumentException If parameters are invalid + */ + def customList[TDoc](query: String, + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): List[TDoc] = + Custom.list[TDoc](query, conn, mapFunc) + + /** + * Execute a query that returns one or no results + * + * @param query The query to retrieve the results + * @param parameters Parameters to use for the query + * @param mapFunc The mapping function between the document and the domain item + * @return An optional document, filled if one matches the query + * @throws DocumentException If parameters are invalid + */ + def customSingle[TDoc](query: String, parameters: Seq[Parameter[?]], + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + Custom.single[TDoc](query, parameters, conn, mapFunc) + + /** + * Execute a query that returns one or no results + * + * @param query The query to retrieve the results + * @param mapFunc The mapping function between the document and the domain item + * @return An optional document, filled if one matches the query + * @throws DocumentException If parameters are invalid + */ + def customSingle[TDoc](query: String, + mapFunc: (ResultSet, ClassTag[TDoc]) => TDoc)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + Custom.single[TDoc](query, conn, mapFunc) + + /** + * Execute a query that returns no results + * + * @param query The query to retrieve the results + * @param parameters Parameters to use for the query + * @throws DocumentException If parameters are invalid + */ + def customNonQuery(query: String, parameters: Seq[Parameter[?]] = List()): Unit = + Custom.nonQuery(query, parameters, conn) + + /** + * Execute a query that returns a scalar result + * + * @param query The query to retrieve the result + * @param parameters Parameters to use for the query + * @param mapFunc The mapping function between the document and the domain item + * @return The scalar value from the query + * @throws DocumentException If parameters are invalid + */ + def customScalar[T](query: String, parameters: Seq[Parameter[?]], + mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T = + Custom.scalar[T](query, parameters, conn, mapFunc) + + /** + * Execute a query that returns a scalar result + * + * @param query The query to retrieve the result + * @param mapFunc The mapping function between the document and the domain item + * @return The scalar value from the query + * @throws DocumentException If parameters are invalid + */ + def customScalar[T](query: String, + mapFunc: (ResultSet, ClassTag[T]) => T)(implicit tag: ClassTag[T]): T = + Custom.scalar[T](query, conn, mapFunc) + + // ~~~ DEFINITION QUERIES ~~~ + + /** + * Create a document table if necessary + * + * @param tableName The table whose existence should be ensured (may include schema) + * @throws DocumentException If the dialect is not configured + */ + def ensureTable(tableName: String): Unit = + Definition.ensureTable(tableName, conn) + + /** + * Create an index on field(s) within documents in the specified table if necessary + * + * @param tableName The table to be indexed (may include schema) + * @param indexName The name of the index to create + * @param fields One or more fields to be indexed< + * @throws DocumentException If any dependent process does + */ + def ensureFieldIndex(tableName: String, indexName: String, fields: Seq[String]): Unit = + Definition.ensureFieldIndex(tableName, indexName, fields, conn) + + /** + * Create a document index on a table (PostgreSQL only) + * + * @param tableName The table to be indexed (may include schema) + * @param indexType The type of index to ensure + * @throws DocumentException If called on a SQLite connection + */ + def ensureDocumentIndex(tableName: String, indexType: DocumentIndex): Unit = + Definition.ensureDocumentIndex (tableName, indexType, conn) + + // ~~~ DOCUMENT MANIPULATION QUERIES ~~~ + + /** + * Insert a new document + * + * @param tableName The table into which the document should be inserted (may include schema) + * @param document The document to be inserted + * @throws DocumentException If IDs are misconfigured, or if the database command fails + */ + def insert[TDoc](tableName: String, document: TDoc): Unit = + Document.insert(tableName, document, conn) + + /** + * Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert") + * + * @param tableName The table in which the document should be saved (may include schema) + * @param document The document to be saved + * @throws DocumentException If the database command fails + */ + def save[TDoc](tableName: String, document: TDoc): Unit = + Document.save(tableName, document, conn) + + /** + * Update (replace) a document by its ID + * + * @param tableName The table in which the document should be replaced (may include schema) + * @param docId The ID of the document to be replaced + * @param document The document to be replaced + * @throws DocumentException If no dialect has been configured, or if the database command fails + */ + def update[TKey, TDoc](tableName: String, docId: TKey, document: TDoc): Unit = + Document.update(tableName, docId, document, conn) + + // ~~~ DOCUMENT COUNT QUERIES ~~~ + + /** + * Count all documents in the table + * + * @param tableName The name of the table in which documents should be counted + * @return A count of the documents in the table + * @throws DocumentException If any dependent process does + */ + def countAll(tableName: String): Long = + Count.all(tableName, conn) + + /** + * Count documents using a field comparison + * + * @param tableName The name of the table in which documents should be counted + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched (optional, default `ALL`) + * @return A count of the matching documents in the table + * @throws DocumentException If the dialect has not been configured + */ + def countByFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Long = + Count.byFields(tableName, fields, howMatched, conn) + + /** + * Count documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table in which documents should be counted + * @param criteria The object for which JSON containment should be checked + * @return A count of the matching documents in the table + * @throws DocumentException If called on a SQLite connection + */ + def countByContains[TContains](tableName: String, criteria: TContains): Long = + Count.byContains(tableName, criteria, conn) + + /** + * Count documents using a JSON Path match query (PostgreSQL only) + * + * @param tableName The name of the table in which documents should be counted + * @param path The JSON path comparison to match + * @return A count of the matching documents in the table + * @throws DocumentException If called on a SQLite connection + */ + def countByJsonPath(tableName: String, path: String): Long = + Count.byJsonPath(tableName, path, conn) + + // ~~~ DOCUMENT EXISTENCE QUERIES ~~~ + + /** + * Determine a document's existence by its ID + * + * @param tableName The name of the table in which document existence should be checked + * @param docId The ID of the document to be checked + * @return True if the document exists, false if not + * @throws DocumentException If no dialect has been configured + */ + def existsById[TKey](tableName: String, docId: TKey): Boolean = + Exists.byId(tableName, docId, conn) + + /** + * Determine document existence using a field comparison + * + * @param tableName The name of the table in which document existence should be checked + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @return True if any matching documents exist, false if not + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def existsByFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Boolean = + Exists.byFields(tableName, fields, howMatched, conn) + + /** + * Determine document existence using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table in which document existence should be checked + * @param criteria The object for which JSON containment should be checked + * @return True if any matching documents exist, false if not + * @throws DocumentException If called on a SQLite connection + */ + def existsByContains[TContains](tableName: String, criteria: TContains): Boolean = + Exists.byContains(tableName, criteria, conn) + + /** + * Determine document existence using a JSON Path match query (PostgreSQL only) + * + * @param tableName The name of the table in which document existence should be checked + * @param path The JSON path comparison to match + * @return True if any matching documents exist, false if not + * @throws DocumentException If called on a SQLite connection + */ + def existsByJsonPath(tableName: String, path: String): Boolean = + Exists.byJsonPath(tableName, path, conn) + + // ~~~ DOCUMENT RETRIEVAL QUERIES ~~~ + + /** + * Retrieve all documents in the given table, ordering results by the optional given fields + * + * @param tableName The table from which documents should be retrieved + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return A list of documents from the given table + * @throws DocumentException If query execution fails + */ + def findAll[TDoc](tableName: String, orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): List[TDoc] = + Find.all[TDoc](tableName, orderBy, conn) + + /** + * Retrieve a document by its ID + * + * @param tableName The table from which the document should be retrieved + * @param docId The ID of the document to retrieve + * @return The document if it is found, `None` otherwise + * @throws DocumentException If no dialect has been configured + */ + def findById[TKey, TDoc](tableName: String, docId: TKey)(implicit tag: ClassTag[TDoc]): Option[TDoc] = + Find.byId[TKey, TDoc](tableName, docId, conn) + + /** + * Retrieve documents using a field comparison, ordering results by the optional given fields + * + * @param tableName The table from which the document should be retrieved + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return A list of documents matching the field comparison + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def findByFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None, + orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): List[TDoc] = + Find.byFields[TDoc](tableName, fields, howMatched, orderBy, conn) + + /** + * Retrieve documents using a JSON containment query, ordering results by the optional given fields (PostgreSQL + * only) + * + * @param tableName The name of the table in which document existence should be checked + * @param criteria The object for which JSON containment should be checked + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return A list of documents matching the JSON containment query + * @throws DocumentException If called on a SQLite connection + */ + def findByContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]] = List()) + (implicit tag: ClassTag[TDoc]): List[TDoc] = + Find.byContains[TDoc, TContains](tableName, criteria, orderBy, conn) + + /** + * Retrieve documents using a JSON Path match query, ordering results by the optional given fields (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param path The JSON path comparison to match + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return A list of documents matching the JSON Path match query + * @throws DocumentException If called on a SQLite connection + */ + def findByJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]] = List()) + (implicit tag: ClassTag[TDoc]): List[TDoc] = + Find.byJsonPath[TDoc](tableName, path, orderBy, conn) + + /** + * Retrieve the first document using a field comparison and optional ordering fields + * + * @param tableName The table from which documents should be retrieved + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched (optional, defaults to `FieldMatch.ALL`) + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return The first document matching the field comparison, or `None` if no matches are found + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def findFirstByFields[TDoc](tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None, + orderBy: Seq[Field[?]] = List())(implicit tag: ClassTag[TDoc]): Option[TDoc] = + Find.firstByFields[TDoc](tableName, fields, howMatched, orderBy, conn) + + /** + * Retrieve the first document using a JSON containment query and optional ordering fields (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param criteria The object for which JSON containment should be checked + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return The first document matching the JSON containment query, or `None` if no matches are found + * @throws DocumentException If called on a SQLite connection + */ + def findFirstByContains[TDoc, TContains](tableName: String, criteria: TContains, orderBy: Seq[Field[?]] = List()) + (implicit tag: ClassTag[TDoc]): Option[TDoc] = + Find.firstByContains[TDoc, TContains](tableName, criteria, orderBy, conn) + + /** + * Retrieve the first document using a JSON Path match query and optional ordering fields (PostgreSQL only) + * + * @param tableName The table from which documents should be retrieved + * @param path The JSON path comparison to match + * @param orderBy Fields by which the query should be ordered (optional, defaults to no ordering) + * @return The first document matching the JSON Path match query, or `None` if no matches are found + * @throws DocumentException If called on a SQLite connection + */ + def findFirstByJsonPath[TDoc](tableName: String, path: String, orderBy: Seq[Field[?]] = List()) + (implicit tag: ClassTag[TDoc]): Option[TDoc] = + Find.firstByJsonPath[TDoc](tableName, path, orderBy, conn) + + // ~~~ DOCUMENT PATCH (PARTIAL UPDATE) QUERIES ~~~ + + /** + * Patch a document by its ID + * + * @param tableName The name of the table in which a document should be patched + * @param docId The ID of the document to be patched + * @param patch The object whose properties should be replaced in the document + * @throws DocumentException If no dialect has been configured + */ + def patchById[TKey, TPatch](tableName: String, docId: TKey, patch: TPatch): Unit = + Patch.byId(tableName, docId, patch, conn) + + /** + * Patch documents using a field comparison + * + * @param tableName The name of the table in which documents should be patched + * @param fields The fields which should be compared + * @param patch The object whose properties should be replaced in the document + * @param howMatched How the fields should be matched + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def patchByFields[TPatch](tableName: String, fields: Seq[Field[?]], patch: TPatch, + howMatched: Option[FieldMatch] = None): Unit = + Patch.byFields(tableName, fields, patch, howMatched, conn) + + /** + * Patch documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table in which documents should be patched + * @param criteria The object against which JSON containment should be checked + * @param patch The object whose properties should be replaced in the document + * @throws DocumentException If called on a SQLite connection + */ + def patchByContains[TContains, TPatch](tableName: String, criteria: TContains, patch: TPatch): Unit = + Patch.byContains(tableName, criteria, patch, conn) + + /** + * Patch documents using a JSON Path match query (PostgreSQL only) + * + * @param tableName The name of the table in which documents should be patched + * @param path The JSON path comparison to match + * @param patch The object whose properties should be replaced in the document + * @throws DocumentException If called on a SQLite connection + */ + def patchByJsonPath[TPatch](tableName: String, path: String, patch: TPatch): Unit = + Patch.byJsonPath(tableName, path, patch, conn) + + // ~~~ DOCUMENT FIELD REMOVAL QUERIES ~~~ + + /** + * Remove fields from a document by its ID + * + * @param tableName The name of the table in which the document's fields should be removed + * @param docId The ID of the document to have fields removed + * @param toRemove The names of the fields to be removed + * @throws DocumentException If no dialect has been configured + */ + def removeFieldsById[TKey](tableName: String, docId: TKey, toRemove: Seq[String]): Unit = + RemoveFields.byId(tableName, docId, toRemove, conn) + + /** + * Remove fields from documents using a field comparison + * + * @param tableName The name of the table in which document fields should be removed + * @param fields The fields which should be compared + * @param toRemove The names of the fields to be removed + * @param howMatched How the fields should be matched + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def removeFieldsByFields(tableName: String, fields: Seq[Field[?]], toRemove: Seq[String], + howMatched: Option[FieldMatch] = None): Unit = + RemoveFields.byFields(tableName, fields, toRemove, howMatched, conn) + + /** + * Remove fields from documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table in which document fields should be removed + * @param criteria The object against which JSON containment should be checked + * @param toRemove The names of the fields to be removed + * @throws DocumentException If called on a SQLite connection + */ + def removeFieldsByContains[TContains](tableName: String, criteria: TContains, toRemove: Seq[String]): Unit = + RemoveFields.byContains(tableName, criteria, toRemove, conn) + + /** + * Remove fields from documents using a JSON Path match query (PostgreSQL only) + * + * @param tableName The name of the table in which document fields should be removed + * @param path The JSON path comparison to match + * @param toRemove The names of the fields to be removed + * @throws DocumentException If called on a SQLite connection + */ + def removeFieldsByJsonPath(tableName: String, path: String, toRemove: Seq[String]): Unit = + RemoveFields.byJsonPath(tableName, path, toRemove, conn) + + // ~~~ DOCUMENT DELETION QUERIES ~~~ + + /** + * Delete a document by its ID + * + * @param tableName The name of the table from which documents should be deleted + * @param docId The ID of the document to be deleted + * @throws DocumentException If no dialect has been configured + */ + def deleteById[TKey](tableName: String, docId: TKey): Unit = + Delete.byId(tableName, docId, conn) + + /** + * Delete documents using a field comparison + * + * @param tableName The name of the table from which documents should be deleted + * @param fields The fields which should be compared + * @param howMatched How the fields should be matched + * @throws DocumentException If no dialect has been configured, or if parameters are invalid + */ + def deleteByFields(tableName: String, fields: Seq[Field[?]], howMatched: Option[FieldMatch] = None): Unit = + Delete.byFields(tableName, fields, howMatched, conn) + + /** + * Delete documents using a JSON containment query (PostgreSQL only) + * + * @param tableName The name of the table from which documents should be deleted + * @param criteria The object for which JSON containment should be checked + * @throws DocumentException If called on a SQLite connection + */ + def deleteByContains[TContains](tableName: String, criteria: TContains): Unit = + Delete.byContains(tableName, criteria, conn) + + /** + * Delete documents using a JSON Path match query (PostgreSQL only) + * + * @param tableName The name of the table from which documents should be deleted + * @param path The JSON path comparison to match + * @throws DocumentException If called on a SQLite connection + */ + def deleteByJsonPath(tableName: String, path: String): Unit = + Delete.byJsonPath(tableName, path, conn) diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/AutoIdTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/AutoIdTest.scala similarity index 95% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/AutoIdTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/AutoIdTest.scala index 6c41e8f..36bbc46 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/AutoIdTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/AutoIdTest.scala @@ -1,12 +1,11 @@ -package solutions.bitbadger.documents.scala +package solutions.bitbadger.documents.scala.tests -import org.junit.jupiter.api.Assertions._ +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{DisplayName, Test} -import solutions.bitbadger.documents.scala.support.{ByteIdClass, IntIdClass, LongIdClass, ShortIdClass, StringIdClass} import solutions.bitbadger.documents.{AutoId, DocumentException} -@DisplayName("JVM | Scala | AutoId") -class AutoIdTest { +@DisplayName("Scala | AutoId") +class AutoIdTest: @Test @DisplayName("Generates a UUID string") @@ -125,4 +124,3 @@ class AutoIdTest { @DisplayName("needsAutoId fails for Random String strategy and non-string ID") def needsAutoIdFailsForRandomNonString(): Unit = assertThrows(classOf[DocumentException], () => AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id")) -} diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ByteIdClass.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ByteIdClass.scala new file mode 100644 index 0000000..130f16a --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ByteIdClass.scala @@ -0,0 +1,3 @@ +package solutions.bitbadger.documents.scala.tests + +class ByteIdClass(var id: Byte) diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ConfigurationTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ConfigurationTest.scala similarity index 87% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ConfigurationTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ConfigurationTest.scala index 1864216..f325ff9 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ConfigurationTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ConfigurationTest.scala @@ -1,11 +1,11 @@ -package solutions.bitbadger.documents.scala +package solutions.bitbadger.documents.scala.tests +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{DisplayName, Test} -import org.junit.jupiter.api.Assertions._ import solutions.bitbadger.documents.{AutoId, Configuration, Dialect, DocumentException} -@DisplayName("JVM | Scala | Configuration") -class ConfigurationTest { +@DisplayName("Scala | Configuration") +class ConfigurationTest: @Test @DisplayName("Default ID field is `id`") @@ -32,4 +32,3 @@ class ConfigurationTest { } finally { Configuration.setConnectionString(null) } -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/CountQueryTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/CountQueryTest.scala similarity index 90% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/CountQueryTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/CountQueryTest.scala index 1b601b0..00879e6 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/CountQueryTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/CountQueryTest.scala @@ -1,16 +1,14 @@ -package solutions.bitbadger.documents.scala.query +package solutions.bitbadger.documents.scala.tests -import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import solutions.bitbadger.documents.{DocumentException, Field} import solutions.bitbadger.documents.query.CountQuery -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE import scala.jdk.CollectionConverters.* -@DisplayName("JVM | Scala | Query | CountQuery") -class CountQueryTest { +@DisplayName("Scala | Query | CountQuery") +class CountQueryTest: /** * Clear the connection string (resets Dialect) @@ -66,4 +64,3 @@ class CountQueryTest { def byJsonPathSQLite(): Unit = ForceDialect.sqlite() assertThrows(classOf[DocumentException], () => CountQuery.byJsonPath(TEST_TABLE)) -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DefinitionQueryTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DefinitionQueryTest.scala similarity index 94% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DefinitionQueryTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DefinitionQueryTest.scala index d963970..62e4263 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DefinitionQueryTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DefinitionQueryTest.scala @@ -1,16 +1,14 @@ -package solutions.bitbadger.documents.scala.query +package solutions.bitbadger.documents.scala.tests -import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import solutions.bitbadger.documents.{Dialect, DocumentException, DocumentIndex} import solutions.bitbadger.documents.query.DefinitionQuery -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE import scala.jdk.CollectionConverters.* -@DisplayName("JVM | Scala | Query | DefinitionQuery") -class DefinitionQueryTest { +@DisplayName("Scala | Query | DefinitionQuery") +class DefinitionQueryTest: /** * Clear the connection string (resets Dialect) @@ -104,4 +102,3 @@ class DefinitionQueryTest { ForceDialect.sqlite() assertThrows(classOf[DocumentException], () => DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL)) -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DeleteQueryTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DeleteQueryTest.scala similarity index 91% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DeleteQueryTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DeleteQueryTest.scala index 1106867..47ba9f5 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DeleteQueryTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DeleteQueryTest.scala @@ -1,16 +1,14 @@ -package solutions.bitbadger.documents.scala.query +package solutions.bitbadger.documents.scala.tests -import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import solutions.bitbadger.documents.{DocumentException, Field} import solutions.bitbadger.documents.query.DeleteQuery -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE import scala.jdk.CollectionConverters.* -@DisplayName("JVM | Scala | Query | DeleteQuery") -class DeleteQueryTest { +@DisplayName("Scala | Query | DeleteQuery") +class DeleteQueryTest: /** * Clear the connection string (resets Dialect) @@ -74,4 +72,3 @@ class DeleteQueryTest { def byJsonPathSQLite(): Unit = ForceDialect.sqlite() assertThrows(classOf[DocumentException], () => DeleteQuery.byJsonPath(TEST_TABLE)) -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DialectTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DialectTest.scala similarity index 91% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DialectTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DialectTest.scala index 5089914..27f86da 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DialectTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DialectTest.scala @@ -1,11 +1,11 @@ -package solutions.bitbadger.documents.scala +package solutions.bitbadger.documents.scala.tests -import org.junit.jupiter.api.{DisplayName, Test} import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.{DisplayName, Test} import solutions.bitbadger.documents.{Dialect, DocumentException} -@DisplayName("JVM | Scala | Dialect") -class DialectTest { +@DisplayName("Scala | Dialect") +class DialectTest: @Test @DisplayName("deriveFromConnectionString derives PostgreSQL correctly") @@ -31,4 +31,4 @@ class DialectTest { assertTrue(ex.getMessage.contains("[SQL Server]"), "The connection string should have been in the exception message") } -} + diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DocumentIndexTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DocumentIndexTest.scala similarity index 80% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DocumentIndexTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DocumentIndexTest.scala index fd372bf..0f2f1b0 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DocumentIndexTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DocumentIndexTest.scala @@ -1,11 +1,11 @@ -package solutions.bitbadger.documents.scala +package solutions.bitbadger.documents.scala.tests import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{DisplayName, Test} import solutions.bitbadger.documents.DocumentIndex -@DisplayName("JVM | Scala | DocumentIndex") -class DocumentIndexTest { +@DisplayName("Scala | DocumentIndex") +class DocumentIndexTest: @Test @DisplayName("FULL uses proper SQL") @@ -16,4 +16,3 @@ class DocumentIndexTest { @DisplayName("OPTIMIZED uses proper SQL") def optimizedSQL(): Unit = assertEquals(" jsonb_path_ops", DocumentIndex.OPTIMIZED.getSql, "The SQL for Optimized is incorrect") -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DocumentQueryTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DocumentQueryTest.scala similarity index 93% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DocumentQueryTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DocumentQueryTest.scala index 2410773..57a7cb8 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DocumentQueryTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/DocumentQueryTest.scala @@ -1,14 +1,12 @@ -package solutions.bitbadger.documents.scala.query +package solutions.bitbadger.documents.scala.tests +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{AfterEach, DisplayName, Test} -import org.junit.jupiter.api.Assertions._ import solutions.bitbadger.documents.{AutoId, Configuration, DocumentException} import solutions.bitbadger.documents.query.DocumentQuery -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE -@DisplayName("JVM | Scala | Query | DocumentQuery") -class DocumentQueryTest { +@DisplayName("Scala | Query | DocumentQuery") +class DocumentQueryTest: /** * Clear the connection string (resets Dialect) @@ -110,4 +108,3 @@ class DocumentQueryTest { def update(): Unit = assertEquals(s"UPDATE $TEST_TABLE SET data = :data", DocumentQuery.update(TEST_TABLE), "Update query not constructed correctly") -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/ExistsQueryTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ExistsQueryTest.scala similarity index 90% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/ExistsQueryTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ExistsQueryTest.scala index 3ab3992..c198d7a 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/ExistsQueryTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ExistsQueryTest.scala @@ -1,16 +1,14 @@ -package solutions.bitbadger.documents.scala.query +package solutions.bitbadger.documents.scala.tests +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{AfterEach, DisplayName, Test} -import org.junit.jupiter.api.Assertions._ import solutions.bitbadger.documents.{DocumentException, Field} import solutions.bitbadger.documents.query.ExistsQuery -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE import scala.jdk.CollectionConverters.* -@DisplayName("JVM | Scala | Query | ExistsQuery") -class ExistsQueryTest { +@DisplayName("Scala | Query | ExistsQuery") +class ExistsQueryTest: /** * Clear the connection string (resets Dialect) @@ -74,4 +72,3 @@ class ExistsQueryTest { def byJsonPathSQLite(): Unit = ForceDialect.sqlite() assertThrows(classOf[DocumentException], () => ExistsQuery.byJsonPath(TEST_TABLE)) -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldMatchTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/FieldMatchTest.scala similarity index 73% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldMatchTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/FieldMatchTest.scala index 4b2440b..596e6f8 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldMatchTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/FieldMatchTest.scala @@ -1,14 +1,14 @@ -package solutions.bitbadger.documents.scala +package solutions.bitbadger.documents.scala.tests -import org.junit.jupiter.api.Assertions._ +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{DisplayName, Test} import solutions.bitbadger.documents.FieldMatch /** * Unit tests for the `FieldMatch` enum */ -@DisplayName("JVM | Scala | FieldMatch") -class FieldMatchTest { +@DisplayName("Scala | FieldMatch") +class FieldMatchTest: @Test @DisplayName("ANY uses proper SQL") @@ -19,4 +19,3 @@ class FieldMatchTest { @DisplayName("ALL uses proper SQL") def all(): Unit = assertEquals("AND", FieldMatch.ALL.getSql, "ALL should use AND") -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/FieldTest.scala similarity index 98% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/FieldTest.scala index 3923561..0b60727 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/FieldTest.scala @@ -1,14 +1,13 @@ -package solutions.bitbadger.documents.scala +package solutions.bitbadger.documents.scala.tests -import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import org.junit.jupiter.api.Assertions.* -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.{Dialect, DocumentException, Field, FieldFormat, Op} +import org.junit.jupiter.api.{AfterEach, DisplayName, Test} +import solutions.bitbadger.documents.* -import scala.jdk.CollectionConverters.* +import _root_.scala.jdk.CollectionConverters.* -@DisplayName("JVM | Scala | Field") -class FieldTest { +@DisplayName("Scala | Field") +class FieldTest: /** * Clear the connection string (resets Dialect) @@ -536,4 +535,3 @@ class FieldTest { assertEquals("data->'A'->'Long'->'Path'->'to'->'the'->'Property'", Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.JSON), "Path not constructed correctly") -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/FindQueryTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/FindQueryTest.scala similarity index 92% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/FindQueryTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/FindQueryTest.scala index 06e1586..a44b825 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/FindQueryTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/FindQueryTest.scala @@ -1,16 +1,14 @@ -package solutions.bitbadger.documents.scala.query +package solutions.bitbadger.documents.scala.tests -import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import solutions.bitbadger.documents.{DocumentException, Field} import solutions.bitbadger.documents.query.FindQuery -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE import scala.jdk.CollectionConverters.* -@DisplayName("JVM | Scala | Query | FindQuery") -class FindQueryTest { +@DisplayName("Scala | Query | FindQuery") +class FindQueryTest: /** * Clear the connection string (resets Dialect) @@ -79,4 +77,3 @@ class FindQueryTest { def byJsonPathSQLite(): Unit = ForceDialect.sqlite() assertThrows(classOf[DocumentException], () => FindQuery.byJsonPath(TEST_TABLE)) -} diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ForceDialect.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ForceDialect.scala new file mode 100644 index 0000000..7665d26 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ForceDialect.scala @@ -0,0 +1,17 @@ +package solutions.bitbadger.documents.scala.tests + +import solutions.bitbadger.documents.Configuration + +/** + * These functions use a dummy connection string to force the given dialect for a given test + */ +object ForceDialect: + + def postgres (): Unit = + Configuration.setConnectionString(":postgresql:") + + def sqlite (): Unit = + Configuration.setConnectionString(":sqlite:") + + def none (): Unit = + Configuration.setConnectionString(null) diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/IntIdClass.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/IntIdClass.scala new file mode 100644 index 0000000..c04bb11 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/IntIdClass.scala @@ -0,0 +1,3 @@ +package solutions.bitbadger.documents.scala.tests + +class IntIdClass(var id: Int) diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/LongIdClass.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/LongIdClass.scala new file mode 100644 index 0000000..1f03ffa --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/LongIdClass.scala @@ -0,0 +1,3 @@ +package solutions.bitbadger.documents.scala.tests + +class LongIdClass(var id: Long) diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/OpTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/OpTest.scala similarity index 95% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/OpTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/OpTest.scala index 62cb683..b28d6fc 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/OpTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/OpTest.scala @@ -1,11 +1,11 @@ -package solutions.bitbadger.documents.scala +package solutions.bitbadger.documents.scala.tests import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{DisplayName, Test} import solutions.bitbadger.documents.Op -@DisplayName("JVM | Kotlin | Op") -class OpTest { +@DisplayName("Scala | Op") +class OpTest: @Test @DisplayName("EQUAL uses proper SQL") @@ -61,4 +61,3 @@ class OpTest { @DisplayName("NOT_EXISTS uses proper SQL") def notExistsSQL(): Unit = assertEquals("IS NULL", Op.NOT_EXISTS.getSql, "The SQL for not-exists is incorrect") -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterNameTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ParameterNameTest.scala similarity index 89% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterNameTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ParameterNameTest.scala index d58c4fa..6231060 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterNameTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ParameterNameTest.scala @@ -1,11 +1,11 @@ -package solutions.bitbadger.documents.scala +package solutions.bitbadger.documents.scala.tests import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{DisplayName, Test} import solutions.bitbadger.documents.ParameterName -@DisplayName("JVM | Scala | ParameterName") -class ParameterNameTest { +@DisplayName("Scala | ParameterName") +class ParameterNameTest: @Test @DisplayName("derive works when given existing names") @@ -22,4 +22,3 @@ class ParameterNameTest { assertEquals(":field1", names.derive(null), "Counter should have advanced from previous call") assertEquals(":field2", names.derive(null), "Counter should have advanced from previous call") assertEquals(":field3", names.derive(null), "Counter should have advanced from previous call") -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ParameterTest.scala similarity index 91% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ParameterTest.scala index 0c3c689..11524cf 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ParameterTest.scala @@ -1,11 +1,11 @@ -package solutions.bitbadger.documents.scala +package solutions.bitbadger.documents.scala.tests import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{DisplayName, Test} import solutions.bitbadger.documents.{DocumentException, Parameter, ParameterType} -@DisplayName("JVM | Scala | Parameter") -class ParameterTest { +@DisplayName("Scala | Parameter") +class ParameterTest: @Test @DisplayName("Construction with colon-prefixed name") @@ -27,4 +27,3 @@ class ParameterTest { @DisplayName("Construction fails with incorrect prefix") def ctorFailsForPrefix(): Unit = assertThrows(classOf[DocumentException], () => Parameter("it", ParameterType.JSON, "")) -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/ParametersTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ParametersTest.scala similarity index 83% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/ParametersTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ParametersTest.scala index 1420bdc..66e1093 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/ParametersTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ParametersTest.scala @@ -1,15 +1,13 @@ -package solutions.bitbadger.documents.scala.jvm +package solutions.bitbadger.documents.scala.tests -import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.{AfterEach, DisplayName, Test} import solutions.bitbadger.documents.{DocumentException, Field, Parameter, ParameterType} -import solutions.bitbadger.documents.jvm.Parameters -import solutions.bitbadger.documents.support.ForceDialect +import solutions.bitbadger.documents.scala.Parameters -import scala.jdk.CollectionConverters.* +@DisplayName("Scala | Parameters") +class ParametersTest: -@DisplayName("JVM | Scala | Parameters") -class ParametersTest { /** * Reset the dialect */ @@ -21,7 +19,7 @@ class ParametersTest { @DisplayName("nameFields works with no changes") def nameFieldsNoChange(): Unit = val fields = Field.equal("a", "", ":test") :: Field.exists("q") :: Field.equal("b", "", ":me") :: Nil - val named = Parameters.nameFields(fields.asJava).asScala.toList + val named = Parameters.nameFields(fields).toList assertEquals(fields.size, named.size, "There should have been 3 fields in the list") assertSame(fields.head, named.head, "The first field should be the same") assertSame(fields(1), named(1), "The second field should be the same") @@ -32,7 +30,7 @@ class ParametersTest { def nameFieldsChange(): Unit = val fields = Field.equal("a", "") :: Field.equal("e", "", ":hi") :: Field.equal("b", "") :: Field.notExists("z") :: Nil - val named = Parameters.nameFields(fields.asJava).asScala.toList + val named = Parameters.nameFields(fields).toList assertEquals(fields.size, named.size, "There should have been 4 fields in the list") assertNotSame(fields.head, named.head, "The first field should not be the same") assertEquals(":field0", named.head.getParameterName, "First parameter name incorrect") @@ -44,8 +42,8 @@ class ParametersTest { @Test @DisplayName("replaceNamesInQuery replaces successfully") def replaceNamesInQuery(): Unit = - val parameters = - (Parameter(":data", ParameterType.JSON, "{}") :: Parameter(":data_ext", ParameterType.STRING, "") :: Nil).asJava + val parameters = Parameter(":data", ParameterType.JSON, "{}") :: + Parameter(":data_ext", ParameterType.STRING, "") :: Nil val query = "SELECT data, data_ext FROM tbl WHERE data = :data AND data_ext = :data_ext AND more_data = :data" assertEquals("SELECT data, data_ext FROM tbl WHERE data = ? AND data_ext = ? AND more_data = ?", Parameters.replaceNamesInQuery(query, parameters), "Parameters not replaced correctly") @@ -54,7 +52,7 @@ class ParametersTest { @DisplayName("fieldNames generates a single parameter (PostgreSQL)") def fieldNamesSinglePostgres(): Unit = ForceDialect.postgres() - val nameParams = Parameters.fieldNames(("test" :: Nil).asJava).asScala.toList + val nameParams = Parameters.fieldNames("test" :: Nil).toList assertEquals(1, nameParams.size, "There should be one name parameter") assertEquals(":name", nameParams.head.getName, "The parameter name is incorrect") assertEquals(ParameterType.STRING, nameParams.head.getType, "The parameter type is incorrect") @@ -64,7 +62,7 @@ class ParametersTest { @DisplayName("fieldNames generates multiple parameters (PostgreSQL)") def fieldNamesMultiplePostgres(): Unit = ForceDialect.postgres() - val nameParams = Parameters.fieldNames(("test" :: "this" :: "today" :: Nil).asJava).asScala.toList + val nameParams = Parameters.fieldNames("test" :: "this" :: "today" :: Nil).toList assertEquals(1, nameParams.size, "There should be one name parameter") assertEquals(":name", nameParams.head.getName, "The parameter name is incorrect") assertEquals(ParameterType.STRING, nameParams.head.getType, "The parameter type is incorrect") @@ -74,7 +72,7 @@ class ParametersTest { @DisplayName("fieldNames generates a single parameter (SQLite)") def fieldNamesSingleSQLite(): Unit = ForceDialect.sqlite() - val nameParams = Parameters.fieldNames(("test" :: Nil).asJava).asScala.toList + val nameParams = Parameters.fieldNames("test" :: Nil).toList assertEquals(1, nameParams.size, "There should be one name parameter") assertEquals(":name0", nameParams.head.getName, "The parameter name is incorrect") assertEquals(ParameterType.STRING, nameParams.head.getType, "The parameter type is incorrect") @@ -84,7 +82,7 @@ class ParametersTest { @DisplayName("fieldNames generates multiple parameters (SQLite)") def fieldNamesMultipleSQLite(): Unit = ForceDialect.sqlite() - val nameParams = Parameters.fieldNames(("test" :: "this" :: "today" :: Nil).asJava).asScala.toList + val nameParams = Parameters.fieldNames("test" :: "this" :: "today" :: Nil).toList assertEquals(3, nameParams.size, "There should be one name parameter") assertEquals(":name0", nameParams.head.getName, "The first parameter name is incorrect") assertEquals(ParameterType.STRING, nameParams.head.getType, "The first parameter type is incorrect") @@ -99,5 +97,4 @@ class ParametersTest { @Test @DisplayName("fieldNames fails if dialect not set") def fieldNamesFails(): Unit = - assertThrows(classOf[DocumentException], () => Parameters.fieldNames(List().asJava)) -} + assertThrows(classOf[DocumentException], () => Parameters.fieldNames(List())) diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/PatchQueryTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/PatchQueryTest.scala similarity index 90% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/PatchQueryTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/PatchQueryTest.scala index 2741f2d..92787dc 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/PatchQueryTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/PatchQueryTest.scala @@ -1,16 +1,14 @@ -package solutions.bitbadger.documents.scala.query +package solutions.bitbadger.documents.scala.tests +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{AfterEach, DisplayName, Test} -import org.junit.jupiter.api.Assertions._ import solutions.bitbadger.documents.{DocumentException, Field} import solutions.bitbadger.documents.query.PatchQuery -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE import scala.jdk.CollectionConverters.* -@DisplayName("JVM | Scala | Query | PatchQuery") -class PatchQueryTest { +@DisplayName("Scala | Query | PatchQuery") +class PatchQueryTest: /** * Reset the dialect @@ -72,4 +70,3 @@ class PatchQueryTest { def byJsonPathSQLite(): Unit = ForceDialect.sqlite() assertThrows(classOf[DocumentException], () => PatchQuery.byJsonPath(TEST_TABLE)) -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/QueryUtilsTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/QueryUtilsTest.scala similarity index 96% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/QueryUtilsTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/QueryUtilsTest.scala index 3076708..77ca9fd 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/QueryUtilsTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/QueryUtilsTest.scala @@ -1,15 +1,14 @@ -package solutions.bitbadger.documents.scala.query +package solutions.bitbadger.documents.scala.tests +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{AfterEach, DisplayName, Test} -import org.junit.jupiter.api.Assertions._ import solutions.bitbadger.documents.{Dialect, Field, FieldMatch} import solutions.bitbadger.documents.query.QueryUtils -import solutions.bitbadger.documents.support.ForceDialect import scala.jdk.CollectionConverters.* -@DisplayName("JVM | Scala | Query | Package Functions") -class QueryUtilsTest { +@DisplayName("Scala | Query | Package Functions") +class QueryUtilsTest: /** * Clear the connection string (resets Dialect) @@ -135,4 +134,3 @@ class QueryUtilsTest { assertEquals(" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST", QueryUtils.orderBy(List(Field.named("i:Test.Field ASC NULLS LAST")).asJava, Dialect.SQLITE), "ORDER BY not constructed correctly") -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/RemoveFieldsQueryTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/RemoveFieldsQueryTest.scala similarity index 91% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/RemoveFieldsQueryTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/RemoveFieldsQueryTest.scala index e5e93e0..59296dc 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/RemoveFieldsQueryTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/RemoveFieldsQueryTest.scala @@ -1,16 +1,14 @@ -package solutions.bitbadger.documents.scala.query +package solutions.bitbadger.documents.scala.tests +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{AfterEach, DisplayName, Test} -import org.junit.jupiter.api.Assertions._ import solutions.bitbadger.documents.{DocumentException, Field, Parameter, ParameterType} import solutions.bitbadger.documents.query.RemoveFieldsQuery -import solutions.bitbadger.documents.support.ForceDialect -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE import scala.jdk.CollectionConverters.* -@DisplayName("JVM | Scala | Query | RemoveFieldsQuery") -class RemoveFieldsQueryTest { +@DisplayName("Scala | Query | RemoveFieldsQuery") +class RemoveFieldsQueryTest: /** * Reset the dialect @@ -82,4 +80,3 @@ class RemoveFieldsQueryTest { def byJsonPathSQLite(): Unit = ForceDialect.sqlite() assertThrows(classOf[DocumentException], () => RemoveFieldsQuery.byJsonPath(TEST_TABLE, List().asJava)) -} diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ShortIdClass.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ShortIdClass.scala new file mode 100644 index 0000000..35f2026 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/ShortIdClass.scala @@ -0,0 +1,3 @@ +package solutions.bitbadger.documents.scala.tests + +class ShortIdClass(var id: Short) diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/StringIdClass.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/StringIdClass.scala new file mode 100644 index 0000000..2a97774 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/StringIdClass.scala @@ -0,0 +1,3 @@ +package solutions.bitbadger.documents.scala.tests + +class StringIdClass(var id: String) diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/WhereTest.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/WhereTest.scala similarity index 95% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/WhereTest.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/WhereTest.scala index 26acf2c..87e05e1 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/WhereTest.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/WhereTest.scala @@ -1,15 +1,14 @@ -package solutions.bitbadger.documents.scala.query +package solutions.bitbadger.documents.scala.tests +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.{AfterEach, DisplayName, Test} -import org.junit.jupiter.api.Assertions._ import solutions.bitbadger.documents.{DocumentException, Field, FieldMatch} import solutions.bitbadger.documents.query.Where -import solutions.bitbadger.documents.support.ForceDialect import scala.jdk.CollectionConverters.* -@DisplayName("JVM | Scala | Query | Where") -class WhereTest { +@DisplayName("Scala | Query | Where") +class WhereTest: /** * Clear the connection string (resets Dialect) @@ -140,4 +139,3 @@ class WhereTest { def jsonPathFailsSQLite(): Unit = ForceDialect.sqlite() assertThrows(classOf[DocumentException], () => Where.jsonPathMatches()) -} diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/ArrayDocument.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/ArrayDocument.scala new file mode 100644 index 0000000..83d1081 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/ArrayDocument.scala @@ -0,0 +1,11 @@ +package solutions.bitbadger.documents.scala.tests.integration + +class ArrayDocument(val id: String = "", val values: List[String] = List()) + +object ArrayDocument: + + /** A set of documents used for integration tests */ + val testDocuments: List[ArrayDocument] = + ArrayDocument("first", "a" :: "b" :: "c" :: Nil) :: + ArrayDocument("second", "c" :: "d" :: "e" :: Nil) :: + ArrayDocument("third", "x" :: "y" :: "z" :: Nil) :: Nil diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/CountFunctions.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/CountFunctions.scala new file mode 100644 index 0000000..0fce19b --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/CountFunctions.scala @@ -0,0 +1,42 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.Assertions.* +import solutions.bitbadger.documents.Field +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE + +object CountFunctions: + + def all(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should have been 5 documents in the table") + + def byFieldsNumeric(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(3L, db.conn.countByFields(TEST_TABLE, Field.between("numValue", 10, 20) :: Nil), + "There should have been 3 matching documents") + + def byFieldsAlpha(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(1L, db.conn.countByFields(TEST_TABLE, Field.between("value", "aardvark", "apple") :: Nil), + "There should have been 1 matching document") + + def byContainsMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(2L, db.conn.countByContains(TEST_TABLE, Map.Map1("value", "purple")), + "There should have been 2 matching documents") + + def byContainsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(0L, db.conn.countByContains(TEST_TABLE, Map.Map1("value", "magenta")), + "There should have been no matching documents") + + def byJsonPathMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(2L, db.conn.countByJsonPath(TEST_TABLE, "$.numValue ? (@ < 5)"), + "There should have been 2 matching documents") + + def byJsonPathNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(0L, db.conn.countByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)"), + "There should have been no matching documents") diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/CustomFunctions.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/CustomFunctions.scala new file mode 100644 index 0000000..0e6f992 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/CustomFunctions.scala @@ -0,0 +1,52 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.Assertions.* +import solutions.bitbadger.documents.query.{CountQuery, DeleteQuery, FindQuery} +import solutions.bitbadger.documents.scala.Results +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE +import solutions.bitbadger.documents.{Configuration, Field, Parameter, ParameterType} + +object CustomFunctions: + + def listEmpty(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + db.conn.deleteByFields(TEST_TABLE, Field.exists(Configuration.idField) :: Nil) + val result = db.conn.customList[JsonDocument](FindQuery.all(TEST_TABLE), Results.fromData) + assertEquals(0, result.size, "There should have been no results") + + def listAll(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val result = db.conn.customList[JsonDocument](FindQuery.all(TEST_TABLE), Results.fromData) + assertEquals(5, result.size, "There should have been 5 results") + + def singleNone(db: ThrowawayDatabase): Unit = + assertTrue(db.conn.customSingle[JsonDocument](FindQuery.all(TEST_TABLE), Results.fromData).isEmpty, + "There should not have been a document returned") + + def singleOne(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertTrue(db.conn.customSingle[JsonDocument](FindQuery.all(TEST_TABLE), Results.fromData).isDefined, + "There should have been a document returned") + + def nonQueryChanges(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.customScalar[Long](CountQuery.all(TEST_TABLE), Results.toCount), + "There should have been 5 documents in the table") + db.conn.customNonQuery(s"DELETE FROM $TEST_TABLE") + assertEquals(0L, db.conn.customScalar[Long](CountQuery.all(TEST_TABLE), Results.toCount), + "There should have been no documents in the table") + + def nonQueryNoChanges(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.customScalar[Long](CountQuery.all(TEST_TABLE), Results.toCount), + "There should have been 5 documents in the table") + db.conn.customNonQuery(DeleteQuery.byId(TEST_TABLE, "eighty-two"), + Parameter(":id", ParameterType.STRING, "eighty-two") :: Nil) + assertEquals(5L, db.conn.customScalar[Long](CountQuery.all(TEST_TABLE), Results.toCount), + "There should still have been 5 documents in the table") + + def scalar(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(3L, db.conn.customScalar[Long](s"SELECT 3 AS it FROM $TEST_TABLE LIMIT 1", Results.toCount), + "The number 3 should have been returned") diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/DefinitionFunctions.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/DefinitionFunctions.scala new file mode 100644 index 0000000..80edff0 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/DefinitionFunctions.scala @@ -0,0 +1,36 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.Assertions.* +import solutions.bitbadger.documents.DocumentIndex +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE + +object DefinitionFunctions: + + def ensureTable(db: ThrowawayDatabase): Unit = + assertFalse(db.dbObjectExists("ensured"), "The 'ensured' table should not exist") + assertFalse(db.dbObjectExists("idx_ensured_key"), "The PK index for the 'ensured' table should not exist") + db.conn.ensureTable("ensured") + assertTrue(db.dbObjectExists("ensured"), "The 'ensured' table should exist") + assertTrue(db.dbObjectExists("idx_ensured_key"), "The PK index for the 'ensured' table should now exist") + + def ensureFieldIndex(db: ThrowawayDatabase): Unit = + assertFalse(db.dbObjectExists("idx_${TEST_TABLE}_test"), "The test index should not exist") + db.conn.ensureFieldIndex(TEST_TABLE, "test", "id" :: "category" :: Nil) + assertTrue(db.dbObjectExists(s"idx_${TEST_TABLE}_test"), "The test index should now exist") + + def ensureDocumentIndexFull(db: ThrowawayDatabase): Unit = + assertFalse(db.dbObjectExists("doc_table"), "The 'doc_table' table should not exist") + db.conn.ensureTable("doc_table") + assertTrue(db.dbObjectExists("doc_table"), "The 'doc_table' table should exist") + assertFalse(db.dbObjectExists("idx_doc_table_document"), "The document index should not exist") + db.conn.ensureDocumentIndex("doc_table", DocumentIndex.FULL) + assertTrue(db.dbObjectExists("idx_doc_table_document"), "The document index should exist") + + def ensureDocumentIndexOptimized(db: ThrowawayDatabase): Unit = + assertFalse(db.dbObjectExists("doc_table"), "The 'doc_table' table should not exist") + db.conn.ensureTable("doc_table") + assertTrue(db.dbObjectExists("doc_table"), "The 'doc_table' table should exist") + assertFalse(db.dbObjectExists("idx_doc_table_document"), "The document index should not exist") + db.conn.ensureDocumentIndex("doc_table", DocumentIndex.OPTIMIZED) + assertTrue(db.dbObjectExists("idx_doc_table_document"), "The document index should exist") diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/DeleteFunctions.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/DeleteFunctions.scala new file mode 100644 index 0000000..faf1281 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/DeleteFunctions.scala @@ -0,0 +1,56 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.Assertions.* +import solutions.bitbadger.documents.Field +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE + +object DeleteFunctions: + + def byIdMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteById(TEST_TABLE, "four") + assertEquals(4L, db.conn.countAll(TEST_TABLE), "There should now be 4 documents in the table") + + def byIdNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteById(TEST_TABLE, "negative four") + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + + def byFieldsMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByFields(TEST_TABLE, Field.notEqual("value", "purple") :: Nil) + assertEquals(2L, db.conn.countAll(TEST_TABLE), "There should now be 2 documents in the table") + + def byFieldsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByFields(TEST_TABLE, Field.equal("value", "crimson") :: Nil) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + + def byContainsMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByContains(TEST_TABLE, Map.Map1("value", "purple")) + assertEquals(3L, db.conn.countAll(TEST_TABLE), "There should now be 3 documents in the table") + + def byContainsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByContains(TEST_TABLE, Map.Map1("target", "acquired")) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") + + def byJsonPathMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByJsonPath(TEST_TABLE, "$.value ? (@ == \"purple\")") + assertEquals(3L, db.conn.countAll(TEST_TABLE), "There should now be 3 documents in the table") + + def byJsonPathNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should be 5 documents in the table") + db.conn.deleteByJsonPath(TEST_TABLE, "$.numValue ? (@ > 100)") + assertEquals(5L, db.conn.countAll(TEST_TABLE), "There should still be 5 documents in the table") diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/DocumentFunctions.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/DocumentFunctions.scala new file mode 100644 index 0000000..210d6de --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/DocumentFunctions.scala @@ -0,0 +1,111 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.Assertions.* +import solutions.bitbadger.documents.{AutoId, Configuration, DocumentException, Field} +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE + +object DocumentFunctions: + + import org.junit.jupiter.api.Assertions.assertThrows + + def insertDefault(db: ThrowawayDatabase): Unit = + assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table") + val doc = JsonDocument("turkey", "", 0, SubDocument("gobble", "gobble")) + db.conn.insert(TEST_TABLE, doc) + val after = db.conn.findAll[JsonDocument](TEST_TABLE) + assertEquals(1, after.size, "There should be one document in the table") + assertEquals(doc, after.head, "The document should be what was inserted") + + def insertDupe(db: ThrowawayDatabase): Unit = + db.conn.insert(TEST_TABLE, JsonDocument("a", "", 0, null)) + assertThrows(classOf[DocumentException], () => db.conn.insert(TEST_TABLE, JsonDocument("a", "b", 22, null)), + "Inserting a document with a duplicate key should have thrown an exception") + + def insertNumAutoId(db: ThrowawayDatabase): Unit = + try { + Configuration.autoIdStrategy = AutoId.NUMBER + Configuration.idField = "key" + assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table") + + db.conn.insert(TEST_TABLE, NumIdDocument(0, "one")) + db.conn.insert(TEST_TABLE, NumIdDocument(0, "two")) + db.conn.insert(TEST_TABLE, NumIdDocument(77, "three")) + db.conn.insert(TEST_TABLE, NumIdDocument(0, "four")) + + val after = db.conn.findAll[NumIdDocument](TEST_TABLE, Field.named("key") :: Nil) + assertEquals(4, after.size, "There should have been 4 documents returned") + assertEquals("1|2|77|78", after.fold("") { (acc, item) => s"$acc|$item" }, "The IDs were not generated correctly") + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED + Configuration.idField = "id" + } + + def insertUUIDAutoId(db: ThrowawayDatabase): Unit = + try { + Configuration.autoIdStrategy = AutoId.UUID + assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table") + + db.conn.insert(TEST_TABLE, JsonDocument("")) + + val after = db.conn.findAll[JsonDocument](TEST_TABLE) + assertEquals(1, after.size, "There should have been 1 document returned") + assertEquals(32, after.head.id.length, "The ID was not generated correctly") + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED + } + + def insertStringAutoId(db: ThrowawayDatabase): Unit = + try { + Configuration.autoIdStrategy = AutoId.RANDOM_STRING + assertEquals(0L, db.conn.countAll(TEST_TABLE), "There should be no documents in the table") + + db.conn.insert(TEST_TABLE, JsonDocument("")) + + Configuration.idStringLength = 21 + db.conn.insert(TEST_TABLE, JsonDocument("")) + + val after = db.conn.findAll[JsonDocument](TEST_TABLE) + assertEquals(2, after.size, "There should have been 2 documents returned") + assertEquals(16, after.head.id.length, "The first document's ID was not generated correctly") + assertEquals(21, after(1).id.length, "The second document's ID was not generated correctly") + } finally { + Configuration.autoIdStrategy = AutoId.DISABLED + Configuration.idStringLength = 16 + } + + def saveMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + db.conn.save(TEST_TABLE, JsonDocument("two", numValue = 44)) + val tryDoc = db.conn.findById[String, JsonDocument](TEST_TABLE, "two") + assertTrue(tryDoc.isDefined, "There should have been a document returned") + val doc = tryDoc.get + assertEquals("two", doc.id, "An incorrect document was returned") + assertEquals("", doc.value, "The \"value\" field was not updated") + assertEquals(44, doc.numValue, "The \"numValue\" field was not updated") + assertNull(doc.sub, "The \"sub\" field was not updated") + + def saveNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + db.conn.save(TEST_TABLE, JsonDocument("test", sub = SubDocument("a", "b"))) + assertTrue(db.conn.findById[String, JsonDocument](TEST_TABLE, "test").isDefined, + "The test document should have been saved") + + def updateMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + db.conn.update(TEST_TABLE, "one", JsonDocument("one", "howdy", 8, SubDocument("y", "z"))) + val tryDoc = db.conn.findById[String, JsonDocument](TEST_TABLE, "one") + assertTrue(tryDoc.isDefined, "There should have been a document returned") + val doc = tryDoc.get + assertEquals("one", doc.id, "An incorrect document was returned") + assertEquals("howdy", doc.value, "The \"value\" field was not updated") + assertEquals(8, doc.numValue, "The \"numValue\" field was not updated") + assertNotNull(doc.sub, "The sub-document should not be null") + assertEquals("y", doc.sub.foo, "The sub-document \"foo\" field was not updated") + assertEquals("z", doc.sub.bar, "The sub-document \"bar\" field was not updated") + + def updateNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsById(TEST_TABLE, "two-hundred")) + db.conn.update(TEST_TABLE, "two-hundred", JsonDocument("two-hundred", numValue = 200)) + assertFalse(db.conn.existsById(TEST_TABLE, "two-hundred")) diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/ExistsFunctions.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/ExistsFunctions.scala new file mode 100644 index 0000000..3f18932 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/ExistsFunctions.scala @@ -0,0 +1,46 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.Assertions.* +import solutions.bitbadger.documents.Field +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE + +object ExistsFunctions: + + def byIdMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertTrue(db.conn.existsById(TEST_TABLE, "three"), "The document with ID \"three\" should exist") + + def byIdNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsById(TEST_TABLE, "seven"), "The document with ID \"seven\" should not exist") + + def byFieldsMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertTrue(db.conn.existsByFields(TEST_TABLE, Field.equal("numValue", 10) :: Nil), + "Matching documents should have been found") + + def byFieldsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsByFields(TEST_TABLE, Field.equal("nothing", "none") :: Nil), + "No matching documents should have been found") + + def byContainsMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertTrue(db.conn.existsByContains(TEST_TABLE, Map.Map1("value", "purple")), + "Matching documents should have been found") + + def byContainsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsByContains(TEST_TABLE, Map.Map1("value", "violet")), + "Matching documents should not have been found") + + def byJsonPathMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertTrue(db.conn.existsByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10)"), + "Matching documents should have been found") + + def byJsonPathNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsByJsonPath(TEST_TABLE, "$.numValue ? (@ == 10.1)"), + "Matching documents should not have been found") diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/FindFunctions.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/FindFunctions.scala new file mode 100644 index 0000000..a5680a1 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/FindFunctions.scala @@ -0,0 +1,217 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.Assertions.* +import solutions.bitbadger.documents.{Configuration, Field} +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE + +import scala.jdk.CollectionConverters.* + +object FindFunctions: + + /** Generate IDs as a pipe-delimited string */ + private def docIds(docs: List[JsonDocument]) = + docs.map(_.id).reduce((ids, docId) => s"$ids|$docId") + + def allDefault(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(5, db.conn.findAll[JsonDocument](TEST_TABLE).size, "There should have been 5 documents returned") + + def allAscending(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val docs = db.conn.findAll[JsonDocument](TEST_TABLE, Field.named("id") :: Nil) + assertEquals(5, docs.size, "There should have been 5 documents returned") + assertEquals("five|four|one|three|two", docIds(docs), "The documents were not ordered correctly") + + def allDescending(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val docs = db.conn.findAll[JsonDocument](TEST_TABLE, Field.named("id DESC") :: Nil) + assertEquals(5, docs.size, "There should have been 5 documents returned") + assertEquals("two|three|one|four|five", docIds(docs), "The documents were not ordered correctly") + + def allNumOrder(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val docs = db.conn.findAll[JsonDocument](TEST_TABLE, + Field.named("sub.foo NULLS LAST") :: Field.named("n:numValue") :: Nil) + assertEquals(5, docs.size, "There should have been 5 documents returned") + assertEquals("two|four|one|three|five", docIds(docs), "The documents were not ordered correctly") + + def allEmpty(db: ThrowawayDatabase): Unit = + assertEquals(0, db.conn.findAll[JsonDocument](TEST_TABLE).size, "There should have been no documents returned") + + def byIdString(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val doc = db.conn.findById[String, JsonDocument](TEST_TABLE, "two") + assertTrue(doc.isDefined, "The document should have been returned") + assertEquals("two", doc.get.id, "An incorrect document was returned") + + def byIdNumber(db: ThrowawayDatabase): Unit = + Configuration.idField = "key" + try { + db.conn.insert(TEST_TABLE, NumIdDocument(18, "howdy")) + val doc = db.conn.findById[Int, NumIdDocument](TEST_TABLE, 18) + assertTrue(doc.isDefined, "The document should have been returned") + } finally { + Configuration.idField = "id" + } + + def byIdNotFound(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.findById[String, JsonDocument](TEST_TABLE, "x").isDefined, + "There should have been no document returned") + + def byFieldsMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val docs = db.conn.findByFields[JsonDocument](TEST_TABLE, + Field.any("value", ("blue" :: "purple" :: Nil).asJava) :: Nil, orderBy = Field.exists("sub") :: Nil) + assertEquals(1, docs.size, "There should have been a document returned") + assertEquals("four", docs.head.id, "The incorrect document was returned") + + def byFieldsMatchOrdered(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val docs = db.conn.findByFields[JsonDocument](TEST_TABLE, Field.equal("value", "purple") :: Nil, + orderBy = Field.named("id") :: Nil) + assertEquals(2, docs.size, "There should have been 2 documents returned") + assertEquals("five|four", docIds(docs), "The documents were not ordered correctly") + + def byFieldsMatchNumIn(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val docs = db.conn.findByFields[JsonDocument](TEST_TABLE, + Field.any("numValue", (2 :: 4 :: 6 :: 8 :: Nil).asJava) :: Nil) + assertEquals(1, docs.size, "There should have been a document returned") + assertEquals("three", docs.head.id, "The incorrect document was returned") + + def byFieldsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(0, db.conn.findByFields[JsonDocument](TEST_TABLE, Field.greater("numValue", 100) :: Nil).size, + "There should have been no documents returned") + + def byFieldsMatchInArray(db: ThrowawayDatabase): Unit = + ArrayDocument.testDocuments.foreach { doc => db.conn.insert(TEST_TABLE, doc) } + val docs = db.conn.findByFields[ArrayDocument](TEST_TABLE, + Field.inArray("values", TEST_TABLE, ("c" :: Nil).asJava) :: Nil) + assertEquals(2, docs.size, "There should have been two documents returned") + assertTrue(("first" :: "second" :: Nil).contains(docs.head.id), + s"An incorrect document was returned (${docs.head.id}") + assertTrue(("first" :: "second" :: Nil).contains(docs(1).id), s"An incorrect document was returned (${docs(1).id})") + + def byFieldsNoMatchInArray(db: ThrowawayDatabase): Unit = + ArrayDocument.testDocuments.foreach { doc => db.conn.insert(TEST_TABLE, doc) } + assertEquals(0, + db.conn.findByFields[ArrayDocument](TEST_TABLE, + Field.inArray("values", TEST_TABLE, ("j" :: Nil).asJava) :: Nil).size, + "There should have been no documents returned") + + def byContainsMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val docs = db.conn.findByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE, Map.Map1("value", "purple")) + assertEquals(2, docs.size, "There should have been 2 documents returned") + assertTrue(("four" :: "five" :: Nil).contains(docs.head.id), + s"An incorrect document was returned (${docs.head.id})") + assertTrue(("four" :: "five" :: Nil).contains(docs(1).id), s"An incorrect document was returned (${docs(1).id})") + + def byContainsMatchOrdered(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val docs = db.conn.findByContains[JsonDocument, Map.Map1[String, Map.Map1[String, String]]](TEST_TABLE, + Map.Map1("sub", Map.Map1("foo", "green")), Field.named("value") :: Nil) + assertEquals(2, docs.size, "There should have been 2 documents returned") + assertEquals("two|four", docIds(docs), "The documents were not ordered correctly") + + def byContainsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(0, + db.conn.findByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE, Map.Map1("value", "indigo")).size, + "There should have been no documents returned") + + def byJsonPathMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val docs = db.conn.findByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 10)") + assertEquals(2, docs.size, "There should have been 2 documents returned") + assertTrue(("four" :: "five" :: Nil).contains(docs.head.id), + s"An incorrect document was returned (${docs.head.id})") + assertTrue(("four" :: "five" :: Nil).contains(docs(1).id), s"An incorrect document was returned (${docs(1).id})") + + def byJsonPathMatchOrdered(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val docs = db.conn.findByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 10)", Field.named("id") :: Nil) + assertEquals(2, docs.size, "There should have been 2 documents returned") + assertEquals("five|four", docIds(docs), "The documents were not ordered correctly") + + def byJsonPathNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertEquals(0, db.conn.findByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 100)").size, + "There should have been no documents returned") + + def firstByFieldsMatchOne(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val doc = db.conn.findFirstByFields[JsonDocument](TEST_TABLE, Field.equal("value", "another") :: Nil) + assertTrue(doc.isDefined, "There should have been a document returned") + assertEquals("two", doc.get.id, "The incorrect document was returned") + + def firstByFieldsMatchMany(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val doc = db.conn.findFirstByFields[JsonDocument](TEST_TABLE, Field.equal("sub.foo", "green") :: Nil) + assertTrue(doc.isDefined, "There should have been a document returned") + assertTrue(("two" :: "four" :: Nil).contains(doc.get.id), s"An incorrect document was returned (${doc.get.id})") + + def firstByFieldsMatchOrdered(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val doc = db.conn.findFirstByFields[JsonDocument](TEST_TABLE, Field.equal("sub.foo", "green") :: Nil, + orderBy = Field.named("n:numValue DESC") :: Nil) + assertTrue(doc.isDefined, "There should have been a document returned") + assertEquals("four", doc.get.id, "An incorrect document was returned") + + def firstByFieldsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.findFirstByFields[JsonDocument](TEST_TABLE, Field.equal("value", "absent") :: Nil).isDefined, + "There should have been no document returned") + + def firstByContainsMatchOne(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val doc = db.conn.findFirstByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE, + Map.Map1("value", "FIRST!")) + assertTrue(doc.isDefined, "There should have been a document returned") + assertEquals("one", doc.get.id, "An incorrect document was returned") + + def firstByContainsMatchMany(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val doc = db.conn.findFirstByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE, + Map.Map1("value", "purple")) + assertTrue(doc.isDefined, "There should have been a document returned") + assertTrue(("four" :: "five" :: Nil).contains(doc.get.id), s"An incorrect document was returned (${doc.get.id})") + + def firstByContainsMatchOrdered(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val doc = db.conn.findFirstByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE, + Map.Map1("value", "purple"), Field.named("sub.bar NULLS FIRST") :: Nil) + assertTrue(doc.isDefined, "There should have been a document returned") + assertEquals("five", doc.get.id, "An incorrect document was returned") + + def firstByContainsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.findFirstByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE, + Map.Map1("value", "indigo")).isDefined, "There should have been no document returned") + + def firstByJsonPathMatchOne(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val doc = db.conn.findFirstByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ == 10)") + assertTrue(doc.isDefined, "There should have been a document returned") + assertEquals("two", doc.get.id, "An incorrect document was returned") + + def firstByJsonPathMatchMany(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val doc = db.conn.findFirstByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 10)") + assertTrue(doc.isDefined, "There should have been a document returned") + assertTrue(("four" :: "five" :: Nil).contains(doc.get.id), s"An incorrect document was returned (${doc.get.id})") + + def firstByJsonPathMatchOrdered(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val doc = db.conn.findFirstByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 10)", + Field.named("id DESC") :: Nil) + assertTrue(doc.isDefined, "There should have been a document returned") + assertEquals("four", doc.get.id, "An incorrect document was returned") + + def firstByJsonPathNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.findFirstByJsonPath[JsonDocument](TEST_TABLE, "$.numValue ? (@ > 100)").isDefined, + "There should have been no document returned") diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/JacksonDocumentSerializer.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/JacksonDocumentSerializer.scala new file mode 100644 index 0000000..6cac9df --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/JacksonDocumentSerializer.scala @@ -0,0 +1,17 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import solutions.bitbadger.documents.DocumentSerializer +import com.fasterxml.jackson.databind.ObjectMapper + +/** + * A JSON serializer using Jackson's default options + */ +class JacksonDocumentSerializer extends DocumentSerializer: + + private val mapper = ObjectMapper() + + override def serialize[TDoc](document: TDoc): String = + mapper.writeValueAsString(document) + + override def deserialize[TDoc](json: String, clazz: Class[TDoc]): TDoc = + mapper.readValue(json, clazz) diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/JsonDocument.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/JsonDocument.scala similarity index 64% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/JsonDocument.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/JsonDocument.scala index 861eeab..899409d 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/JsonDocument.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/JsonDocument.scala @@ -1,8 +1,7 @@ -package solutions.bitbadger.documents.scala.support +package solutions.bitbadger.documents.scala.tests.integration -import solutions.bitbadger.documents.jvm.Document -import solutions.bitbadger.documents.support.ThrowawayDatabase -import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE +import solutions.bitbadger.documents.scala.extensions.insert +import solutions.bitbadger.documents.scala.tests.TEST_TABLE class JsonDocument(val id: String = "", val value: String = "", val numValue: Int = 0, val sub: SubDocument = null) @@ -17,4 +16,4 @@ object JsonDocument: JsonDocument("five", "purple", 18, null)) def load(db: ThrowawayDatabase, tableName: String = TEST_TABLE): Unit = - testDocuments.foreach { it => Document.insert(tableName, it, db.getConn) } + testDocuments.foreach { it => db.conn.insert(tableName, it) } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/NumIdDocument.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/NumIdDocument.scala new file mode 100644 index 0000000..e426246 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/NumIdDocument.scala @@ -0,0 +1,3 @@ +package solutions.bitbadger.documents.scala.tests.integration + +class NumIdDocument(val key: Int = 0, val text: String = "") diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PatchFunctions.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PatchFunctions.scala new file mode 100644 index 0000000..b999c30 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PatchFunctions.scala @@ -0,0 +1,65 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.Assertions.* +import solutions.bitbadger.documents.Field +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE + +object PatchFunctions: + + def byIdMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + db.conn.patchById(TEST_TABLE, "one", Map.Map1("numValue", 44)) + val doc = db.conn.findById[String, JsonDocument](TEST_TABLE, "one") + assertTrue(doc.isDefined, "There should have been a document returned") + assertEquals("one", doc.get.id, "An incorrect document was returned") + assertEquals(44, doc.get.numValue, "The document was not patched") + + def byIdNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsById(TEST_TABLE, "forty-seven"), "Document with ID \"forty-seven\" should not exist") + db.conn.patchById(TEST_TABLE, "forty-seven", Map.Map1("foo", "green")) // no exception = pass + + def byFieldsMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + db.conn.patchByFields(TEST_TABLE, Field.equal("value", "purple") :: Nil, Map.Map1("numValue", 77)) + assertEquals(2L, db.conn.countByFields(TEST_TABLE, Field.equal("numValue", 77) :: Nil), + "There should have been 2 documents with numeric value 77") + + def byFieldsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val fields = Field.equal("value", "burgundy") :: Nil + assertFalse(db.conn.existsByFields(TEST_TABLE, fields), "There should be no documents with value of \"burgundy\"") + db.conn.patchByFields(TEST_TABLE, fields, Map.Map1("foo", "green")) // no exception = pass + + def byContainsMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val contains = Map.Map1("value", "another") + db.conn.patchByContains(TEST_TABLE, contains, Map.Map1("numValue", 12)) + val doc = db.conn.findFirstByContains[JsonDocument, Map.Map1[String, String]](TEST_TABLE, contains) + assertTrue(doc.isDefined, "There should have been a document returned") + assertEquals("two", doc.get.id, "The incorrect document was returned") + assertEquals(12, doc.get.numValue, "The document was not updated") + + def byContainsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val contains = Map.Map1("value", "updated") + assertFalse(db.conn.existsByContains(TEST_TABLE, contains), "There should be no matching documents") + db.conn.patchByContains(TEST_TABLE, contains, Map.Map1("sub.foo", "green")) // no exception = pass + + def byJsonPathMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val path = "$.numValue ? (@ > 10)" + db.conn.patchByJsonPath(TEST_TABLE, path, Map.Map1("value", "blue")) + val docs = db.conn.findByJsonPath[JsonDocument](TEST_TABLE, path) + assertEquals(2, docs.size, "There should have been two documents returned") + docs.foreach { doc => + assertTrue(("four" :: "five" :: Nil).contains(doc.id), s"An incorrect document was returned (${doc.id})") + assertEquals("blue", doc.value, s"The value for ID ${doc.id} was incorrect") + } + + def byJsonPathNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val path = "$.numValue ? (@ > 100)" + assertFalse(db.conn.existsByJsonPath(TEST_TABLE, path), "There should be no documents with numeric values over 100") + db.conn.patchByJsonPath(TEST_TABLE, path, Map.Map1("value", "blue")) // no exception = pass diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PgDB.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PgDB.scala new file mode 100644 index 0000000..27c1e41 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PgDB.scala @@ -0,0 +1,45 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import solutions.bitbadger.documents.{Configuration, Parameter, ParameterType} +import solutions.bitbadger.documents.scala.Results +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE + +import java.sql.Connection +import scala.util.Using + +/** + * A wrapper for a throwaway PostgreSQL database + */ +class PgDB extends ThrowawayDatabase: + + Configuration.setConnectionString(PgDB.connString("postgres")) + Using(Configuration.dbConn()) { conn => conn.customNonQuery(s"CREATE DATABASE $dbName") } + + Configuration.setConnectionString(PgDB.connString(dbName)) + + override val conn: Connection = Configuration.dbConn() + + conn.ensureTable(TEST_TABLE) + + override def close(): Unit = + conn.close() + Configuration.setConnectionString(PgDB.connString("postgres")) + Using(Configuration.dbConn()) { conn => conn.customNonQuery(s"DROP DATABASE $dbName") } + Configuration.setConnectionString(null) + + override def dbObjectExists(name: String): Boolean = + conn.customScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = :name) AS it", + Parameter(":name", ParameterType.STRING, name) :: Nil, Results.toExists) + + +object PgDB: + + /** + * Create a connection string for the given database + * + * @param database The database to which the library should connect + * @return The connection string for the database + */ + private def connString(database: String): String = + s"jdbc:postgresql://localhost/$database?user=postgres&password=postgres" diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/postgresql/CountIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLCountIT.scala similarity index 81% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/postgresql/CountIT.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLCountIT.scala index 1533bde..fcb84e1 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/postgresql/CountIT.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLCountIT.scala @@ -1,13 +1,11 @@ -package solutions.bitbadger.documents.scala.jvm.integration.postgresql +package solutions.bitbadger.documents.scala.tests.integration import org.junit.jupiter.api.{DisplayName, Test} -import solutions.bitbadger.documents.jvm.integration.postgresql.PgDB -import solutions.bitbadger.documents.scala.jvm.integration.common.CountFunctions import scala.util.Using -@DisplayName("JVM | Scala | PostgreSQL: Count") -class CountIT { +@DisplayName("Scala | PostgreSQL: Count") +class PostgreSQLCountIT: @Test @DisplayName("all counts all documents") @@ -43,4 +41,3 @@ class CountIT { @DisplayName("byJsonPath counts documents when no matches are found") def byJsonPathNoMatch(): Unit = Using(PgDB()) { db => CountFunctions.byJsonPathNoMatch(db) } -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/postgresql/CustomIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLCustomIT.scala similarity index 79% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/postgresql/CustomIT.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLCustomIT.scala index 4e74f7d..8ad437b 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/postgresql/CustomIT.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLCustomIT.scala @@ -1,13 +1,11 @@ -package solutions.bitbadger.documents.scala.jvm.integration.postgresql +package solutions.bitbadger.documents.scala.tests.integration import org.junit.jupiter.api.{DisplayName, Test} -import solutions.bitbadger.documents.jvm.integration.postgresql.PgDB -import solutions.bitbadger.documents.scala.jvm.integration.common.CustomFunctions import scala.util.Using -@DisplayName("JVM | Scala | PostgreSQL: Custom") -class CustomIT { +@DisplayName("Scala | PostgreSQL: Custom") +class PostgreSQLCustomIT: @Test @DisplayName("list succeeds with empty list") @@ -43,4 +41,3 @@ class CustomIT { @DisplayName("scalar succeeds") def scalar(): Unit = Using(PgDB()) { db => CustomFunctions.scalar(db) } -} diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLDefinitionIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLDefinitionIT.scala new file mode 100644 index 0000000..abdf325 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLDefinitionIT.scala @@ -0,0 +1,31 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} + +import scala.util.Using + +/** + * PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions + */ +@DisplayName("Scala | PostgreSQL: Definition") +class PostgreSQLDefinitionIT: + + @Test + @DisplayName("ensureTable creates table and index") + def ensureTable(): Unit = + Using(PgDB()) { db => DefinitionFunctions.ensureTable(db) } + + @Test + @DisplayName("ensureFieldIndex creates an index") + def ensureFieldIndex(): Unit = + Using(PgDB()) { db => DefinitionFunctions.ensureFieldIndex(db) } + + @Test + @DisplayName("ensureDocumentIndex creates a full index") + def ensureDocumentIndexFull(): Unit = + Using(PgDB()) { db => DefinitionFunctions.ensureDocumentIndexFull(db) } + + @Test + @DisplayName("ensureDocumentIndex creates an optimized index") + def ensureDocumentIndexOptimized(): Unit = + Using(PgDB()) { db => DefinitionFunctions.ensureDocumentIndexOptimized(db) } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLDeleteIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLDeleteIT.scala new file mode 100644 index 0000000..df349b0 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLDeleteIT.scala @@ -0,0 +1,51 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} + +import scala.util.Using + +/** + * PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions + */ +@DisplayName("Scala | PostgreSQL: Delete") +class PostgreSQLDeleteIT: + + @Test + @DisplayName("byId deletes a matching ID") + def byIdMatch(): Unit = + Using(PgDB()) { db => DeleteFunctions.byIdMatch(db) } + + @Test + @DisplayName("byId succeeds when no ID matches") + def byIdNoMatch(): Unit = + Using(PgDB()) { db => DeleteFunctions.byIdNoMatch(db) } + + @Test + @DisplayName("byFields deletes matching documents") + def byFieldsMatch(): Unit = + Using(PgDB()) { db => DeleteFunctions.byFieldsMatch(db) } + + @Test + @DisplayName("byFields succeeds when no documents match") + def byFieldsNoMatch(): Unit = + Using(PgDB()) { db => DeleteFunctions.byFieldsNoMatch(db) } + + @Test + @DisplayName("byContains deletes matching documents") + def byContainsMatch(): Unit = + Using(PgDB()) { db => DeleteFunctions.byContainsMatch(db) } + + @Test + @DisplayName("byContains succeeds when no documents match") + def byContainsNoMatch(): Unit = + Using(PgDB()) { db => DeleteFunctions.byContainsNoMatch(db) } + + @Test + @DisplayName("byJsonPath deletes matching documents") + def byJsonPathMatch(): Unit = + Using(PgDB()) { db => DeleteFunctions.byJsonPathMatch(db) } + + @Test + @DisplayName("byJsonPath succeeds when no documents match") + def byJsonPathNoMatch(): Unit = + Using(PgDB()) { db => DeleteFunctions.byJsonPathNoMatch(db) } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLDocumentIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLDocumentIT.scala new file mode 100644 index 0000000..77ebe77 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLDocumentIT.scala @@ -0,0 +1,56 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} + +import scala.util.Using + +/** + * PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions + */ +@DisplayName("Scala | PostgreSQL: Document") +class PostgreSQLDocumentIT: + + @Test + @DisplayName("insert works with default values") + def insertDefault(): Unit = + Using(PgDB()) { db => DocumentFunctions.insertDefault(db) } + + @Test + @DisplayName("insert fails with duplicate key") + def insertDupe(): Unit = + Using(PgDB()) { db => DocumentFunctions.insertDupe(db) } + + @Test + @DisplayName("insert succeeds with numeric auto IDs") + def insertNumAutoId(): Unit = + Using(PgDB()) { db => DocumentFunctions.insertNumAutoId(db) } + + @Test + @DisplayName("insert succeeds with UUID auto ID") + def insertUUIDAutoId(): Unit = + Using(PgDB()) { db => DocumentFunctions.insertUUIDAutoId(db) } + + @Test + @DisplayName("insert succeeds with random string auto ID") + def insertStringAutoId(): Unit = + Using(PgDB()) { db => DocumentFunctions.insertStringAutoId(db) } + + @Test + @DisplayName("save updates an existing document") + def saveMatch(): Unit = + Using(PgDB()) { db => DocumentFunctions.saveMatch(db) } + + @Test + @DisplayName("save inserts a new document") + def saveNoMatch(): Unit = + Using(PgDB()) { db => DocumentFunctions.saveNoMatch(db) } + + @Test + @DisplayName("update replaces an existing document") + def updateMatch(): Unit = + Using(PgDB()) { db => DocumentFunctions.updateMatch(db) } + + @Test + @DisplayName("update succeeds when no document exists") + def updateNoMatch(): Unit = + Using(PgDB()) { db => DocumentFunctions.updateNoMatch(db) } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLExistsIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLExistsIT.scala new file mode 100644 index 0000000..ea04640 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLExistsIT.scala @@ -0,0 +1,51 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} + +import scala.util.Using + +/** + * PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions + */ +@DisplayName("Scala | PostgreSQL: Exists") +class PostgreSQLExistsIT: + + @Test + @DisplayName("byId returns true when a document matches the ID") + def byIdMatch(): Unit = + Using(PgDB()) { db => ExistsFunctions.byIdMatch(db) } + + @Test + @DisplayName("byId returns false when no document matches the ID") + def byIdNoMatch(): Unit = + Using(PgDB()) { db => ExistsFunctions.byIdNoMatch(db) } + + @Test + @DisplayName("byFields returns true when documents match") + def byFieldsMatch(): Unit = + Using(PgDB()) { db => ExistsFunctions.byFieldsMatch(db) } + + @Test + @DisplayName("byFields returns false when no documents match") + def byFieldsNoMatch(): Unit = + Using(PgDB()) { db => ExistsFunctions.byFieldsNoMatch(db) } + + @Test + @DisplayName("byContains returns true when documents match") + def byContainsMatch(): Unit = + Using(PgDB()) { db => ExistsFunctions.byContainsMatch(db) } + + @Test + @DisplayName("byContains returns false when no documents match") + def byContainsNoMatch(): Unit = + Using(PgDB()) { db => ExistsFunctions.byContainsNoMatch(db) } + + @Test + @DisplayName("byJsonPath returns true when documents match") + def byJsonPathMatch(): Unit = + Using(PgDB()) { db => ExistsFunctions.byJsonPathMatch(db) } + + @Test + @DisplayName("byJsonPath returns false when no documents match") + def byJsonPathNoMatch(): Unit = + Using(PgDB()) { db => ExistsFunctions.byJsonPathNoMatch(db) } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLFindIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLFindIT.scala new file mode 100644 index 0000000..7074ada --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLFindIT.scala @@ -0,0 +1,172 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} + +import scala.util.Using + +/** + * PostgreSQL integration tests for the `Find` object / `find*` connection extension functions + */ +@DisplayName("Scala | PostgreSQL: Find") +class PostgreSQLFindIT: + + @Test + @DisplayName("all retrieves all documents") + def allDefault(): Unit = + Using(PgDB()) { db => FindFunctions.allDefault(db) } + + @Test + @DisplayName("all sorts data ascending") + def allAscending(): Unit = + Using(PgDB()) { db => FindFunctions.allAscending(db) } + + @Test + @DisplayName("all sorts data descending") + def allDescending(): Unit = + Using(PgDB()) { db => FindFunctions.allDescending(db) } + + @Test + @DisplayName("all sorts data numerically") + def allNumOrder(): Unit = + Using(PgDB()) { db => FindFunctions.allNumOrder(db) } + + @Test + @DisplayName("all succeeds with an empty table") + def allEmpty(): Unit = + Using(PgDB()) { db => FindFunctions.allEmpty(db) } + + @Test + @DisplayName("byId retrieves a document via a string ID") + def byIdString(): Unit = + Using(PgDB()) { db => FindFunctions.byIdString(db) } + + @Test + @DisplayName("byId retrieves a document via a numeric ID") + def byIdNumber(): Unit = + Using(PgDB()) { db => FindFunctions.byIdNumber(db) } + + @Test + @DisplayName("byId returns null when a matching ID is not found") + def byIdNotFound(): Unit = + Using(PgDB()) { db => FindFunctions.byIdNotFound(db) } + + @Test + @DisplayName("byFields retrieves matching documents") + def byFieldsMatch(): Unit = + Using(PgDB()) { db => FindFunctions.byFieldsMatch(db) } + + @Test + @DisplayName("byFields retrieves ordered matching documents") + def byFieldsMatchOrdered(): Unit = + Using(PgDB()) { db => FindFunctions.byFieldsMatchOrdered(db) } + + @Test + @DisplayName("byFields retrieves matching documents with a numeric IN clause") + def byFieldsMatchNumIn(): Unit = + Using(PgDB()) { db => FindFunctions.byFieldsMatchNumIn(db) } + + @Test + @DisplayName("byFields succeeds when no documents match") + def byFieldsNoMatch(): Unit = + Using(PgDB()) { db => FindFunctions.byFieldsNoMatch(db) } + + @Test + @DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison") + def byFieldsMatchInArray(): Unit = + Using(PgDB()) { db => FindFunctions.byFieldsMatchInArray(db) } + + @Test + @DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison") + def byFieldsNoMatchInArray(): Unit = + Using(PgDB()) { db => FindFunctions.byFieldsNoMatchInArray(db) } + + @Test + @DisplayName("byContains retrieves matching documents") + def byContainsMatch(): Unit = + Using(PgDB()) { db => FindFunctions.byContainsMatch(db) } + + @Test + @DisplayName("byContains retrieves ordered matching documents") + def byContainsMatchOrdered(): Unit = + Using(PgDB()) { db => FindFunctions.byContainsMatchOrdered(db) } + + @Test + @DisplayName("byContains succeeds when no documents match") + def byContainsNoMatch(): Unit = + Using(PgDB()) { db => FindFunctions.byContainsNoMatch(db) } + + @Test + @DisplayName("byJsonPath retrieves matching documents") + def byJsonPathMatch(): Unit = + Using(PgDB()) { db => FindFunctions.byJsonPathMatch(db) } + + @Test + @DisplayName("byJsonPath retrieves ordered matching documents") + def byJsonPathMatchOrdered(): Unit = + Using(PgDB()) { db => FindFunctions.byJsonPathMatchOrdered(db) } + + @Test + @DisplayName("byJsonPath succeeds when no documents match") + def byJsonPathNoMatch(): Unit = + Using(PgDB()) { db => FindFunctions.byJsonPathNoMatch(db) } + + @Test + @DisplayName("firstByFields retrieves a matching document") + def firstByFieldsMatchOne(): Unit = + Using(PgDB()) { db => FindFunctions.firstByFieldsMatchOne(db) } + + @Test + @DisplayName("firstByFields retrieves a matching document among many") + def firstByFieldsMatchMany(): Unit = + Using(PgDB()) { db => FindFunctions.firstByFieldsMatchMany(db) } + + @Test + @DisplayName("firstByFields retrieves a matching document among many (ordered)") + def firstByFieldsMatchOrdered(): Unit = + Using(PgDB()) { db => FindFunctions.firstByFieldsMatchOrdered(db) } + + @Test + @DisplayName("firstByFields returns null when no document matches") + def firstByFieldsNoMatch(): Unit = + Using(PgDB()) { db => FindFunctions.firstByFieldsNoMatch(db) } + + @Test + @DisplayName("firstByContains retrieves a matching document") + def firstByContainsMatchOne(): Unit = + Using(PgDB()) { db => FindFunctions.firstByContainsMatchOne(db) } + + @Test + @DisplayName("firstByContains retrieves a matching document among many") + def firstByContainsMatchMany(): Unit = + Using(PgDB()) { db => FindFunctions.firstByContainsMatchMany(db) } + + @Test + @DisplayName("firstByContains retrieves a matching document among many (ordered)") + def firstByContainsMatchOrdered(): Unit = + Using(PgDB()) { db => FindFunctions.firstByContainsMatchOrdered(db) } + + @Test + @DisplayName("firstByContains returns null when no document matches") + def firstByContainsNoMatch(): Unit = + Using(PgDB()) { db => FindFunctions.firstByContainsNoMatch(db) } + + @Test + @DisplayName("firstByJsonPath retrieves a matching document") + def firstByJsonPathMatchOne(): Unit = + Using(PgDB()) { db => FindFunctions.firstByJsonPathMatchOne(db) } + + @Test + @DisplayName("firstByJsonPath retrieves a matching document among many") + def firstByJsonPathMatchMany(): Unit = + Using(PgDB()) { db => FindFunctions.firstByJsonPathMatchMany(db) } + + @Test + @DisplayName("firstByJsonPath retrieves a matching document among many (ordered)") + def firstByJsonPathMatchOrdered(): Unit = + Using(PgDB()) { db => FindFunctions.firstByJsonPathMatchOrdered(db) } + + @Test + @DisplayName("firstByJsonPath returns null when no document matches") + def firstByJsonPathNoMatch(): Unit = + Using(PgDB()) { db => FindFunctions.firstByJsonPathNoMatch(db) } + diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLPatchIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLPatchIT.scala new file mode 100644 index 0000000..06e9410 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLPatchIT.scala @@ -0,0 +1,51 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} + +import scala.util.Using + +/** + * PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions + */ +@DisplayName("Scala | PostgreSQL: Patch") +class PostgreSQLPatchIT: + + @Test + @DisplayName("byId patches an existing document") + def byIdMatch(): Unit = + Using(PgDB()) { db => PatchFunctions. byIdMatch(db) } + + @Test + @DisplayName("byId succeeds for a non-existent document") + def byIdNoMatch(): Unit = + Using(PgDB()) { db => PatchFunctions. byIdNoMatch(db) } + + @Test + @DisplayName("byFields patches matching document") + def byFieldsMatch(): Unit = + Using(PgDB()) { db => PatchFunctions. byFieldsMatch(db) } + + @Test + @DisplayName("byFields succeeds when no documents match") + def byFieldsNoMatch(): Unit = + Using(PgDB()) { db => PatchFunctions. byFieldsNoMatch(db) } + + @Test + @DisplayName("byContains patches matching document") + def byContainsMatch(): Unit = + Using(PgDB()) { db => PatchFunctions. byContainsMatch(db) } + + @Test + @DisplayName("byContains succeeds when no documents match") + def byContainsNoMatch(): Unit = + Using(PgDB()) { db => PatchFunctions. byContainsNoMatch(db) } + + @Test + @DisplayName("byJsonPath patches matching document") + def byJsonPathMatch(): Unit = + Using(PgDB()) { db => PatchFunctions. byJsonPathMatch(db) } + + @Test + @DisplayName("byJsonPath succeeds when no documents match") + def byJsonPathNoMatch(): Unit = + Using(PgDB()) { db => PatchFunctions. byJsonPathNoMatch(db) } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLRemoveFieldsIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLRemoveFieldsIT.scala new file mode 100644 index 0000000..ffbc6c2 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/PostgreSQLRemoveFieldsIT.scala @@ -0,0 +1,71 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} + +import scala.util.Using + +/** + * PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions + */ +@DisplayName("Scala | PostgreSQL: RemoveFields") +class PostgreSQLRemoveFieldsIT: + + @Test + @DisplayName("byId removes fields from an existing document") + def byIdMatchFields(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byIdMatchFields(db) } + + @Test + @DisplayName("byId succeeds when fields do not exist on an existing document") + def byIdMatchNoFields(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byIdMatchNoFields(db) } + + @Test + @DisplayName("byId succeeds when no document exists") + def byIdNoMatch(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byIdNoMatch(db) } + + @Test + @DisplayName("byFields removes fields from matching documents") + def byFieldsMatchFields(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byFieldsMatchFields(db) } + + @Test + @DisplayName("byFields succeeds when fields do not exist on matching documents") + def byFieldsMatchNoFields(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byFieldsMatchNoFields(db) } + + @Test + @DisplayName("byFields succeeds when no matching documents exist") + def byFieldsNoMatch(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byFieldsNoMatch(db) } + + @Test + @DisplayName("byContains removes fields from matching documents") + def byContainsMatchFields(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byContainsMatchFields(db) } + + @Test + @DisplayName("byContains succeeds when fields do not exist on matching documents") + def byContainsMatchNoFields(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byContainsMatchNoFields(db) } + + @Test + @DisplayName("byContains succeeds when no matching documents exist") + def byContainsNoMatch(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byContainsNoMatch(db) } + + @Test + @DisplayName("byJsonPath removes fields from matching documents") + def byJsonPathMatchFields(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byJsonPathMatchFields(db) } + + @Test + @DisplayName("byJsonPath succeeds when fields do not exist on matching documents") + def byJsonPathMatchNoFields(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byJsonPathMatchNoFields(db) } + + @Test + @DisplayName("byJsonPath succeeds when no matching documents exist") + def byJsonPathNoMatch(): Unit = + Using(PgDB()) { db => RemoveFieldsFunctions. byJsonPathNoMatch(db) } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/RemoveFieldsFunctions.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/RemoveFieldsFunctions.scala new file mode 100644 index 0000000..1531355 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/RemoveFieldsFunctions.scala @@ -0,0 +1,92 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.Assertions.* +import solutions.bitbadger.documents.Field +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE + +object RemoveFieldsFunctions: + + def byIdMatchFields(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + db.conn.removeFieldsById(TEST_TABLE, "two", "sub" :: "value" :: Nil) + val doc = db.conn.findById[String, JsonDocument](TEST_TABLE, "two") + assertTrue(doc.isDefined, "There should have been a document returned") + assertEquals("", doc.get.value, "The value should have been empty") + assertNull(doc.get.sub, "The sub-document should have been removed") + + def byIdMatchNoFields(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsByFields(TEST_TABLE, Field.exists("a_field_that_does_not_exist") :: Nil)) + db.conn.removeFieldsById(TEST_TABLE, "one", "a_field_that_does_not_exist" :: Nil) // no exception = pass + + def byIdNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsById(TEST_TABLE, "fifty")) + db.conn.removeFieldsById(TEST_TABLE, "fifty", "sub" :: Nil) // no exception = pass + + def byFieldsMatchFields(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val fields = Field.equal("numValue", 17) :: Nil + db.conn.removeFieldsByFields(TEST_TABLE, fields, "sub" :: Nil) + val doc = db.conn.findFirstByFields[JsonDocument](TEST_TABLE, fields) + assertTrue(doc.isDefined, "The document should have been returned") + assertEquals("four", doc.get.id, "An incorrect document was returned") + assertNull(doc.get.sub, "The sub-document should have been removed") + + def byFieldsMatchNoFields(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsByFields(TEST_TABLE, Field.exists("nada") :: Nil)) + db.conn.removeFieldsByFields(TEST_TABLE, Field.equal("numValue", 17) :: Nil, "nada" :: Nil) // no exn = pass + + def byFieldsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val fields = Field.notEqual("missing", "nope") :: Nil + assertFalse(db.conn.existsByFields(TEST_TABLE, fields)) + db.conn.removeFieldsByFields(TEST_TABLE, fields, "value" :: Nil) // no exception = pass + + def byContainsMatchFields(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val criteria = Map.Map1("sub", Map.Map1("foo", "green")) + db.conn.removeFieldsByContains(TEST_TABLE, criteria, "value" :: Nil) + val docs = db.conn.findByContains[JsonDocument, Map.Map1[String, Map.Map1[String, String]]](TEST_TABLE, criteria) + assertEquals(2, docs.size, "There should have been 2 documents returned") + docs.foreach { doc => + assertTrue(("two" :: "four" :: Nil).contains(doc.id), s"An incorrect document was returned (${doc.id})") + assertEquals("", doc.value, "The value should have been empty") + } + + def byContainsMatchNoFields(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsByFields(TEST_TABLE, Field.exists("invalid_field") :: Nil)) + db.conn.removeFieldsByContains(TEST_TABLE, Map.Map1("sub", Map.Map1("foo", "green")), "invalid_field" :: Nil) + // no exception = pass + + def byContainsNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val contains = Map.Map1("value", "substantial") + assertFalse(db.conn.existsByContains(TEST_TABLE, contains)) + db.conn.removeFieldsByContains(TEST_TABLE, contains, "numValue" :: Nil) + + def byJsonPathMatchFields(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val path = "$.value ? (@ == \"purple\")" + db.conn.removeFieldsByJsonPath(TEST_TABLE, path, "sub" :: Nil) + val docs = db.conn.findByJsonPath[JsonDocument](TEST_TABLE, path) + assertEquals(2, docs.size, "There should have been 2 documents returned") + docs.foreach { doc => + assertTrue(("four" :: "five" :: Nil).contains(doc.id), s"An incorrect document was returned (${doc.id})") + assertNull(doc.sub, "The sub-document should have been removed") + } + + def byJsonPathMatchNoFields(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + assertFalse(db.conn.existsByFields(TEST_TABLE, Field.exists("submarine") :: Nil)) + db.conn.removeFieldsByJsonPath(TEST_TABLE, "$.value ? (@ == \"purple\")", "submarine" :: Nil) // no exn = pass + + def byJsonPathNoMatch(db: ThrowawayDatabase): Unit = + JsonDocument.load(db) + val path = "$.value ? (@ == \"mauve\")" + assertFalse(db.conn.existsByJsonPath(TEST_TABLE, path)) + db.conn.removeFieldsByJsonPath(TEST_TABLE, path, "value" :: Nil) // no exception = pass + \ No newline at end of file diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/sqlite/CountIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteCountIT.scala similarity index 78% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/sqlite/CountIT.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteCountIT.scala index c77f453..eba322d 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/sqlite/CountIT.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteCountIT.scala @@ -1,15 +1,13 @@ -package solutions.bitbadger.documents.scala.jvm.integration.sqlite +package solutions.bitbadger.documents.scala.tests.integration -import org.junit.jupiter.api.{DisplayName, Test} -import solutions.bitbadger.documents.jvm.integration.sqlite.SQLiteDB -import solutions.bitbadger.documents.scala.jvm.integration.common.CountFunctions import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.{DisplayName, Test} import solutions.bitbadger.documents.DocumentException import scala.util.Using -@DisplayName("JVM | Scala | SQLite: Count") -class CountIT { +@DisplayName("Scala | SQLite: Count") +class SQLiteCountIT: @Test @DisplayName("all counts all documents") @@ -35,4 +33,3 @@ class CountIT { @DisplayName("byJsonPath fails") def byJsonPathMatch(): Unit = Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => CountFunctions.byJsonPathMatch(db)) } -} diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/sqlite/CustomIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteCustomIT.scala similarity index 80% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/sqlite/CustomIT.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteCustomIT.scala index 36c08ab..f393a1e 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/jvm/integration/sqlite/CustomIT.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteCustomIT.scala @@ -1,13 +1,11 @@ -package solutions.bitbadger.documents.scala.jvm.integration.sqlite +package solutions.bitbadger.documents.scala.tests.integration import org.junit.jupiter.api.{DisplayName, Test} -import solutions.bitbadger.documents.jvm.integration.sqlite.SQLiteDB -import solutions.bitbadger.documents.scala.jvm.integration.common.CustomFunctions import scala.util.Using -@DisplayName("JVM | Scala | SQLite: Custom") -class CustomIT { +@DisplayName("Scala | SQLite: Custom") +class SQLiteCustomIT: @Test @DisplayName("list succeeds with empty list") @@ -43,4 +41,3 @@ class CustomIT { @DisplayName("scalar succeeds") def scalar(): Unit = Using(SQLiteDB()) { db => CustomFunctions.scalar(db) } -} diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDB.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDB.scala new file mode 100644 index 0000000..5a90a0e --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDB.scala @@ -0,0 +1,30 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import solutions.bitbadger.documents.{Configuration, Parameter, ParameterType} +import solutions.bitbadger.documents.scala.Results +import solutions.bitbadger.documents.scala.extensions.* +import solutions.bitbadger.documents.scala.tests.TEST_TABLE + +import java.io.File +import java.sql.Connection + +/** + * A wrapper for a throwaway SQLite database + */ +class SQLiteDB extends ThrowawayDatabase: + + Configuration.setConnectionString(s"jdbc:sqlite:$dbName.db") + + override val conn: Connection = Configuration.dbConn() + + conn.ensureTable(TEST_TABLE) + + override def close(): Unit = + conn.close() + File(s"$dbName.db").delete() + Configuration.setConnectionString(null) + + override def dbObjectExists(name: String): Boolean = + conn.customScalar[Boolean]("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE name = :name) AS it", + Parameter(":name", ParameterType.STRING, name) :: Nil, Results.toExists) + diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDefinitionIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDefinitionIT.scala new file mode 100644 index 0000000..051d820 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDefinitionIT.scala @@ -0,0 +1,30 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} +import org.junit.jupiter.api.Assertions.assertThrows +import solutions.bitbadger.documents.DocumentException + +import scala.util.Using + +/** + * SQLite integration tests for the `Definition` object / `ensure*` connection extension functions + */ +@DisplayName("Scala | SQLite: Definition") +class SQLiteDefinitionIT: + + @Test + @DisplayName("ensureTable creates table and index") + def ensureTable(): Unit = + Using(SQLiteDB()) { db => DefinitionFunctions.ensureTable(db) } + + @Test + @DisplayName("ensureFieldIndex creates an index") + def ensureFieldIndex(): Unit = + Using(SQLiteDB()) { db => DefinitionFunctions.ensureFieldIndex(db) } + + @Test + @DisplayName("ensureDocumentIndex creates a full index") + def ensureDocumentIndexFull(): Unit = + Using(SQLiteDB()) { db => + assertThrows(classOf[DocumentException], () => DefinitionFunctions.ensureDocumentIndexFull(db)) + } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDeleteIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDeleteIT.scala new file mode 100644 index 0000000..35c9c91 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDeleteIT.scala @@ -0,0 +1,43 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} +import org.junit.jupiter.api.Assertions.assertThrows +import solutions.bitbadger.documents.DocumentException + +import scala.util.Using + +/** + * SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions + */ +@DisplayName("Scala | SQLite: Delete") +class SQLiteDeleteIT: + + @Test + @DisplayName("byId deletes a matching ID") + def byIdMatch(): Unit = + Using(SQLiteDB()) { db => DeleteFunctions.byIdMatch(db) } + + @Test + @DisplayName("byId succeeds when no ID matches") + def byIdNoMatch(): Unit = + Using(SQLiteDB()) { db => DeleteFunctions.byIdNoMatch(db) } + + @Test + @DisplayName("byFields deletes matching documents") + def byFieldsMatch(): Unit = + Using(SQLiteDB()) { db => DeleteFunctions.byFieldsMatch(db) } + + @Test + @DisplayName("byFields succeeds when no documents match") + def byFieldsNoMatch(): Unit = + Using(SQLiteDB()) { db => DeleteFunctions.byFieldsNoMatch(db) } + + @Test + @DisplayName("byContains fails") + def byContainsFails(): Unit = + Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => DeleteFunctions.byContainsMatch(db)) } + + @Test + @DisplayName("byJsonPath fails") + def byJsonPathFails(): Unit = + Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => DeleteFunctions.byJsonPathMatch(db)) } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDocumentIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDocumentIT.scala new file mode 100644 index 0000000..9c3040a --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteDocumentIT.scala @@ -0,0 +1,56 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} + +import scala.util.Using + +/** + * SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions + */ +@DisplayName("Scala | SQLite: Document") +class SQLiteDocumentIT: + + @Test + @DisplayName("insert works with default values") + def insertDefault(): Unit = + Using(SQLiteDB()) { db => DocumentFunctions.insertDefault(db) } + + @Test + @DisplayName("insert fails with duplicate key") + def insertDupe(): Unit = + Using(SQLiteDB()) { db => DocumentFunctions.insertDupe(db) } + + @Test + @DisplayName("insert succeeds with numeric auto IDs") + def insertNumAutoId(): Unit = + Using(SQLiteDB()) { db => DocumentFunctions.insertNumAutoId(db) } + + @Test + @DisplayName("insert succeeds with UUID auto ID") + def insertUUIDAutoId(): Unit = + Using(SQLiteDB()) { db => DocumentFunctions.insertUUIDAutoId(db) } + + @Test + @DisplayName("insert succeeds with random string auto ID") + def insertStringAutoId(): Unit = + Using(SQLiteDB()) { db => DocumentFunctions.insertStringAutoId(db) } + + @Test + @DisplayName("save updates an existing document") + def saveMatch(): Unit = + Using(SQLiteDB()) { db => DocumentFunctions.saveMatch(db) } + + @Test + @DisplayName("save inserts a new document") + def saveNoMatch(): Unit = + Using(SQLiteDB()) { db => DocumentFunctions.saveNoMatch(db) } + + @Test + @DisplayName("update replaces an existing document") + def updateMatch(): Unit = + Using(SQLiteDB()) { db => DocumentFunctions.updateMatch(db) } + + @Test + @DisplayName("update succeeds when no document exists") + def updateNoMatch(): Unit = + Using(SQLiteDB()) { db => DocumentFunctions.updateNoMatch(db) } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteExistsIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteExistsIT.scala new file mode 100644 index 0000000..86636b4 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteExistsIT.scala @@ -0,0 +1,43 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} +import org.junit.jupiter.api.Assertions.assertThrows +import solutions.bitbadger.documents.DocumentException + +import scala.util.Using + +/** + * SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions + */ +@DisplayName("Scala | SQLite: Exists") +class SQLiteExistsIT: + + @Test + @DisplayName("byId returns true when a document matches the ID") + def byIdMatch(): Unit = + Using(SQLiteDB()) { db => ExistsFunctions.byIdMatch(db) } + + @Test + @DisplayName("byId returns false when no document matches the ID") + def byIdNoMatch(): Unit = + Using(SQLiteDB()) { db => ExistsFunctions.byIdNoMatch(db) } + + @Test + @DisplayName("byFields returns true when documents match") + def byFieldsMatch(): Unit = + Using(SQLiteDB()) { db => ExistsFunctions.byFieldsMatch(db) } + + @Test + @DisplayName("byFields returns false when no documents match") + def byFieldsNoMatch(): Unit = + Using(SQLiteDB()) { db => ExistsFunctions.byFieldsNoMatch(db) } + + @Test + @DisplayName("byContains fails") + def byContainsFails(): Unit = + Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => ExistsFunctions.byContainsMatch(db)) } + + @Test + @DisplayName("byJsonPath fails") + def byJsonPathFails(): Unit = + Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => ExistsFunctions.byJsonPathMatch(db)) } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteFindIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteFindIT.scala new file mode 100644 index 0000000..c99bcdf --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteFindIT.scala @@ -0,0 +1,127 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.{DisplayName, Test} +import solutions.bitbadger.documents.DocumentException + +import scala.util.Using + +/** + * SQLite integration tests for the `Find` object / `find*` connection extension functions + */ +@DisplayName("Scala | SQLite: Find") +class SQLiteFindIT: + + @Test + @DisplayName("all retrieves all documents") + def allDefault(): Unit = + Using(SQLiteDB()) { db => FindFunctions.allDefault(db) } + + @Test + @DisplayName("all sorts data ascending") + def allAscending(): Unit = + Using(SQLiteDB()) { db => FindFunctions.allAscending(db) } + + @Test + @DisplayName("all sorts data descending") + def allDescending(): Unit = + Using(SQLiteDB()) { db => FindFunctions.allDescending(db) } + + @Test + @DisplayName("all sorts data numerically") + def allNumOrder(): Unit = + Using(SQLiteDB()) { db => FindFunctions.allNumOrder(db) } + + @Test + @DisplayName("all succeeds with an empty table") + def allEmpty(): Unit = + Using(SQLiteDB()) { db => FindFunctions.allEmpty(db) } + + @Test + @DisplayName("byId retrieves a document via a string ID") + def byIdString(): Unit = + Using(SQLiteDB()) { db => FindFunctions.byIdString(db) } + + @Test + @DisplayName("byId retrieves a document via a numeric ID") + def byIdNumber(): Unit = + Using(SQLiteDB()) { db => FindFunctions.byIdNumber(db) } + + @Test + @DisplayName("byId returns null when a matching ID is not found") + def byIdNotFound(): Unit = + Using(SQLiteDB()) { db => FindFunctions.byIdNotFound(db) } + + @Test + @DisplayName("byFields retrieves matching documents") + def byFieldsMatch(): Unit = + Using(SQLiteDB()) { db => FindFunctions.byFieldsMatch(db) } + + @Test + @DisplayName("byFields retrieves ordered matching documents") + def byFieldsMatchOrdered(): Unit = + Using(SQLiteDB()) { db => FindFunctions.byFieldsMatchOrdered(db) } + + @Test + @DisplayName("byFields retrieves matching documents with a numeric IN clause") + def byFieldsMatchNumIn(): Unit = + Using(SQLiteDB()) { db => FindFunctions.byFieldsMatchNumIn(db) } + + @Test + @DisplayName("byFields succeeds when no documents match") + def byFieldsNoMatch(): Unit = + Using(SQLiteDB()) { db => FindFunctions.byFieldsNoMatch(db) } + + @Test + @DisplayName("byFields retrieves matching documents with an IN_ARRAY comparison") + def byFieldsMatchInArray(): Unit = + Using(SQLiteDB()) { db => FindFunctions.byFieldsMatchInArray(db) } + + @Test + @DisplayName("byFields succeeds when no documents match an IN_ARRAY comparison") + def byFieldsNoMatchInArray(): Unit = + Using(SQLiteDB()) { db => FindFunctions.byFieldsNoMatchInArray(db) } + + @Test + @DisplayName("byContains fails") + def byContainsFails(): Unit = + Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => FindFunctions.byContainsMatch(db)) } + + @Test + @DisplayName("byJsonPath fails") + def byJsonPathFails(): Unit = + Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => FindFunctions.byJsonPathMatch(db)) } + + @Test + @DisplayName("firstByFields retrieves a matching document") + def firstByFieldsMatchOne(): Unit = + Using(SQLiteDB()) { db => FindFunctions.firstByFieldsMatchOne(db) } + + @Test + @DisplayName("firstByFields retrieves a matching document among many") + def firstByFieldsMatchMany(): Unit = + Using(SQLiteDB()) { db => FindFunctions.firstByFieldsMatchMany(db) } + + @Test + @DisplayName("firstByFields retrieves a matching document among many (ordered)") + def firstByFieldsMatchOrdered(): Unit = + Using(SQLiteDB()) { db => FindFunctions.firstByFieldsMatchOrdered(db) } + + @Test + @DisplayName("firstByFields returns null when no document matches") + def firstByFieldsNoMatch(): Unit = + Using(SQLiteDB()) { db => FindFunctions.firstByFieldsNoMatch(db) } + + @Test + @DisplayName("firstByContains fails") + def firstByContainsFails(): Unit = + Using(SQLiteDB()) { db => + assertThrows(classOf[DocumentException], () => FindFunctions.firstByContainsMatchOne(db)) + } + + @Test + @DisplayName("firstByJsonPath fails") + def firstByJsonPathFails(): Unit = + Using(SQLiteDB()) { db => + assertThrows(classOf[DocumentException], () => FindFunctions.firstByJsonPathMatchOne(db)) + } diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLitePatchIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLitePatchIT.scala new file mode 100644 index 0000000..fb77238 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLitePatchIT.scala @@ -0,0 +1,44 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} +import org.junit.jupiter.api.Assertions.assertThrows +import solutions.bitbadger.documents.DocumentException + +import scala.util.Using + +/** + * SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions + */ +@DisplayName("Scala | SQLite: Patch") +class SQLitePatchIT: + + @Test + @DisplayName("byId patches an existing document") + def byIdMatch(): Unit = + Using(SQLiteDB()) { db => PatchFunctions.byIdMatch(db) } + + @Test + @DisplayName("byId succeeds for a non-existent document") + def byIdNoMatch(): Unit = + Using(SQLiteDB()) { db => PatchFunctions.byIdNoMatch(db) } + + @Test + @DisplayName("byFields patches matching document") + def byFieldsMatch(): Unit = + Using(SQLiteDB()) { db => PatchFunctions.byFieldsMatch(db) } + + @Test + @DisplayName("byFields succeeds when no documents match") + def byFieldsNoMatch(): Unit = + Using(SQLiteDB()) { db => PatchFunctions.byFieldsNoMatch(db) } + + @Test + @DisplayName("byContains fails") + def byContainsFails(): Unit = + Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => PatchFunctions.byContainsMatch(db)) } + + @Test + @DisplayName("byJsonPath fails") + def byJsonPathFails(): Unit = + Using(SQLiteDB()) { db => assertThrows(classOf[DocumentException], () => PatchFunctions.byJsonPathMatch(db)) } + diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteRemoveFieldsIT.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteRemoveFieldsIT.scala new file mode 100644 index 0000000..0d8a679 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SQLiteRemoveFieldsIT.scala @@ -0,0 +1,57 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import org.junit.jupiter.api.{DisplayName, Test} +import org.junit.jupiter.api.Assertions.assertThrows +import solutions.bitbadger.documents.DocumentException + +import scala.util.Using + +/** + * SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions + */ +@DisplayName("Scala | SQLite: RemoveFields") +class SQLiteRemoveFieldsIT: + + @Test + @DisplayName("byId removes fields from an existing document") + def byIdMatchFields(): Unit = + Using(SQLiteDB()) { db => RemoveFieldsFunctions.byIdMatchFields(db) } + + @Test + @DisplayName("byId succeeds when fields do not exist on an existing document") + def byIdMatchNoFields(): Unit = + Using(SQLiteDB()) { db => RemoveFieldsFunctions.byIdMatchNoFields(db) } + + @Test + @DisplayName("byId succeeds when no document exists") + def byIdNoMatch(): Unit = + Using(SQLiteDB()) { db => RemoveFieldsFunctions.byIdNoMatch(db) } + + @Test + @DisplayName("byFields removes fields from matching documents") + def byFieldsMatchFields(): Unit = + Using(SQLiteDB()) { db => RemoveFieldsFunctions.byFieldsMatchFields(db) } + + @Test + @DisplayName("byFields succeeds when fields do not exist on matching documents") + def byFieldsMatchNoFields(): Unit = + Using(SQLiteDB()) { db => RemoveFieldsFunctions.byFieldsMatchNoFields(db) } + + @Test + @DisplayName("byFields succeeds when no matching documents exist") + def byFieldsNoMatch(): Unit = + Using(SQLiteDB()) { db => RemoveFieldsFunctions.byFieldsNoMatch(db) } + + @Test + @DisplayName("byContains fails") + def byContainsFails(): Unit = + Using(SQLiteDB()) { db => + assertThrows(classOf[DocumentException], () => RemoveFieldsFunctions.byContainsMatchFields(db)) + } + + @Test + @DisplayName("byJsonPath fails") + def byJsonPathFails(): Unit = + Using(SQLiteDB()) { db => + assertThrows(classOf[DocumentException], () => RemoveFieldsFunctions.byJsonPathMatchFields(db)) + } diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/SubDocument.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SubDocument.scala similarity index 50% rename from src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/SubDocument.scala rename to src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SubDocument.scala index 9ec17d5..66f65b5 100644 --- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/SubDocument.scala +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/SubDocument.scala @@ -1,3 +1,3 @@ -package solutions.bitbadger.documents.scala.support +package solutions.bitbadger.documents.scala.tests.integration class SubDocument(val foo: String = "", val bar: String = "") diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/ThrowawayDatabase.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/ThrowawayDatabase.scala new file mode 100644 index 0000000..4d12442 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/integration/ThrowawayDatabase.scala @@ -0,0 +1,28 @@ +package solutions.bitbadger.documents.scala.tests.integration + +import solutions.bitbadger.documents.AutoId +import solutions.bitbadger.documents.java.DocumentConfig + +import java.sql.Connection + +/** + * Common trait for PostgreSQL and SQLite throwaway databases + */ +trait ThrowawayDatabase extends AutoCloseable: + + /** The database connection for the throwaway database */ + def conn: Connection + + /** + * Determine if a database object exists + * + * @param name The name of the object whose existence should be checked + * @return True if the object exists, false if not + */ + def dbObjectExists(name: String): Boolean + + /** The name for the throwaway database */ + val dbName = s"throwaway_${AutoId.generateRandomString(8)}" + + // Use a Jackson-based document serializer for testing + DocumentConfig.setSerializer(JacksonDocumentSerializer()) diff --git a/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/package.scala b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/package.scala new file mode 100644 index 0000000..8992837 --- /dev/null +++ b/src/scala/src/test/scala/solutions/bitbadger/documents/scala/tests/package.scala @@ -0,0 +1,6 @@ +package solutions.bitbadger.documents.scala + +package object tests { + + def TEST_TABLE = "test_table" +} \ No newline at end of file