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