diff --git a/.idea/libraries/Maven__scala_sdk_3_1_3.xml b/.idea/libraries/Maven__scala_sdk_3_1_3.xml
new file mode 100644
index 0000000..17f32de
--- /dev/null
+++ b/.idea/libraries/Maven__scala_sdk_3_1_3.xml
@@ -0,0 +1,26 @@
+
+
+
+ Scala_3_1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-sbt-bridge/3.1.3/scala3-sbt-bridge-3.1.3.jar
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__scala_sdk_3_3_3.xml b/.idea/libraries/Maven__scala_sdk_3_3_3.xml
new file mode 100644
index 0000000..a753719
--- /dev/null
+++ b/.idea/libraries/Maven__scala_sdk_3_3_3.xml
@@ -0,0 +1,25 @@
+
+
+
+ Scala_3_3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-sbt-bridge/3.3.3/scala3-sbt-bridge-3.3.3.jar
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__scala_sdk_3_5_2.xml b/.idea/libraries/Maven__scala_sdk_3_5_2.xml
new file mode 100644
index 0000000..edaffc7
--- /dev/null
+++ b/.idea/libraries/Maven__scala_sdk_3_5_2.xml
@@ -0,0 +1,26 @@
+
+
+
+ Scala_3_5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-sbt-bridge/3.5.2/scala3-sbt-bridge-3.5.2.jar
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/jvm/pom.xml b/src/jvm/pom.xml
index 674731b..343f18b 100644
--- a/src/jvm/pom.xml
+++ b/src/jvm/pom.xml
@@ -37,9 +37,9 @@
test
- org.scalatest
- scalatest_3
- 3.2.9
+ org.scala-lang
+ scala3-library_3
+ 3.5.2
test
@@ -134,24 +134,6 @@
--add-opens java.base/java.lang=ALL-UNNAMED
-
- org.scalatest
- scalatest-maven-plugin
- 2.2.0
-
- ${project.build.directory}/surefire-reports
- .
- WDF TestSuite.txt
-
-
-
- test
-
- test
-
-
-
-
maven-failsafe-plugin
2.22.2
diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/RemoveFieldsQueryTest.groovy b/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/RemoveFieldsQueryTest.groovy
new file mode 100644
index 0000000..44acb89
--- /dev/null
+++ b/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/RemoveFieldsQueryTest.groovy
@@ -0,0 +1,107 @@
+package solutions.bitbadger.documents.groovy.query
+
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.DisplayName
+import org.junit.jupiter.api.Test
+//import solutions.bitbadger.documents.DocumentException
+import solutions.bitbadger.documents.Field
+import solutions.bitbadger.documents.Parameter
+import solutions.bitbadger.documents.ParameterType
+import solutions.bitbadger.documents.query.RemoveFieldsQuery
+import solutions.bitbadger.documents.support.ForceDialect
+
+import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
+import static groovy.test.GroovyAssert.*
+
+/**
+ * Unit tests for the `RemoveFields` object
+ */
+@DisplayName('JVM | Groovy | Query | RemoveFieldsQuery')
+class RemoveFieldsQueryTest {
+
+ /**
+ * Reset the dialect
+ */
+ @AfterEach
+ void cleanUp() {
+ ForceDialect.none()
+ }
+
+ @Test
+ @DisplayName('byId generates correctly | PostgreSQL')
+ void byIdPostgres() {
+ ForceDialect.postgres()
+ assertEquals('Remove Fields query not constructed correctly',
+ "UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data->>'id' = :id".toString(),
+ RemoveFieldsQuery.byId(TEST_TABLE, List.of(new Parameter(':name', ParameterType.STRING, '{a,z}'))))
+ }
+
+ @Test
+ @DisplayName('byId generates correctly | SQLite')
+ void byIdSQLite() {
+ ForceDialect.sqlite()
+ assertEquals('Remove Field query not constructed correctly',
+ "UPDATE $TEST_TABLE SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id".toString(),
+ RemoveFieldsQuery.byId(TEST_TABLE, List.of(new Parameter(':name0', ParameterType.STRING, 'a'),
+ new Parameter(':name1', ParameterType.STRING, 'z'))))
+ }
+
+ @Test
+ @DisplayName('byFields generates correctly | PostgreSQL')
+ void byFieldsPostgres() {
+ ForceDialect.postgres()
+ assertEquals('Remove Field query not constructed correctly',
+ "UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data->>'f' > :g".toString(),
+ RemoveFieldsQuery.byFields(TEST_TABLE, List.of(new Parameter(':name', ParameterType.STRING, '{b,c}')),
+ List.of(Field.greater('f', '', ':g'))))
+ }
+
+ @Test
+ @DisplayName('byFields generates correctly | SQLite')
+ void byFieldsSQLite() {
+ ForceDialect.sqlite()
+ assertEquals('Remove Field query not constructed correctly',
+ "UPDATE $TEST_TABLE SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g".toString(),
+ RemoveFieldsQuery.byFields(TEST_TABLE,
+ List.of(new Parameter(':name0', ParameterType.STRING, 'b'),
+ new Parameter(':name1', ParameterType.STRING, 'c')),
+ List.of(Field.greater('f', '', ':g'))))
+ }
+
+ @Test
+ @DisplayName('byContains generates correctly | PostgreSQL')
+ void byContainsPostgres() {
+ ForceDialect.postgres()
+ assertEquals('Remove Field query not constructed correctly',
+ "UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data @> :criteria".toString(),
+ RemoveFieldsQuery.byContains(TEST_TABLE,
+ List.of(new Parameter(':name', ParameterType.STRING, '{m,n}'))))
+ }
+
+// TODO: resolve java.base open issue
+// @Test
+// @DisplayName('byContains fails | SQLite')
+// void byContainsSQLite() {
+// ForceDialect.sqlite()
+// assertThrows(DocumentException) { RemoveFieldsQuery.byContains(TEST_TABLE, List.of()) }
+// }
+
+ @Test
+ @DisplayName('byJsonPath generates correctly | PostgreSQL')
+ void byJsonPathPostgres() {
+ ForceDialect.postgres()
+ assertEquals('Remove Field query not constructed correctly',
+ "UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)"
+ .toString(),
+ RemoveFieldsQuery.byJsonPath(TEST_TABLE,
+ List.of(new Parameter(':name', ParameterType.STRING, '{o,p}'))))
+ }
+
+// TODO: resolve java.base open issue
+// @Test
+// @DisplayName('byJsonPath fails | SQLite')
+// void byJsonPathSQLite() {
+// ForceDialect.sqlite()
+// assertThrows(DocumentException) { RemoveFieldsQuery.byJsonPath(TEST_TABLE, List.of()) }
+// }
+}
diff --git a/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/WhereTest.groovy b/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/WhereTest.groovy
new file mode 100644
index 0000000..d65a115
--- /dev/null
+++ b/src/jvm/src/test/groovy/solutions/bitbadger/documents/groovy/query/WhereTest.groovy
@@ -0,0 +1,175 @@
+package solutions.bitbadger.documents.groovy.query
+
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.DisplayName
+import org.junit.jupiter.api.Test
+//import solutions.bitbadger.documents.DocumentException
+import solutions.bitbadger.documents.Field
+import solutions.bitbadger.documents.FieldMatch
+import solutions.bitbadger.documents.query.Where
+import solutions.bitbadger.documents.support.ForceDialect
+
+import static groovy.test.GroovyAssert.*
+
+/**
+ * Unit tests for the `Where` object
+ */
+@DisplayName('JVM | Groovy | Query | Where')
+class WhereTest {
+
+ /**
+ * Clear the connection string (resets Dialect)
+ */
+ @AfterEach
+ void cleanUp() {
+ ForceDialect.none()
+ }
+
+ @Test
+ @DisplayName('byFields is blank when given no fields')
+ void byFieldsBlankIfEmpty() {
+ assertEquals('', Where.byFields(List.of()))
+ }
+
+ @Test
+ @DisplayName('byFields generates one numeric field | PostgreSQL')
+ void byFieldsOneFieldPostgres() {
+ ForceDialect.postgres()
+ assertEquals("(data->>'it')::numeric = :that", Where.byFields(List.of(Field.equal('it', 9, ':that'))))
+ }
+
+ @Test
+ @DisplayName('byFields generates one alphanumeric field | PostgreSQL')
+ void byFieldsOneAlphaFieldPostgres() {
+ ForceDialect.postgres()
+ assertEquals("data->>'it' = :that", Where.byFields(List.of(Field.equal('it', '', ':that'))))
+ }
+
+ @Test
+ @DisplayName('byFields generates one field | SQLite')
+ void byFieldsOneFieldSQLite() {
+ ForceDialect.sqlite()
+ assertEquals("data->>'it' = :that", Where.byFields(List.of(Field.equal('it', '', ':that'))))
+ }
+
+ @Test
+ @DisplayName('byFields generates multiple fields w/ default match | PostgreSQL')
+ void byFieldsMultipleDefaultPostgres() {
+ ForceDialect.postgres()
+ assertEquals("data->>'1' = :one AND (data->>'2')::numeric = :two AND data->>'3' = :three",
+ Where.byFields(
+ List.of(Field.equal('1', '', ':one'), Field.equal('2', 0L, ':two'),
+ Field.equal('3', '', ':three'))))
+ }
+
+ @Test
+ @DisplayName('byFields generates multiple fields w/ default match | SQLite')
+ void byFieldsMultipleDefaultSQLite() {
+ ForceDialect.sqlite()
+ assertEquals("data->>'1' = :one AND data->>'2' = :two AND data->>'3' = :three",
+ Where.byFields(
+ List.of(Field.equal('1', '', ':one'), Field.equal('2', 0L, ':two'),
+ Field.equal('3', '', ':three'))))
+ }
+
+ @Test
+ @DisplayName('byFields generates multiple fields w/ ANY match | PostgreSQL')
+ void byFieldsMultipleAnyPostgres() {
+ ForceDialect.postgres()
+ assertEquals("data->>'1' = :one OR (data->>'2')::numeric = :two OR data->>'3' = :three",
+ Where.byFields(
+ List.of(Field.equal('1', '', ':one'), Field.equal('2', 0L, ':two'),
+ Field.equal('3', '', ':three')),
+ FieldMatch.ANY))
+ }
+
+ @Test
+ @DisplayName('byFields generates multiple fields w/ ANY match | SQLite')
+ void byFieldsMultipleAnySQLite() {
+ ForceDialect.sqlite()
+ assertEquals("data->>'1' = :one OR data->>'2' = :two OR data->>'3' = :three",
+ Where.byFields(
+ List.of(Field.equal('1', '', ':one'), Field.equal('2', 0L, ':two'),
+ Field.equal('3', '', ':three')),
+ FieldMatch.ANY))
+ }
+
+ @Test
+ @DisplayName('byId generates defaults for alphanumeric key | PostgreSQL')
+ void byIdDefaultAlphaPostgres() {
+ ForceDialect.postgres()
+ assertEquals("data->>'id' = :id", Where.byId())
+ }
+
+ @Test
+ @DisplayName('byId generates defaults for numeric key | PostgreSQL')
+ void byIdDefaultNumericPostgres() {
+ ForceDialect.postgres()
+ assertEquals("(data->>'id')::numeric = :id", Where.byId(":id", 5))
+ }
+
+ @Test
+ @DisplayName('byId generates defaults | SQLite')
+ void byIdDefaultSQLite() {
+ ForceDialect.sqlite()
+ assertEquals("data->>'id' = :id", Where.byId())
+ }
+
+ @Test
+ @DisplayName('byId generates named ID | PostgreSQL')
+ void byIdDefaultNamedPostgres() {
+ ForceDialect.postgres()
+ assertEquals("data->>'id' = :key", Where.byId(':key'))
+ }
+
+ @Test
+ @DisplayName('byId generates named ID | SQLite')
+ void byIdDefaultNamedSQLite() {
+ ForceDialect.sqlite()
+ assertEquals("data->>'id' = :key", Where.byId(':key'))
+ }
+
+ @Test
+ @DisplayName('jsonContains generates defaults | PostgreSQL')
+ void jsonContainsDefaultPostgres() {
+ ForceDialect.postgres()
+ assertEquals('data @> :criteria', Where.jsonContains())
+ }
+
+ @Test
+ @DisplayName('jsonContains generates named parameter | PostgreSQL')
+ void jsonContainsNamedPostgres() {
+ ForceDialect.postgres()
+ assertEquals('data @> :it', Where.jsonContains(':it'))
+ }
+
+// TODO: resolve java.base open issue
+// @Test
+// @DisplayName('jsonContains fails | SQLite')
+// void jsonContainsFailsSQLite() {
+// ForceDialect.sqlite()
+// assertThrows(DocumentException) { Where.jsonContains() }
+// }
+
+ @Test
+ @DisplayName('jsonPathMatches generates defaults | PostgreSQL')
+ void jsonPathMatchDefaultPostgres() {
+ ForceDialect.postgres()
+ assertEquals('jsonb_path_exists(data, :path::jsonpath)', Where.jsonPathMatches())
+ }
+
+ @Test
+ @DisplayName('jsonPathMatches generates named parameter | PostgreSQL')
+ void jsonPathMatchNamedPostgres() {
+ ForceDialect.postgres()
+ assertEquals('jsonb_path_exists(data, :jp::jsonpath)', Where.jsonPathMatches(':jp'))
+ }
+
+// TODO: resolve java.base open issue
+// @Test
+// @DisplayName('jsonPathMatches fails | SQLite')
+// void jsonPathFailsSQLite() {
+// ForceDialect.sqlite()
+// assertThrows(DocumentException) { Where.jsonPathMatches() }
+// }
+}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/AutoIdSpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/AutoIdSpec.scala
deleted file mode 100644
index 7653c91..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/AutoIdSpec.scala
+++ /dev/null
@@ -1,86 +0,0 @@
-package solutions.bitbadger.documents.scala
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.scala.support.{ByteIdClass, IntIdClass, LongIdClass, ShortIdClass, StringIdClass}
-import solutions.bitbadger.documents.{AutoId, DocumentException}
-
-class AutoIdSpec extends AnyFunSpec with Matchers {
-
- describe("generateUUID") {
- it("generates a UUID string") {
- AutoId.generateUUID().length shouldEqual 32
- }
- }
-
- describe("generateRandomString") {
- it("generates a random hex character string of an even length") {
- AutoId.generateRandomString(8).length shouldEqual 8
- }
- it("generates a random hex character string of an odd length") {
- AutoId.generateRandomString(11).length shouldEqual 11
- }
- it("generates different random hex character strings") {
- val result1 = AutoId.generateRandomString(16)
- val result2 = AutoId.generateRandomString(16)
- result1 should not be theSameInstanceAs (result2)
- }
- }
-
- describe("needsAutoId") {
- it("fails for null document") {
- a [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.DISABLED, null, "id")
- }
- it("fails for missing ID property") {
- a [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.UUID, IntIdClass(0), "Id")
- }
- it("returns false if disabled") {
- AutoId.needsAutoId(AutoId.DISABLED, "", "") shouldBe false
- }
- it("returns true for Number strategy and byte ID of 0") {
- AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(0), "id") shouldBe true
- }
- it("returns false for Number strategy and byte ID of non-0") {
- AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(77), "id") shouldBe false
- }
- it("returns true for Number strategy and short ID of 0") {
- AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(0), "id") shouldBe true
- }
- it("returns false for Number strategy and short ID of non-0") {
- AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(31), "id") shouldBe false
- }
- it("returns true for Number strategy and int ID of 0") {
- AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(0), "id") shouldBe true
- }
- it("returns false for Number strategy and int ID of non-0") {
- AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(6), "id") shouldBe false
- }
- it("returns true for Number strategy and long ID of 0") {
- AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(0), "id") shouldBe true
- }
- it("returns false for Number strategy and long ID of non-0") {
- AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(2), "id") shouldBe false
- }
- it("fails for Number strategy and non-number ID") {
- a [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.NUMBER, StringIdClass(""), "id")
- }
- it("returns true for UUID strategy and blank ID") {
- AutoId.needsAutoId(AutoId.UUID, StringIdClass(""), "id") shouldBe true
- }
- it("returns false for UUID strategy and non-blank ID") {
- AutoId.needsAutoId(AutoId.UUID, StringIdClass("howdy"), "id") shouldBe false
- }
- it("fails for UUID strategy and non-string ID") {
- a [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.UUID, IntIdClass(5), "id")
- }
- it("returns true for Random String strategy and blank ID") {
- AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass(""), "id") shouldBe true
- }
- it("returns false for Random String strategy and non-blank ID") {
- AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass("full"), "id") shouldBe false
- }
- it("fails for Random String strategy and non-string ID") {
- a [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id")
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/AutoIdTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/AutoIdTest.scala
new file mode 100644
index 0000000..6c41e8f
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/AutoIdTest.scala
@@ -0,0 +1,128 @@
+package solutions.bitbadger.documents.scala
+
+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 {
+
+ @Test
+ @DisplayName("Generates a UUID string")
+ def generateUUID(): Unit =
+ assertEquals(32, AutoId.generateUUID().length(), "The UUID should have been a 32-character string")
+
+ @Test
+ @DisplayName("Generates a random hex character string of an even length")
+ def generateRandomStringEven(): Unit =
+ val result = AutoId.generateRandomString(8)
+ assertEquals(8, result.length(), s"There should have been 8 characters in $result")
+
+ @Test
+ @DisplayName("Generates a random hex character string of an odd length")
+ def generateRandomStringOdd(): Unit =
+ val result = AutoId.generateRandomString(11)
+ assertEquals(11, result.length(), s"There should have been 11 characters in $result")
+
+ @Test
+ @DisplayName("Generates different random hex character strings")
+ def generateRandomStringIsRandom(): Unit =
+ val result1 = AutoId.generateRandomString(16)
+ val result2 = AutoId.generateRandomString(16)
+ assertNotEquals(result1, result2, "There should have been 2 different strings generated")
+
+ @Test
+ @DisplayName("needsAutoId fails for null document")
+ def needsAutoIdFailsForNullDocument(): Unit =
+ assertThrows(classOf[DocumentException], () => AutoId.needsAutoId(AutoId.DISABLED, null, "id"))
+
+ @Test
+ @DisplayName("needsAutoId fails for missing ID property")
+ def needsAutoIdFailsForMissingId(): Unit =
+ assertThrows(classOf[DocumentException], () => AutoId.needsAutoId(AutoId.UUID, IntIdClass(0), "Id"))
+
+
+ @Test
+ @DisplayName("needsAutoId returns false if disabled")
+ def needsAutoIdFalseIfDisabled(): Unit =
+ assertFalse(AutoId.needsAutoId(AutoId.DISABLED, "", ""), "Disabled Auto ID should always return false")
+
+ @Test
+ @DisplayName("needsAutoId returns true for Number strategy and byte ID of 0")
+ def needsAutoIdTrueForByteWithZero(): Unit =
+ assertTrue(AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(0), "id"), "Number Auto ID with 0 should return true")
+
+ @Test
+ @DisplayName("needsAutoId returns false for Number strategy and byte ID of non-0")
+ def needsAutoIdFalseForByteWithNonZero(): Unit =
+ assertFalse(AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(77), "id"), "Number Auto ID with 77 should return false")
+
+ @Test
+ @DisplayName("needsAutoId returns true for Number strategy and short ID of 0")
+ def needsAutoIdTrueForShortWithZero(): Unit =
+ assertTrue(AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(0), "id"), "Number Auto ID with 0 should return true")
+
+ @Test
+ @DisplayName("needsAutoId returns false for Number strategy and short ID of non-0")
+ def needsAutoIdFalseForShortWithNonZero(): Unit =
+ assertFalse(AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(31), "id"), "Number Auto ID with 31 should return false")
+
+ @Test
+ @DisplayName("needsAutoId returns true for Number strategy and int ID of 0")
+ def needsAutoIdTrueForIntWithZero(): Unit =
+ assertTrue(AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(0), "id"), "Number Auto ID with 0 should return true")
+
+ @Test
+ @DisplayName("needsAutoId returns false for Number strategy and int ID of non-0")
+ def needsAutoIdFalseForIntWithNonZero(): Unit =
+ assertFalse(AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(6), "id"), "Number Auto ID with 6 should return false")
+
+ @Test
+ @DisplayName("needsAutoId returns true for Number strategy and long ID of 0")
+ def needsAutoIdTrueForLongWithZero(): Unit =
+ assertTrue(AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(0), "id"), "Number Auto ID with 0 should return true")
+
+ @Test
+ @DisplayName("needsAutoId returns false for Number strategy and long ID of non-0")
+ def needsAutoIdFalseForLongWithNonZero(): Unit =
+ assertFalse(AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(2), "id"), "Number Auto ID with 2 should return false")
+
+ @Test
+ @DisplayName("needsAutoId fails for Number strategy and non-number ID")
+ def needsAutoIdFailsForNumberWithStringId(): Unit =
+ assertThrows(classOf[DocumentException], () => AutoId.needsAutoId(AutoId.NUMBER, StringIdClass(""), "id"))
+
+ @Test
+ @DisplayName("needsAutoId returns true for UUID strategy and blank ID")
+ def needsAutoIdTrueForUUIDWithBlank(): Unit =
+ assertTrue(AutoId.needsAutoId(AutoId.UUID, StringIdClass(""), "id"), "UUID Auto ID with blank should return true")
+
+ @Test
+ @DisplayName("needsAutoId returns false for UUID strategy and non-blank ID")
+ def needsAutoIdFalseForUUIDNotBlank(): Unit =
+ assertFalse(AutoId.needsAutoId(AutoId.UUID, StringIdClass("howdy"), "id"),
+ "UUID Auto ID with non-blank should return false")
+
+ @Test
+ @DisplayName("needsAutoId fails for UUID strategy and non-string ID")
+ def needsAutoIdFailsForUUIDNonString(): Unit =
+ assertThrows(classOf[DocumentException], () => AutoId.needsAutoId(AutoId.UUID, IntIdClass(5), "id"))
+
+ @Test
+ @DisplayName("needsAutoId returns true for Random String strategy and blank ID")
+ def needsAutoIdTrueForRandomWithBlank(): Unit =
+ assertTrue(AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass(""), "id"),
+ "Random String Auto ID with blank should return true")
+
+ @Test
+ @DisplayName("needsAutoId returns false for Random String strategy and non-blank ID")
+ def needsAutoIdFalseForRandomNotBlank(): Unit =
+ assertFalse(AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass("full"), "id"),
+ "Random String Auto ID with non-blank should return false")
+
+ @Test
+ @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/jvm/src/test/scala/solutions/bitbadger/documents/scala/ClearConfiguration.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ClearConfiguration.scala
deleted file mode 100644
index b2b64cf..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ClearConfiguration.scala
+++ /dev/null
@@ -1,11 +0,0 @@
-package solutions.bitbadger.documents.scala
-
-import org.scalatest.{BeforeAndAfterEach, Suite}
-import solutions.bitbadger.documents.support.ForceDialect
-
-trait ClearConfiguration extends BeforeAndAfterEach { this: Suite =>
-
- override def afterEach(): Unit =
- try super.afterEach ()
- finally ForceDialect.none()
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ConfigurationSpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ConfigurationSpec.scala
deleted file mode 100644
index 183f4b6..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ConfigurationSpec.scala
+++ /dev/null
@@ -1,38 +0,0 @@
-package solutions.bitbadger.documents.scala
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{AutoId, Configuration, Dialect, DocumentException}
-
-class ConfigurationSpec extends AnyFunSpec with Matchers {
-
- describe("idField") {
- it("defaults to `id`") {
- Configuration.idField shouldEqual "id"
- }
- }
-
- describe("autoIdStrategy") {
- it("defaults to `DISABLED`") {
- Configuration.autoIdStrategy shouldEqual AutoId.DISABLED
- }
- }
-
- describe("idStringLength") {
- it("defaults to 16") {
- Configuration.idStringLength shouldEqual 16
- }
- }
-
- describe("dialect") {
- it("is derived from connection string") {
- try {
- a [DocumentException] should be thrownBy Configuration.dialect()
- Configuration.setConnectionString("jdbc:postgresql:db")
- Configuration.dialect() shouldEqual Dialect.POSTGRESQL
- } finally {
- Configuration.setConnectionString(null)
- }
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ConfigurationTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ConfigurationTest.scala
new file mode 100644
index 0000000..1864216
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ConfigurationTest.scala
@@ -0,0 +1,35 @@
+package solutions.bitbadger.documents.scala
+
+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 {
+
+ @Test
+ @DisplayName("Default ID field is `id`")
+ def defaultIdField(): Unit =
+ assertEquals("id", Configuration.idField, "Default ID field incorrect")
+
+ @Test
+ @DisplayName("Default Auto ID strategy is `DISABLED`")
+ def defaultAutoId(): Unit =
+ assertEquals(AutoId.DISABLED, Configuration.autoIdStrategy, "Default Auto ID strategy should be `disabled`")
+
+ @Test
+ @DisplayName("Default ID string length should be 16")
+ def defaultIdStringLength(): Unit =
+ assertEquals(16, Configuration.idStringLength, "Default ID string length should be 16")
+
+ @Test
+ @DisplayName("Dialect is derived from connection string")
+ def dialectIsDerived(): Unit =
+ try {
+ assertThrows(classOf[DocumentException], () => Configuration.dialect())
+ Configuration.setConnectionString("jdbc:postgresql:db")
+ assertEquals(Dialect.POSTGRESQL, Configuration.dialect())
+ } finally {
+ Configuration.setConnectionString(null)
+ }
+}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DialectSpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DialectSpec.scala
deleted file mode 100644
index 7321fe0..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DialectSpec.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-package solutions.bitbadger.documents.scala
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{Dialect, DocumentException}
-
-class DialectSpec extends AnyFunSpec with Matchers {
-
- describe("deriveFromConnectionString") {
- it("derives PostgreSQL correctly") {
- Dialect.deriveFromConnectionString("jdbc:postgresql:db") shouldEqual Dialect.POSTGRESQL
- }
- it("derives SQLite correctly") {
- Dialect.deriveFromConnectionString("jdbc:sqlite:memory") shouldEqual Dialect.SQLITE
- }
- it("fails when the connection string is unknown") {
- try {
- Dialect.deriveFromConnectionString("SQL Server")
- fail("Dialect derivation should have failed")
- } catch {
- case ex: DocumentException =>
- ex.getMessage should not be null
- ex.getMessage should include ("[SQL Server]")
- }
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DialectTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DialectTest.scala
new file mode 100644
index 0000000..5089914
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DialectTest.scala
@@ -0,0 +1,34 @@
+package solutions.bitbadger.documents.scala
+
+import org.junit.jupiter.api.{DisplayName, Test}
+import org.junit.jupiter.api.Assertions.*
+import solutions.bitbadger.documents.{Dialect, DocumentException}
+
+@DisplayName("JVM | Scala | Dialect")
+class DialectTest {
+
+ @Test
+ @DisplayName("deriveFromConnectionString derives PostgreSQL correctly")
+ def derivesPostgres(): Unit =
+ assertEquals(Dialect.POSTGRESQL, Dialect.deriveFromConnectionString("jdbc:postgresql:db"),
+ "Dialect should have been PostgreSQL")
+
+ @Test
+ @DisplayName("deriveFromConnectionString derives SQLite correctly")
+ def derivesSQLite(): Unit =
+ assertEquals(Dialect.SQLITE, Dialect.deriveFromConnectionString("jdbc:sqlite:memory"),
+ "Dialect should have been SQLite")
+
+ @Test
+ @DisplayName("deriveFromConnectionString fails when the connection string is unknown")
+ def deriveFailsWhenUnknown(): Unit =
+ try {
+ Dialect.deriveFromConnectionString("SQL Server")
+ fail("Dialect derivation should have failed")
+ } catch {
+ case ex: DocumentException =>
+ assertNotNull(ex.getMessage, "The exception message should not have been null")
+ 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/DocumentIndexSpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DocumentIndexSpec.scala
deleted file mode 100644
index 3e21f8e..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DocumentIndexSpec.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-package solutions.bitbadger.documents.scala
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.DocumentIndex
-
-class DocumentIndexSpec extends AnyFunSpec with Matchers {
-
- describe("sql") {
- it("returns blank for FULL") {
- DocumentIndex.FULL.getSql shouldEqual ""
- }
- it("returns jsonb_path_ops for OPTIMIZED") {
- DocumentIndex.OPTIMIZED.getSql shouldEqual " jsonb_path_ops"
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DocumentIndexTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DocumentIndexTest.scala
new file mode 100644
index 0000000..fd372bf
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/DocumentIndexTest.scala
@@ -0,0 +1,19 @@
+package solutions.bitbadger.documents.scala
+
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.{DisplayName, Test}
+import solutions.bitbadger.documents.DocumentIndex
+
+@DisplayName("JVM | Scala | DocumentIndex")
+class DocumentIndexTest {
+
+ @Test
+ @DisplayName("FULL uses proper SQL")
+ def fullSQL(): Unit =
+ assertEquals("", DocumentIndex.FULL.getSql, "The SQL for Full is incorrect")
+
+ @Test
+ @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/FieldMatchSpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldMatchSpec.scala
deleted file mode 100644
index 07d0843..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldMatchSpec.scala
+++ /dev/null
@@ -1,20 +0,0 @@
-package solutions.bitbadger.documents.scala
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.FieldMatch
-
-/**
- * Unit tests for the `FieldMatch` enum
- */
-class FieldMatchSpec extends AnyFunSpec with Matchers {
-
- describe("sql") {
- it("returns OR for ANY") {
- FieldMatch.ANY.getSql shouldEqual "OR"
- }
- it("returns AND for ALL") {
- FieldMatch.ALL.getSql shouldEqual "AND"
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldMatchTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldMatchTest.scala
new file mode 100644
index 0000000..4b2440b
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldMatchTest.scala
@@ -0,0 +1,22 @@
+package solutions.bitbadger.documents.scala
+
+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 {
+
+ @Test
+ @DisplayName("ANY uses proper SQL")
+ def any(): Unit =
+ assertEquals("OR", FieldMatch.ANY.getSql, "ANY should use OR")
+
+ @Test
+ @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/FieldSpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldSpec.scala
deleted file mode 100644
index d54390b..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldSpec.scala
+++ /dev/null
@@ -1,428 +0,0 @@
-package solutions.bitbadger.documents.scala
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.support.ForceDialect
-import solutions.bitbadger.documents.{Dialect, DocumentException, Field, FieldFormat, Op}
-
-import scala.jdk.CollectionConverters.*
-
-class FieldSpec extends AnyFunSpec with ClearConfiguration with Matchers {
-
- // ~~~ INSTANCE METHODS ~~~
-
- describe("withParameterName") {
- it("fails for invalid name") {
- a [DocumentException] should be thrownBy Field.equal("it", "").withParameterName("2424")
- }
- it("works with colon prefix") {
- val field = Field.equal("abc", "22").withQualifier("me")
- val withParam = field.withParameterName(":test")
- withParam should not be theSameInstanceAs (field)
- withParam.getName shouldEqual field.getName
- withParam.getComparison shouldEqual field.getComparison
- withParam.getParameterName shouldEqual ":test"
- withParam.getQualifier shouldEqual field.getQualifier
- }
- it("works with at-sign prefix") {
- val field = Field.equal("def", "44")
- val withParam = field.withParameterName("@unit")
- withParam should not be theSameInstanceAs (field)
- withParam.getName shouldEqual field.getName
- withParam.getComparison shouldEqual field.getComparison
- withParam.getParameterName shouldEqual "@unit"
- withParam.getQualifier shouldEqual field.getQualifier
- }
- }
-
- describe("withQualifier") {
- it("sets qualifier correctly") {
- val field = Field.equal("j", "k")
- val withQual = field.withQualifier("test")
- withQual should not be theSameInstanceAs (field)
- withQual.getName shouldEqual field.getName
- withQual.getComparison shouldEqual field.getComparison
- withQual.getParameterName shouldEqual field.getParameterName
- withQual.getQualifier shouldEqual "test"
- }
- }
-
- describe("path") {
- it("generates simple unqualified PostgreSQL field") {
- Field.greaterOrEqual("SomethingCool", 18).path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual
- "data->>'SomethingCool'"
- }
- it("generates simple qualified PostgreSQL field") {
- Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual
- "this.data->>'SomethingElse'"
- }
- it("generates nested unqualified PostgreSQL field") {
- Field.equal("My.Nested.Field", "howdy").path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual
- "data#>>'{My,Nested,Field}'"
- }
- it("generates nested qualified PostgreSQL field") {
- Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual
- "bird.data#>>'{Nest,Away}'"
- }
- it("generates simple unqualified SQLite field") {
- Field.greaterOrEqual("SomethingCool", 18).path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual
- "data->>'SomethingCool'"
- }
- it("generates simple qualified SQLite field") {
- Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual
- "this.data->>'SomethingElse'"
- }
- it("generates nested unqualified SQLite field") {
- Field.equal("My.Nested.Field", "howdy").path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual
- "data->'My'->'Nested'->>'Field'"
- }
- it("generates nested qualified SQLite field") {
- Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual
- "bird.data->'Nest'->>'Away'"
- }
- }
-
- describe("toWhere") {
- it("generates exists w/o qualifier | PostgreSQL") {
- ForceDialect.postgres()
- Field.exists("that_field").toWhere shouldEqual "data->>'that_field' IS NOT NULL"
- }
- it("generates exists w/o qualifier | SQLite") {
- ForceDialect.sqlite()
- Field.exists("that_field").toWhere shouldEqual "data->>'that_field' IS NOT NULL"
- }
- it("generates not-exists w/o qualifier | PostgreSQL") {
- ForceDialect.postgres()
- Field.notExists("a_field").toWhere shouldEqual "data->>'a_field' IS NULL"
- }
- it("generates not-exists w/o qualifier | SQLite") {
- ForceDialect.sqlite()
- Field.notExists("a_field").toWhere shouldEqual "data->>'a_field' IS NULL"
- }
- it("generates BETWEEN w/o qualifier, numeric range | PostgreSQL") {
- ForceDialect.postgres()
- Field.between("age", 13, 17, "@age").toWhere shouldEqual "(data->>'age')::numeric BETWEEN @agemin AND @agemax"
- }
- it("generates BETWEEN w/o qualifier, alphanumeric range | PostgreSQL") {
- ForceDialect.postgres()
- Field.between("city", "Atlanta", "Chicago", ":city").toWhere shouldEqual
- "data->>'city' BETWEEN :citymin AND :citymax"
- }
- it("generates BETWEEN w/o qualifier | SQLite") {
- ForceDialect.sqlite()
- Field.between("age", 13, 17, "@age").toWhere shouldEqual "data->>'age' BETWEEN @agemin AND @agemax"
- }
- it("generates BETWEEN w/ qualifier, numeric range | PostgreSQL") {
- ForceDialect.postgres()
- Field.between("age", 13, 17, "@age").withQualifier("test").toWhere shouldEqual
- "(test.data->>'age')::numeric BETWEEN @agemin AND @agemax"
- }
- it("generates BETWEEN w/ qualifier, alphanumeric range | PostgreSQL") {
- ForceDialect.postgres()
- Field.between("city", "Atlanta", "Chicago", ":city").withQualifier("unit").toWhere shouldEqual
- "unit.data->>'city' BETWEEN :citymin AND :citymax"
- }
- it("generates BETWEEN w/ qualifier | SQLite") {
- ForceDialect.sqlite()
- Field.between("age", 13, 17, "@age").withQualifier("my").toWhere shouldEqual
- "my.data->>'age' BETWEEN @agemin AND @agemax"
- }
- it("generates IN/any, numeric values | PostgreSQL") {
- ForceDialect.postgres()
- Field.any("even", List(2, 4, 6).asJava, ":nbr").toWhere shouldEqual
- "(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)"
- }
- it("generates IN/any, alphanumeric values | PostgreSQL") {
- ForceDialect.postgres()
- Field.any("test", List("Atlanta", "Chicago").asJava, ":city").toWhere shouldEqual
- "data->>'test' IN (:city_0, :city_1)"
- }
- it("generates IN/any | SQLite") {
- ForceDialect.sqlite()
- Field.any("test", List("Atlanta", "Chicago").asJava, ":city").toWhere shouldEqual
- "data->>'test' IN (:city_0, :city_1)"
- }
- it("generates inArray | PostgreSQL") {
- ForceDialect.postgres()
- Field.inArray("even", "tbl", List(2, 4, 6, 8).asJava, ":it").toWhere shouldEqual
- "data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]"
- }
- it("generates inArray | SQLite") {
- ForceDialect.sqlite()
- Field.inArray("test", "tbl", List("Atlanta", "Chicago").asJava, ":city").toWhere shouldEqual
- "EXISTS (SELECT 1 FROM json_each(tbl.data, '$.test') WHERE value IN (:city_0, :city_1))"
- }
- it("generates others w/o qualifier | PostgreSQL") {
- ForceDialect.postgres()
- Field.equal("some_field", "", ":value").toWhere shouldEqual "data->>'some_field' = :value"
- }
- it("generates others w/o qualifier | SQLite") {
- ForceDialect.sqlite()
- Field.equal("some_field", "", ":value").toWhere shouldEqual "data->>'some_field' = :value"
- }
- it("generates no-parameter w/ qualifier | PostgreSQL") {
- ForceDialect.postgres()
- Field.exists("no_field").withQualifier("test").toWhere shouldEqual "test.data->>'no_field' IS NOT NULL"
- }
- it("generates no-parameter w/ qualifier | SQLite") {
- ForceDialect.sqlite()
- Field.exists("no_field").withQualifier("test").toWhere shouldEqual "test.data->>'no_field' IS NOT NULL"
- }
- it("generates parameter w/ qualifier | PostgreSQL") {
- ForceDialect.postgres()
- Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere shouldEqual
- "(q.data->>'le_field')::numeric <= :it"
- }
- it("generates parameter w/ qualifier | SQLite") {
- ForceDialect.sqlite()
- Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere shouldEqual "q.data->>'le_field' <= :it"
- }
- }
-
- // ~~~ STATIC CONSTRUCTOR TESTS ~~~
-
- describe("equal") {
- it("constructs a field w/o parameter name") {
- val field = Field.equal("Test", 14)
- field.getName shouldEqual "Test"
- field.getComparison.getOp shouldEqual Op.EQUAL
- field.getComparison.getValue shouldEqual 14
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- it("constructs a field w/ parameter name") {
- val field = Field.equal("Test", 14, ":w")
- field.getName shouldEqual "Test"
- field.getComparison.getOp shouldEqual Op.EQUAL
- field.getComparison.getValue shouldEqual 14
- field.getParameterName shouldEqual ":w"
- field.getQualifier should be (null)
- }
- }
-
- describe("greater") {
- it("constructs a field w/o parameter name") {
- val field = Field.greater("Great", "night")
- field.getName shouldEqual "Great"
- field.getComparison.getOp shouldEqual Op.GREATER
- field.getComparison.getValue shouldEqual "night"
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- it("constructs a field w/ parameter name") {
- val field = Field.greater("Great", "night", ":yeah")
- field.getName shouldEqual "Great"
- field.getComparison.getOp shouldEqual Op.GREATER
- field.getComparison.getValue shouldEqual "night"
- field.getParameterName shouldEqual ":yeah"
- field.getQualifier should be (null)
- }
- }
-
- describe("greaterOrEqual") {
- it("constructs a field w/o parameter name") {
- val field = Field.greaterOrEqual("Nice", 88L)
- field.getName shouldEqual "Nice"
- field.getComparison.getOp shouldEqual Op.GREATER_OR_EQUAL
- field.getComparison.getValue shouldEqual 88L
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- it("constructs a field w/ parameter name") {
- val field = Field.greaterOrEqual("Nice", 88L, ":nice")
- field.getName shouldEqual "Nice"
- field.getComparison.getOp shouldEqual Op.GREATER_OR_EQUAL
- field.getComparison.getValue shouldEqual 88L
- field.getParameterName shouldEqual ":nice"
- field.getQualifier should be (null)
- }
- }
-
- describe("less") {
- it("constructs a field w/o parameter name") {
- val field = Field.less("Lesser", "seven")
- field.getName shouldEqual "Lesser"
- field.getComparison.getOp shouldEqual Op.LESS
- field.getComparison.getValue shouldEqual "seven"
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- it("constructs a field w/ parameter name") {
- val field = Field.less("Lesser", "seven", ":max")
- field.getName shouldEqual "Lesser"
- field.getComparison.getOp shouldEqual Op.LESS
- field.getComparison.getValue shouldEqual "seven"
- field.getParameterName shouldEqual ":max"
- field.getQualifier should be (null)
- }
- }
-
- describe("lessOrEqual") {
- it("constructs a field w/o parameter name") {
- val field = Field.lessOrEqual("Nobody", "KNOWS")
- field.getName shouldEqual "Nobody"
- field.getComparison.getOp shouldEqual Op.LESS_OR_EQUAL
- field.getComparison.getValue shouldEqual "KNOWS"
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- it("constructs a field w/ parameter name") {
- val field = Field.lessOrEqual("Nobody", "KNOWS", ":nope")
- field.getName shouldEqual "Nobody"
- field.getComparison.getOp shouldEqual Op.LESS_OR_EQUAL
- field.getComparison.getValue shouldEqual "KNOWS"
- field.getParameterName shouldEqual ":nope"
- field.getQualifier should be (null)
- }
- }
-
- describe("notEqual") {
- it("constructs a field w/o parameter name") {
- val field = Field.notEqual("Park", "here")
- field.getName shouldEqual "Park"
- field.getComparison.getOp shouldEqual Op.NOT_EQUAL
- field.getComparison.getValue shouldEqual "here"
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- it("constructs a field w/ parameter name") {
- val field = Field.notEqual("Park", "here", ":now")
- field.getName shouldEqual "Park"
- field.getComparison.getOp shouldEqual Op.NOT_EQUAL
- field.getComparison.getValue shouldEqual "here"
- field.getParameterName shouldEqual ":now"
- field.getQualifier should be (null)
- }
- }
-
- describe("between") {
- it("constructs a field w/o parameter name") {
- val field = Field.between("Age", 18, 49)
- field.getName shouldEqual "Age"
- field.getComparison.getOp shouldEqual Op.BETWEEN
- field.getComparison.getValue.getFirst shouldEqual 18
- field.getComparison.getValue.getSecond shouldEqual 49
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- it("constructs a field w/ parameter name") {
- val field = Field.between("Age", 18, 49, ":limit")
- field.getName shouldEqual "Age"
- field.getComparison.getOp shouldEqual Op.BETWEEN
- field.getComparison.getValue.getFirst shouldEqual 18
- field.getComparison.getValue.getSecond shouldEqual 49
- field.getParameterName shouldEqual ":limit"
- field.getQualifier should be (null)
- }
- }
-
- describe("any") {
- it("constructs a field w/o parameter name") {
- val field = Field.any("Here", List(8, 16, 32).asJava)
- field.getName shouldEqual "Here"
- field.getComparison.getOp shouldEqual Op.IN
- field.getComparison.getValue should be (List(8, 16, 32).asJava)
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- it("constructs a field w/ parameter name") {
- val field = Field.any("Here", List(8, 16, 32).asJava, ":list")
- field.getName shouldEqual "Here"
- field.getComparison.getOp shouldEqual Op.IN
- field.getComparison.getValue should be (List(8, 16, 32).asJava)
- field.getParameterName shouldEqual ":list"
- field.getQualifier should be (null)
- }
- }
-
- describe("inArray") {
- it("constructs a field w/o parameter name") {
- val field = Field.inArray("ArrayField", "table", List("z").asJava)
- field.getName shouldEqual "ArrayField"
- field.getComparison.getOp shouldEqual Op.IN_ARRAY
- field.getComparison.getValue.getFirst shouldEqual "table"
- field.getComparison.getValue.getSecond should be (List("z").asJava)
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- it("constructs a field w/ parameter name") {
- val field = Field.inArray("ArrayField", "table", List("z").asJava, ":a")
- field.getName shouldEqual "ArrayField"
- field.getComparison.getOp shouldEqual Op.IN_ARRAY
- field.getComparison.getValue.getFirst shouldEqual "table"
- field.getComparison.getValue.getSecond should be (List("z").asJava)
- field.getParameterName shouldEqual ":a"
- field.getQualifier should be (null)
- }
- }
-
- describe("exists") {
- it("constructs a field") {
- val field = Field.exists("Groovy")
- field.getName shouldEqual "Groovy"
- field.getComparison.getOp shouldEqual Op.EXISTS
- field.getComparison.getValue shouldEqual ""
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- }
-
- describe("notExists") {
- it("constructs a field") {
- val field = Field.notExists("Groovy")
- field.getName shouldEqual "Groovy"
- field.getComparison.getOp shouldEqual Op.NOT_EXISTS
- field.getComparison.getValue shouldEqual ""
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- }
-
- describe("named") {
- it("named constructs a field") {
- val field = Field.named("Tacos")
- field.getName shouldEqual "Tacos"
- field.getComparison.getOp shouldEqual Op.EQUAL
- field.getComparison.getValue shouldEqual ""
- field.getParameterName should be (null)
- field.getQualifier should be (null)
- }
- }
-
- describe("static constructors") {
- it("fail for invalid parameter name") {
- a [DocumentException] should be thrownBy Field.equal("a", "b", "that ain't it, Jack...")
- }
- }
-
- describe("nameToPath") {
- it("creates a simple PostgreSQL SQL name") {
- Field.nameToPath("Simple", Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual "data->>'Simple'"
- }
- it("creates a simple SQLite SQL name") {
- Field.nameToPath("Simple", Dialect.SQLITE, FieldFormat.SQL) shouldEqual "data->>'Simple'"
- }
- it("creates a nested PostgreSQL SQL name") {
- Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual
- "data#>>'{A,Long,Path,to,the,Property}'"
- }
- it("creates a nested SQLite SQL name") {
- Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.SQL) shouldEqual
- "data->'A'->'Long'->'Path'->'to'->'the'->>'Property'"
- }
- it("creates a simple PostgreSQL JSON name") {
- Field.nameToPath("Simple", Dialect.POSTGRESQL, FieldFormat.JSON) shouldEqual "data->'Simple'"
- }
- it("creates a simple SQLite JSON name") {
- Field.nameToPath("Simple", Dialect.SQLITE, FieldFormat.JSON) shouldEqual "data->'Simple'"
- }
- it("creates a nested PostgreSQL JSON name") {
- Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.JSON) shouldEqual
- "data#>'{A,Long,Path,to,the,Property}'"
- }
- it("creates a nested SQLite JSON name") {
- Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.JSON) shouldEqual
- "data->'A'->'Long'->'Path'->'to'->'the'->'Property'"
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldTest.scala
new file mode 100644
index 0000000..3923561
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/FieldTest.scala
@@ -0,0 +1,539 @@
+package solutions.bitbadger.documents.scala
+
+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 scala.jdk.CollectionConverters.*
+
+@DisplayName("JVM | Scala | Field")
+class FieldTest {
+
+ /**
+ * Clear the connection string (resets Dialect)
+ */
+ @AfterEach
+ def cleanUp(): Unit =
+ ForceDialect.none()
+
+ // ~~~ INSTANCE METHODS ~~~
+
+ @Test
+ @DisplayName("withParameterName fails for invalid name")
+ def withParamNameFails(): Unit =
+ assertThrows(classOf[DocumentException], () => Field.equal("it", "").withParameterName("2424"))
+
+ @Test
+ @DisplayName("withParameterName works with colon prefix")
+ def withParamNameColon(): Unit =
+ val field = Field.equal("abc", "22").withQualifier("me")
+ val withParam = field.withParameterName(":test")
+ assertNotSame(field, withParam, "A new Field instance should have been created")
+ assertEquals(field.getName, withParam.getName, "Name should have been preserved")
+ assertEquals(field.getComparison, withParam.getComparison, "Comparison should have been preserved")
+ assertEquals(":test", withParam.getParameterName, "Parameter name not set correctly")
+ assertEquals(field.getQualifier, withParam.getQualifier, "Qualifier should have been preserved")
+
+ @Test
+ @DisplayName("withParameterName works with at-sign prefix")
+ def withParamNameAtSign(): Unit =
+ val field = Field.equal("def", "44")
+ val withParam = field.withParameterName("@unit")
+ assertNotSame(field, withParam, "A new Field instance should have been created")
+ assertEquals(field.getName, withParam.getName, "Name should have been preserved")
+ assertEquals(field.getComparison, withParam.getComparison, "Comparison should have been preserved")
+ assertEquals("@unit", withParam.getParameterName, "Parameter name not set correctly")
+ assertEquals(field.getQualifier, withParam.getQualifier, "Qualifier should have been preserved")
+
+ @Test
+ @DisplayName("withQualifier sets qualifier correctly")
+ def withQualifier(): Unit =
+ val field = Field.equal("j", "k")
+ val withQual = field.withQualifier("test")
+ assertNotSame(field, withQual, "A new Field instance should have been created")
+ assertEquals(field.getName, withQual.getName, "Name should have been preserved")
+ assertEquals(field.getComparison, withQual.getComparison, "Comparison should have been preserved")
+ assertEquals(field.getParameterName, withQual.getParameterName, "Parameter Name should have been preserved")
+ assertEquals("test", withQual.getQualifier, "Qualifier not set correctly")
+
+ @Test
+ @DisplayName("path generates for simple unqualified PostgreSQL field")
+ def pathPostgresSimpleUnqualified(): Unit =
+ assertEquals("data->>'SomethingCool'",
+ Field.greaterOrEqual("SomethingCool", 18).path(Dialect.POSTGRESQL, FieldFormat.SQL), "Path not correct")
+
+ @Test
+ @DisplayName("path generates for simple qualified PostgreSQL field")
+ def pathPostgresSimpleQualified(): Unit =
+ assertEquals("this.data->>'SomethingElse'",
+ Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.POSTGRESQL, FieldFormat.SQL),
+ "Path not correct")
+
+ @Test
+ @DisplayName("path generates for nested unqualified PostgreSQL field")
+ def pathPostgresNestedUnqualified(): Unit =
+ assertEquals("data#>>'{My,Nested,Field}'",
+ Field.equal("My.Nested.Field", "howdy").path(Dialect.POSTGRESQL, FieldFormat.SQL), "Path not correct")
+
+ @Test
+ @DisplayName("path generates for nested qualified PostgreSQL field")
+ def pathPostgresNestedQualified(): Unit =
+ assertEquals("bird.data#>>'{Nest,Away}'",
+ Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.POSTGRESQL, FieldFormat.SQL),
+ "Path not correct")
+
+ @Test
+ @DisplayName("path generates for simple unqualified SQLite field")
+ def pathSQLiteSimpleUnqualified(): Unit =
+ assertEquals("data->>'SomethingCool'",
+ Field.greaterOrEqual("SomethingCool", 18).path(Dialect.SQLITE, FieldFormat.SQL), "Path not correct")
+
+ @Test
+ @DisplayName("path generates for simple qualified SQLite field")
+ def pathSQLiteSimpleQualified(): Unit =
+ assertEquals("this.data->>'SomethingElse'",
+ Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.SQLITE, FieldFormat.SQL),
+ "Path not correct")
+
+ @Test
+ @DisplayName("path generates for nested unqualified SQLite field")
+ def pathSQLiteNestedUnqualified(): Unit =
+ assertEquals("data->'My'->'Nested'->>'Field'",
+ Field.equal("My.Nested.Field", "howdy").path(Dialect.SQLITE, FieldFormat.SQL), "Path not correct")
+
+ @Test
+ @DisplayName("path generates for nested qualified SQLite field")
+ def pathSQLiteNestedQualified(): Unit =
+ assertEquals("bird.data->'Nest'->>'Away'",
+ Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.SQLITE, FieldFormat.SQL),
+ "Path not correct")
+
+ @Test
+ @DisplayName("toWhere generates for exists w/o qualifier | PostgreSQL")
+ def toWhereExistsNoQualPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->>'that_field' IS NOT NULL", Field.exists("that_field").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for exists w/o qualifier | SQLite")
+ def toWhereExistsNoQualSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("data->>'that_field' IS NOT NULL", Field.exists("that_field").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for not-exists w/o qualifier | PostgreSQL")
+ def toWhereNotExistsNoQualPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->>'a_field' IS NULL", Field.notExists("a_field").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for not-exists w/o qualifier | SQLite")
+ def toWhereNotExistsNoQualSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("data->>'a_field' IS NULL", Field.notExists("a_field").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for BETWEEN w/o qualifier, numeric range | PostgreSQL")
+ def toWhereBetweenNoQualNumericPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("(data->>'age')::numeric BETWEEN @agemin AND @agemax",
+ Field.between("age", 13, 17, "@age").toWhere, "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for BETWEEN w/o qualifier, alphanumeric range | PostgreSQL")
+ def toWhereBetweenNoQualAlphaPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->>'city' BETWEEN :citymin AND :citymax",
+ Field.between("city", "Atlanta", "Chicago", ":city").toWhere, "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for BETWEEN w/o qualifier | SQLite")
+ def toWhereBetweenNoQualSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("data->>'age' BETWEEN @agemin AND @agemax", Field.between("age", 13, 17, "@age").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for BETWEEN w/ qualifier, numeric range | PostgreSQL")
+ def toWhereBetweenQualNumericPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("(test.data->>'age')::numeric BETWEEN @agemin AND @agemax",
+ Field.between("age", 13, 17, "@age").withQualifier("test").toWhere, "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for BETWEEN w/ qualifier, alphanumeric range | PostgreSQL")
+ def toWhereBetweenQualAlphaPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("unit.data->>'city' BETWEEN :citymin AND :citymax",
+ Field.between("city", "Atlanta", "Chicago", ":city").withQualifier("unit").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for BETWEEN w/ qualifier | SQLite")
+ def toWhereBetweenQualSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("my.data->>'age' BETWEEN @agemin AND @agemax",
+ Field.between("age", 13, 17, "@age").withQualifier("my").toWhere, "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for IN/any, numeric values | PostgreSQL")
+ def toWhereAnyNumericPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)",
+ Field.any("even", List(2, 4, 6).asJava, ":nbr").toWhere, "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for IN/any, alphanumeric values | PostgreSQL")
+ def toWhereAnyAlphaPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->>'test' IN (:city_0, :city_1)",
+ Field.any("test", List("Atlanta", "Chicago").asJava, ":city").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for IN/any | SQLite")
+ def toWhereAnySQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("data->>'test' IN (:city_0, :city_1)",
+ Field.any("test", List("Atlanta", "Chicago").asJava, ":city").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for inArray | PostgreSQL")
+ def toWhereInArrayPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]",
+ Field.inArray("even", "tbl", List(2, 4, 6, 8).asJava, ":it").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for inArray | SQLite")
+ def toWhereInArraySQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("EXISTS (SELECT 1 FROM json_each(tbl.data, '$.test') WHERE value IN (:city_0, :city_1))",
+ Field.inArray("test", "tbl", List("Atlanta", "Chicago").asJava, ":city").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for others w/o qualifier | PostgreSQL")
+ def toWhereOtherNoQualPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->>'some_field' = :value", Field.equal("some_field", "", ":value").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates for others w/o qualifier | SQLite")
+ def toWhereOtherNoQualSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("data->>'some_field' = :value", Field.equal("some_field", "", ":value").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates no-parameter w/ qualifier | PostgreSQL")
+ def toWhereNoParamWithQualPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("test.data->>'no_field' IS NOT NULL", Field.exists("no_field").withQualifier("test").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates no-parameter w/ qualifier | SQLite")
+ def toWhereNoParamWithQualSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("test.data->>'no_field' IS NOT NULL", Field.exists("no_field").withQualifier("test").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates parameter w/ qualifier | PostgreSQL")
+ def toWhereParamWithQualPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("(q.data->>'le_field')::numeric <= :it",
+ Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere, "Field WHERE clause not generated correctly")
+
+ @Test
+ @DisplayName("toWhere generates parameter w/ qualifier | SQLite")
+ def toWhereParamWithQualSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("q.data->>'le_field' <= :it",
+ Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere,
+ "Field WHERE clause not generated correctly")
+
+ // ~~~ STATIC CONSTRUCTOR TESTS ~~~
+
+ @Test
+ @DisplayName("equal constructs a field w/o parameter name")
+ def equalCtor(): Unit =
+ val field = Field.equal("Test", 14)
+ assertEquals("Test", field.getName, "Field name not filled correctly")
+ assertEquals(Op.EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals(14, field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("equal constructs a field w/ parameter name")
+ def equalParameterCtor(): Unit =
+ val field = Field.equal("Test", 14, ":w")
+ assertEquals("Test", field.getName, "Field name not filled correctly")
+ assertEquals(Op.EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals(14, field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertEquals(":w", field.getParameterName, "Field parameter name not filled correctly")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("greater constructs a field w/o parameter name")
+ def greaterCtor(): Unit =
+ val field = Field.greater("Great", "night")
+ assertEquals("Great", field.getName, "Field name not filled correctly")
+ assertEquals(Op.GREATER, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("night", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("greater constructs a field w/ parameter name")
+ def greaterParameterCtor(): Unit =
+ val field = Field.greater("Great", "night", ":yeah")
+ assertEquals("Great", field.getName, "Field name not filled correctly")
+ assertEquals(Op.GREATER, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("night", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertEquals(":yeah", field.getParameterName, "Field parameter name not filled correctly")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("greaterOrEqual constructs a field w/o parameter name")
+ def greaterOrEqualCtor(): Unit =
+ val field = Field.greaterOrEqual("Nice", 88L)
+ assertEquals("Nice", field.getName, "Field name not filled correctly")
+ assertEquals(Op.GREATER_OR_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals(88L, field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("greaterOrEqual constructs a field w/ parameter name")
+ def greaterOrEqualParameterCtor(): Unit =
+ val field = Field.greaterOrEqual("Nice", 88L, ":nice")
+ assertEquals("Nice", field.getName, "Field name not filled correctly")
+ assertEquals(Op.GREATER_OR_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals(88L, field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertEquals(":nice", field.getParameterName, "Field parameter name not filled correctly")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("less constructs a field w/o parameter name")
+ def lessCtor(): Unit =
+ val field = Field.less("Lesser", "seven")
+ assertEquals("Lesser", field.getName, "Field name not filled correctly")
+ assertEquals(Op.LESS, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("seven", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("less constructs a field w/ parameter name")
+ def lessParameterCtor(): Unit =
+ val field = Field.less("Lesser", "seven", ":max")
+ assertEquals("Lesser", field.getName, "Field name not filled correctly")
+ assertEquals(Op.LESS, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("seven", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertEquals(":max", field.getParameterName, "Field parameter name not filled correctly")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("lessOrEqual constructs a field w/o parameter name")
+ def lessOrEqualCtor(): Unit =
+ val field = Field.lessOrEqual("Nobody", "KNOWS")
+ assertEquals("Nobody", field.getName, "Field name not filled correctly")
+ assertEquals(Op.LESS_OR_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("KNOWS", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("lessOrEqual constructs a field w/ parameter name")
+ def lessOrEqualParameterCtor(): Unit =
+ val field = Field.lessOrEqual("Nobody", "KNOWS", ":nope")
+ assertEquals("Nobody", field.getName, "Field name not filled correctly")
+ assertEquals(Op.LESS_OR_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("KNOWS", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertEquals(":nope", field.getParameterName, "Field parameter name not filled correctly")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("notEqual constructs a field w/o parameter name")
+ def notEqualCtor(): Unit =
+ val field = Field.notEqual("Park", "here")
+ assertEquals("Park", field.getName, "Field name not filled correctly")
+ assertEquals(Op.NOT_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("here", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("notEqual constructs a field w/ parameter name")
+ def notEqualParameterCtor(): Unit =
+ val field = Field.notEqual("Park", "here", ":now")
+ assertEquals("Park", field.getName, "Field name not filled correctly")
+ assertEquals(Op.NOT_EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("here", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertEquals(":now", field.getParameterName, "Field parameter name not filled correctly")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("between constructs a field w/o parameter name")
+ def betweenCtor(): Unit =
+ val field = Field.between("Age", 18, 49)
+ assertEquals("Age", field.getName, "Field name not filled correctly")
+ assertEquals(Op.BETWEEN, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals(18, field.getComparison.getValue.getFirst, "Field comparison min value not filled correctly")
+ assertEquals(49, field.getComparison.getValue.getSecond, "Field comparison max value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("between constructs a field w/ parameter name")
+ def betweenParameterCtor(): Unit =
+ val field = Field.between("Age", 18, 49, ":limit")
+ assertEquals("Age", field.getName, "Field name not filled correctly")
+ assertEquals(Op.BETWEEN, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals(18, field.getComparison.getValue.getFirst, "Field comparison min value not filled correctly")
+ assertEquals(49, field.getComparison.getValue.getSecond, "Field comparison max value not filled correctly")
+ assertEquals(":limit", field.getParameterName, "Field parameter name not filled correctly")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("any constructs a field w/o parameter name")
+ def anyCtor(): Unit =
+ val field = Field.any("Here", List(8, 16, 32).asJava)
+ assertEquals("Here", field.getName, "Field name not filled correctly")
+ assertEquals(Op.IN, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals(List(8, 16, 32).asJava, field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("any constructs a field w/ parameter name")
+ def anyParameterCtor(): Unit =
+ val field = Field.any("Here", List(8, 16, 32).asJava, ":list")
+ assertEquals("Here", field.getName, "Field name not filled correctly")
+ assertEquals(Op.IN, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals(List(8, 16, 32).asJava, field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertEquals(":list", field.getParameterName, "Field parameter name not filled correctly")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("inArray constructs a field w/o parameter name")
+ def inArrayCtor(): Unit =
+ val field = Field.inArray("ArrayField", "table", List("z").asJava)
+ assertEquals("ArrayField", field.getName, "Field name not filled correctly")
+ assertEquals(Op.IN_ARRAY, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("table", field.getComparison.getValue.getFirst, "Field comparison table not filled correctly")
+ assertEquals(List("z").asJava, field.getComparison.getValue.getSecond,
+ "Field comparison values not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("inArray constructs a field w/ parameter name")
+ def inArrayParameterCtor(): Unit =
+ val field = Field.inArray("ArrayField", "table", List("z").asJava, ":a")
+ assertEquals("ArrayField", field.getName, "Field name not filled correctly")
+ assertEquals(Op.IN_ARRAY, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("table", field.getComparison.getValue.getFirst, "Field comparison table not filled correctly")
+ assertEquals(List("z").asJava, field.getComparison.getValue.getSecond,
+ "Field comparison values not filled correctly")
+ assertEquals(":a", field.getParameterName, "Field parameter name not filled correctly")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("exists constructs a field")
+ def existsCtor(): Unit =
+ val field = Field.exists("Groovy")
+ assertEquals("Groovy", field.getName, "Field name not filled correctly")
+ assertEquals(Op.EXISTS, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("notExists constructs a field")
+ def notExistsCtor(): Unit =
+ val field = Field.notExists("Groovy")
+ assertEquals("Groovy", field.getName, "Field name not filled correctly")
+ assertEquals(Op.NOT_EXISTS, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("named constructs a field")
+ def namedCtor(): Unit =
+ val field = Field.named("Tacos")
+ assertEquals("Tacos", field.getName, "Field name not filled correctly")
+ assertEquals(Op.EQUAL, field.getComparison.getOp, "Field comparison operation not filled correctly")
+ assertEquals("", field.getComparison.getValue, "Field comparison value not filled correctly")
+ assertNull(field.getParameterName, "The parameter name should have been null")
+ assertNull(field.getQualifier, "The qualifier should have been null")
+
+ @Test
+ @DisplayName("static constructors fail for invalid parameter name")
+ def staticCtorsFailOnParamName(): Unit =
+ assertThrows(classOf[DocumentException], () => Field.equal("a", "b", "that ain't it, Jack..."))
+
+ @Test
+ @DisplayName("nameToPath creates a simple PostgreSQL SQL name")
+ def nameToPathPostgresSimpleSQL(): Unit =
+ assertEquals("data->>'Simple'", Field.nameToPath("Simple", Dialect.POSTGRESQL, FieldFormat.SQL),
+ "Path not constructed correctly")
+
+ @Test
+ @DisplayName("nameToPath creates a simple SQLite SQL name")
+ def nameToPathSQLiteSimpleSQL(): Unit =
+ assertEquals("data->>'Simple'", Field.nameToPath("Simple", Dialect.SQLITE, FieldFormat.SQL),
+ "Path not constructed correctly")
+
+ @Test
+ @DisplayName("nameToPath creates a nested PostgreSQL SQL name")
+ def nameToPathPostgresNestedSQL(): Unit =
+ assertEquals("data#>>'{A,Long,Path,to,the,Property}'",
+ Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.SQL),
+ "Path not constructed correctly")
+
+ @Test
+ @DisplayName("nameToPath creates a nested SQLite SQL name")
+ def nameToPathSQLiteNestedSQL(): Unit =
+ assertEquals("data->'A'->'Long'->'Path'->'to'->'the'->>'Property'",
+ Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.SQL),
+ "Path not constructed correctly")
+
+ @Test
+ @DisplayName("nameToPath creates a simple PostgreSQL JSON name")
+ def nameToPathPostgresSimpleJSON(): Unit =
+ assertEquals("data->'Simple'", Field.nameToPath("Simple", Dialect.POSTGRESQL, FieldFormat.JSON),
+ "Path not constructed correctly")
+
+ @Test
+ @DisplayName("nameToPath creates a simple SQLite JSON name")
+ def nameToPathSQLiteSimpleJSON(): Unit =
+ assertEquals("data->'Simple'", Field.nameToPath("Simple", Dialect.SQLITE, FieldFormat.JSON),
+ "Path not constructed correctly")
+
+ @Test
+ @DisplayName("nameToPath creates a nested PostgreSQL JSON name")
+ def nameToPathPostgresNestedJSON(): Unit =
+ assertEquals("data#>'{A,Long,Path,to,the,Property}'",
+ Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.JSON),
+ "Path not constructed correctly")
+
+ @Test
+ @DisplayName("nameToPath creates a nested SQLite JSON name")
+ def nameToPathSQLiteNestedJSON(): Unit =
+ 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/OpSpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/OpSpec.scala
deleted file mode 100644
index aea074f..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/OpSpec.scala
+++ /dev/null
@@ -1,44 +0,0 @@
-package solutions.bitbadger.documents.scala
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.Op
-
-class OpSpec extends AnyFunSpec with Matchers {
-
- describe("sql") {
- it("returns = for EQUAL") {
- Op.EQUAL.getSql shouldEqual "="
- }
- it("returns > for GREATER") {
- Op.GREATER.getSql shouldEqual ">"
- }
- it("returns >= for GREATER_OR_EQUAL") {
- Op.GREATER_OR_EQUAL.getSql shouldEqual ">="
- }
- it("returns < for LESS") {
- Op.LESS.getSql shouldEqual "<"
- }
- it("returns <= for LESS_OR_EQUAL") {
- Op.LESS_OR_EQUAL.getSql shouldEqual "<="
- }
- it("returns <> for NOT_EQUAL") {
- Op.NOT_EQUAL.getSql shouldEqual "<>"
- }
- it("returns BETWEEN for BETWEEN") {
- Op.BETWEEN.getSql shouldEqual "BETWEEN"
- }
- it("returns IN for IN") {
- Op.IN.getSql shouldEqual "IN"
- }
- it("returns ??| for IN_ARRAY") {
- Op.IN_ARRAY.getSql shouldEqual "??|"
- }
- it("returns IS NOT NULL for EXISTS") {
- Op.EXISTS.getSql shouldEqual "IS NOT NULL"
- }
- it("returns IS NULL for NOT_EXISTS") {
- Op.NOT_EXISTS.getSql shouldEqual "IS NULL"
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/OpTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/OpTest.scala
new file mode 100644
index 0000000..62cb683
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/OpTest.scala
@@ -0,0 +1,64 @@
+package solutions.bitbadger.documents.scala
+
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.{DisplayName, Test}
+import solutions.bitbadger.documents.Op
+
+@DisplayName("JVM | Kotlin | Op")
+class OpTest {
+
+ @Test
+ @DisplayName("EQUAL uses proper SQL")
+ def equalSQL(): Unit =
+ assertEquals("=", Op.EQUAL.getSql, "The SQL for equal is incorrect")
+
+ @Test
+ @DisplayName("GREATER uses proper SQL")
+ def greaterSQL(): Unit =
+ assertEquals(">", Op.GREATER.getSql, "The SQL for greater is incorrect")
+
+ @Test
+ @DisplayName("GREATER_OR_EQUAL uses proper SQL")
+ def greaterOrEqualSQL(): Unit =
+ assertEquals(">=", Op.GREATER_OR_EQUAL.getSql, "The SQL for greater-or-equal is incorrect")
+
+ @Test
+ @DisplayName("LESS uses proper SQL")
+ def lessSQL(): Unit =
+ assertEquals("<", Op.LESS.getSql, "The SQL for less is incorrect")
+
+ @Test
+ @DisplayName("LESS_OR_EQUAL uses proper SQL")
+ def lessOrEqualSQL(): Unit =
+ assertEquals("<=", Op.LESS_OR_EQUAL.getSql, "The SQL for less-or-equal is incorrect")
+
+ @Test
+ @DisplayName("NOT_EQUAL uses proper SQL")
+ def notEqualSQL(): Unit =
+ assertEquals("<>", Op.NOT_EQUAL.getSql, "The SQL for not-equal is incorrect")
+
+ @Test
+ @DisplayName("BETWEEN uses proper SQL")
+ def betweenSQL(): Unit =
+ assertEquals("BETWEEN", Op.BETWEEN.getSql, "The SQL for between is incorrect")
+
+ @Test
+ @DisplayName("IN uses proper SQL")
+ def inSQL(): Unit =
+ assertEquals("IN", Op.IN.getSql, "The SQL for in is incorrect")
+
+ @Test
+ @DisplayName("IN_ARRAY uses proper SQL")
+ def inArraySQL(): Unit =
+ assertEquals("??|", Op.IN_ARRAY.getSql, "The SQL for in-array is incorrect")
+
+ @Test
+ @DisplayName("EXISTS uses proper SQL")
+ def existsSQL(): Unit =
+ assertEquals("IS NOT NULL", Op.EXISTS.getSql, "The SQL for exists is incorrect")
+
+ @Test
+ @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/ParameterNameSpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterNameSpec.scala
deleted file mode 100644
index 35188e8..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterNameSpec.scala
+++ /dev/null
@@ -1,23 +0,0 @@
-package solutions.bitbadger.documents.scala
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.ParameterName
-
-class ParameterNameSpec extends AnyFunSpec with Matchers {
-
- describe("derive") {
- it("works when given existing names") {
- val names = new ParameterName()
- names.derive(":taco") shouldEqual ":taco"
- names.derive(null) shouldEqual ":field0"
- }
- it("works when given all anonymous fields") {
- val names = new ParameterName()
- names.derive(null) shouldEqual ":field0"
- names.derive(null) shouldEqual ":field1"
- names.derive(null) shouldEqual ":field2"
- names.derive(null) shouldEqual ":field3"
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterNameTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterNameTest.scala
new file mode 100644
index 0000000..d58c4fa
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterNameTest.scala
@@ -0,0 +1,25 @@
+package solutions.bitbadger.documents.scala
+
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.{DisplayName, Test}
+import solutions.bitbadger.documents.ParameterName
+
+@DisplayName("JVM | Scala | ParameterName")
+class ParameterNameTest {
+
+ @Test
+ @DisplayName("derive works when given existing names")
+ def withExisting(): Unit =
+ val names = ParameterName()
+ assertEquals(":taco", names.derive(":taco"), "Name should have been :taco")
+ assertEquals(":field0", names.derive(null), "Counter should not have advanced for named field")
+
+ @Test
+ @DisplayName("derive works when given all anonymous fields")
+ def allAnonymous(): Unit =
+ val names = ParameterName()
+ assertEquals(":field0", names.derive(null), "Anonymous field name should have been returned")
+ 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/ParameterSpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterSpec.scala
deleted file mode 100644
index 3e393b7..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterSpec.scala
+++ /dev/null
@@ -1,26 +0,0 @@
-package solutions.bitbadger.documents.scala
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{DocumentException, Parameter, ParameterType}
-
-class ParameterSpec extends AnyFunSpec with Matchers {
-
- describe("constructor") {
- it("succeeds with colon-prefixed name") {
- val p = new Parameter(":test", ParameterType.STRING, "ABC")
- p.getName shouldEqual ":test"
- p.getType shouldEqual ParameterType.STRING
- p.getValue shouldEqual "ABC"
- }
- it("succeeds with at-sign-prefixed name") {
- val p = Parameter("@yo", ParameterType.NUMBER, null)
- p.getName shouldEqual "@yo"
- p.getType shouldEqual ParameterType.NUMBER
- p.getValue should be (null)
- }
- it("fails with incorrect prefix") {
- a [DocumentException] should be thrownBy Parameter("it", ParameterType.JSON, "")
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterTest.scala
new file mode 100644
index 0000000..0c3c689
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/ParameterTest.scala
@@ -0,0 +1,30 @@
+package solutions.bitbadger.documents.scala
+
+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 {
+
+ @Test
+ @DisplayName("Construction with colon-prefixed name")
+ def ctorWithColon(): Unit =
+ val p = Parameter(":test", ParameterType.STRING, "ABC")
+ assertEquals(":test", p.getName, "Parameter name was incorrect")
+ assertEquals(ParameterType.STRING, p.getType, "Parameter type was incorrect")
+ assertEquals("ABC", p.getValue, "Parameter value was incorrect")
+
+ @Test
+ @DisplayName("Construction with at-sign-prefixed name")
+ def ctorWithAtSign(): Unit =
+ val p = Parameter("@yo", ParameterType.NUMBER, null)
+ assertEquals("@yo", p.getName, "Parameter name was incorrect")
+ assertEquals(ParameterType.NUMBER, p.getType, "Parameter type was incorrect")
+ assertNull(p.getValue, "Parameter value was incorrect")
+
+ @Test
+ @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/query/CountQuerySpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/CountQuerySpec.scala
deleted file mode 100644
index 6a24e4c..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/CountQuerySpec.scala
+++ /dev/null
@@ -1,56 +0,0 @@
-package solutions.bitbadger.documents.scala.query
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{DocumentException, Field}
-import solutions.bitbadger.documents.query.CountQuery
-import solutions.bitbadger.documents.scala.ClearConfiguration
-import solutions.bitbadger.documents.support.ForceDialect
-import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
-
-import scala.jdk.CollectionConverters.*
-
-class CountQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
-
- describe("all") {
- it("generates correctly") {
- CountQuery.all(TEST_TABLE) shouldEqual s"SELECT COUNT(*) AS it FROM $TEST_TABLE"
- }
- }
-
- describe("byFields") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- CountQuery.byFields(TEST_TABLE, List(Field.equal("test", "", ":field0")).asJava) shouldEqual
- s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0"
- }
- it("generates correctly | SQLite") {
- ForceDialect.sqlite()
- CountQuery.byFields(TEST_TABLE, List(Field.equal("test", "", ":field0")).asJava) shouldEqual
- s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0"
- }
- }
-
- describe("byContains") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- CountQuery.byContains(TEST_TABLE) shouldEqual s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data @> :criteria"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy CountQuery.byContains(TEST_TABLE)
- }
- }
-
- describe("byJsonPath") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- CountQuery.byJsonPath(TEST_TABLE) shouldEqual
- s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy CountQuery.byJsonPath(TEST_TABLE)
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/CountQueryTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/CountQueryTest.scala
new file mode 100644
index 0000000..1b601b0
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/CountQueryTest.scala
@@ -0,0 +1,69 @@
+package solutions.bitbadger.documents.scala.query
+
+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.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 {
+
+ /**
+ * Clear the connection string (resets Dialect)
+ */
+ @AfterEach
+ def cleanUp(): Unit =
+ ForceDialect.none()
+
+ @Test
+ @DisplayName("all generates correctly")
+ def all(): Unit =
+ assertEquals(s"SELECT COUNT(*) AS it FROM $TEST_TABLE", CountQuery.all(TEST_TABLE),
+ "Count query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | PostgreSQL")
+ def byFieldsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0",
+ CountQuery.byFields(TEST_TABLE, List(Field.equal("test", "", ":field0")).asJava),
+ "Count query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | SQLite")
+ def byFieldsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0",
+ CountQuery.byFields(TEST_TABLE, List(Field.equal("test", "", ":field0")).asJava),
+ "Count query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains generates correctly | PostgreSQL")
+ def byContainsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data @> :criteria", CountQuery.byContains(TEST_TABLE),
+ "Count query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains fails | SQLite")
+ def byContainsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => CountQuery.byContains(TEST_TABLE))
+
+ @Test
+ @DisplayName("byJsonPath generates correctly | PostgreSQL")
+ def byJsonPathPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
+ CountQuery.byJsonPath(TEST_TABLE), "Count query not constructed correctly")
+
+ @Test
+ @DisplayName("byJsonPath fails | SQLite")
+ 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/DefinitionQuerySpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DefinitionQuerySpec.scala
deleted file mode 100644
index dbf6aad..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DefinitionQuerySpec.scala
+++ /dev/null
@@ -1,81 +0,0 @@
-package solutions.bitbadger.documents.scala.query
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{Dialect, DocumentException, DocumentIndex}
-import solutions.bitbadger.documents.query.DefinitionQuery
-import solutions.bitbadger.documents.scala.ClearConfiguration
-import solutions.bitbadger.documents.support.ForceDialect
-import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
-
-import scala.jdk.CollectionConverters.*
-
-class DefinitionQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
-
- describe("ensureTableFor") {
- it("generates correctly") {
- DefinitionQuery.ensureTableFor("my.table", "JSONB") shouldEqual
- "CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)"
- }
- }
-
- describe("ensureTable") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- DefinitionQuery.ensureTable(TEST_TABLE) shouldEqual
- s"CREATE TABLE IF NOT EXISTS $TEST_TABLE (data JSONB NOT NULL)"
- }
- it("generates correctly | SQLite") {
- ForceDialect.sqlite()
- DefinitionQuery.ensureTable(TEST_TABLE) shouldEqual
- s"CREATE TABLE IF NOT EXISTS $TEST_TABLE (data TEXT NOT NULL)"
- }
- it("fails when no dialect is set") {
- a [DocumentException] should be thrownBy DefinitionQuery.ensureTable(TEST_TABLE)
- }
- }
-
- describe("ensureKey") {
- it("generates correctly with schema") {
- DefinitionQuery.ensureKey("test.table", Dialect.POSTGRESQL) shouldEqual
- "CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'id'))"
- }
- it("generates correctly without schema") {
- DefinitionQuery.ensureKey(TEST_TABLE, Dialect.SQLITE) shouldEqual
- s"CREATE UNIQUE INDEX IF NOT EXISTS idx_${TEST_TABLE}_key ON $TEST_TABLE ((data->>'id'))"
- }
- }
-
- describe("ensureIndexOn") {
- it("generates multiple fields and directions") {
- DefinitionQuery.ensureIndexOn("test.table", "gibberish", List("taco", "guac DESC", "salsa ASC").asJava,
- Dialect.POSTGRESQL) shouldEqual
- "CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table ((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)"
- }
- it("generates nested field | PostgreSQL") {
- DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", List("a.b.c").asJava, Dialect.POSTGRESQL) shouldEqual
- s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data#>>'{a,b,c}'))"
- }
- it("generates nested field | SQLite") {
- DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", List("a.b.c").asJava, Dialect.SQLITE) shouldEqual
- s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data->'a'->'b'->>'c'))"
- }
- }
-
- describe("ensureDocumentOn") {
- it("generates Full | PostgreSQL") {
- ForceDialect.postgres()
- DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL) shouldEqual
- s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data)"
- }
- it("generates Optimized | PostgreSQL") {
- ForceDialect.postgres()
- DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.OPTIMIZED) shouldEqual
- s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data jsonb_path_ops)"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL)
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DefinitionQueryTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DefinitionQueryTest.scala
new file mode 100644
index 0000000..d963970
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DefinitionQueryTest.scala
@@ -0,0 +1,107 @@
+package solutions.bitbadger.documents.scala.query
+
+import org.junit.jupiter.api.{AfterEach, DisplayName, Test}
+import org.junit.jupiter.api.Assertions.*
+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 {
+
+ /**
+ * Clear the connection string (resets Dialect)
+ */
+ @AfterEach
+ def cleanUp(): Unit =
+ ForceDialect.none()
+
+ @Test
+ @DisplayName("ensureTableFor generates correctly")
+ def ensureTableFor(): Unit =
+ assertEquals("CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)",
+ DefinitionQuery.ensureTableFor("my.table", "JSONB"), "CREATE TABLE statement not constructed correctly")
+
+ @Test
+ @DisplayName("ensureTable generates correctly | PostgreSQL")
+ def ensureTablePostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"CREATE TABLE IF NOT EXISTS $TEST_TABLE (data JSONB NOT NULL)",
+ DefinitionQuery.ensureTable(TEST_TABLE))
+
+ @Test
+ @DisplayName("ensureTable generates correctly | SQLite")
+ def ensureTableSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"CREATE TABLE IF NOT EXISTS $TEST_TABLE (data TEXT NOT NULL)",
+ DefinitionQuery.ensureTable(TEST_TABLE))
+
+ @Test
+ @DisplayName("ensureTable fails when no dialect is set")
+ def ensureTableFailsUnknown(): Unit =
+ assertThrows(classOf[DocumentException], () => DefinitionQuery.ensureTable(TEST_TABLE))
+
+ @Test
+ @DisplayName("ensureKey generates correctly with schema")
+ def ensureKeyWithSchema(): Unit =
+ assertEquals("CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'id'))",
+ DefinitionQuery.ensureKey("test.table", Dialect.POSTGRESQL),
+ "CREATE INDEX for key statement with schema not constructed correctly")
+
+ @Test
+ @DisplayName("ensureKey generates correctly without schema")
+ def ensureKeyWithoutSchema(): Unit =
+ assertEquals(s"CREATE UNIQUE INDEX IF NOT EXISTS idx_${TEST_TABLE}_key ON $TEST_TABLE ((data->>'id'))",
+ DefinitionQuery.ensureKey(TEST_TABLE, Dialect.SQLITE),
+ "CREATE INDEX for key statement without schema not constructed correctly")
+
+ @Test
+ @DisplayName("ensureIndexOn generates multiple fields and directions")
+ def ensureIndexOnMultipleFields(): Unit =
+ assertEquals("CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table " +
+ "((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)",
+ DefinitionQuery.ensureIndexOn("test.table", "gibberish", List("taco", "guac DESC", "salsa ASC").asJava,
+ Dialect.POSTGRESQL),
+ "CREATE INDEX for multiple field statement not constructed correctly")
+
+ @Test
+ @DisplayName("ensureIndexOn generates nested field | PostgreSQL")
+ def ensureIndexOnNestedPostgres(): Unit =
+ assertEquals(s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data#>>'{a,b,c}'))",
+ DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", List("a.b.c").asJava, Dialect.POSTGRESQL),
+ "CREATE INDEX for nested PostgreSQL field incorrect")
+
+ @Test
+ @DisplayName("ensureIndexOn generates nested field | SQLite")
+ def ensureIndexOnNestedSQLite(): Unit =
+ assertEquals(s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data->'a'->'b'->>'c'))",
+ DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", List("a.b.c").asJava, Dialect.SQLITE),
+ "CREATE INDEX for nested SQLite field incorrect")
+
+ @Test
+ @DisplayName("ensureDocumentIndexOn generates Full | PostgreSQL")
+ def ensureDocumentIndexOnFullPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data)",
+ DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL),
+ "CREATE INDEX for full document index incorrect")
+
+ @Test
+ @DisplayName("ensureDocumentIndexOn generates Optimized | PostgreSQL")
+ def ensureDocumentIndexOnOptimizedPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(
+ s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data jsonb_path_ops)",
+ DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.OPTIMIZED),
+ "CREATE INDEX for optimized document index incorrect")
+
+ @Test
+ @DisplayName("ensureDocumentIndexOn fails | SQLite")
+ def ensureDocumentIndexOnFailsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException],
+ () => DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL))
+}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DeleteQuerySpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DeleteQuerySpec.scala
deleted file mode 100644
index 728d1cd..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DeleteQuerySpec.scala
+++ /dev/null
@@ -1,61 +0,0 @@
-package solutions.bitbadger.documents.scala.query
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{DocumentException, Field}
-import solutions.bitbadger.documents.query.DeleteQuery
-import solutions.bitbadger.documents.scala.ClearConfiguration
-import solutions.bitbadger.documents.support.ForceDialect
-import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
-
-import scala.jdk.CollectionConverters.*
-
-class DeleteQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
-
- describe("byId") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- DeleteQuery.byId(TEST_TABLE) shouldEqual s"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id"
- }
- it("generates correctly | SQLite") {
- ForceDialect.sqlite()
- DeleteQuery.byId(TEST_TABLE) shouldEqual s"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id"
- }
- }
-
- describe("byFields") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- DeleteQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b")).asJava) shouldEqual
- s"DELETE FROM $TEST_TABLE WHERE data->>'a' = :b"
- }
- it("generates correctly | SQLite") {
- ForceDialect.sqlite()
- DeleteQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b")).asJava) shouldEqual
- s"DELETE FROM $TEST_TABLE WHERE data->>'a' = :b"
- }
- }
-
- describe("byContains") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- DeleteQuery.byContains(TEST_TABLE) shouldEqual s"DELETE FROM $TEST_TABLE WHERE data @> :criteria"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy DeleteQuery.byContains(TEST_TABLE)
- }
- }
-
- describe("byJsonPath") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- DeleteQuery.byJsonPath(TEST_TABLE) shouldEqual
- s"DELETE FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy DeleteQuery.byJsonPath(TEST_TABLE)
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DeleteQueryTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DeleteQueryTest.scala
new file mode 100644
index 0000000..1106867
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DeleteQueryTest.scala
@@ -0,0 +1,77 @@
+package solutions.bitbadger.documents.scala.query
+
+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.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 {
+
+ /**
+ * Clear the connection string (resets Dialect)
+ */
+ @AfterEach
+ def cleanUp(): Unit =
+ ForceDialect.none()
+
+ @Test
+ @DisplayName("byId generates correctly | PostgreSQL")
+ def byIdPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id", DeleteQuery.byId(TEST_TABLE),
+ "Delete query not constructed correctly")
+
+ @Test
+ @DisplayName("byId generates correctly | SQLite")
+ def byIdSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id", DeleteQuery.byId(TEST_TABLE),
+ "Delete query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | PostgreSQL")
+ def byFieldsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"DELETE FROM $TEST_TABLE WHERE data->>'a' = :b",
+ DeleteQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b")).asJava),
+ "Delete query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | SQLite")
+ def byFieldsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"DELETE FROM $TEST_TABLE WHERE data->>'a' = :b",
+ DeleteQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b")).asJava),
+ "Delete query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains generates correctly | PostgreSQL")
+ def byContainsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"DELETE FROM $TEST_TABLE WHERE data @> :criteria", DeleteQuery.byContains(TEST_TABLE),
+ "Delete query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains fails | SQLite")
+ def byContainsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => DeleteQuery.byContains(TEST_TABLE))
+
+ @Test
+ @DisplayName("byJsonPath generates correctly | PostgreSQL")
+ def byJsonPathPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"DELETE FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
+ DeleteQuery.byJsonPath(TEST_TABLE), "Delete query not constructed correctly")
+
+ @Test
+ @DisplayName("byJsonPath fails | SQLite")
+ def byJsonPathSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => DeleteQuery.byJsonPath(TEST_TABLE))
+}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DocumentQuerySpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DocumentQuerySpec.scala
deleted file mode 100644
index adbef90..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DocumentQuerySpec.scala
+++ /dev/null
@@ -1,85 +0,0 @@
-package solutions.bitbadger.documents.scala.query
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{AutoId, Configuration, DocumentException}
-import solutions.bitbadger.documents.query.DocumentQuery
-import solutions.bitbadger.documents.scala.ClearConfiguration
-import solutions.bitbadger.documents.support.ForceDialect
-import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
-
-class DocumentQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
-
- describe("insert") {
- it("generates no auto ID | PostgreSQL") {
- ForceDialect.postgres()
- DocumentQuery.insert(TEST_TABLE) shouldEqual s"INSERT INTO $TEST_TABLE VALUES (:data)"
- }
- it("generates no auto ID | SQLite") {
- ForceDialect.sqlite()
- DocumentQuery.insert(TEST_TABLE) shouldEqual s"INSERT INTO $TEST_TABLE VALUES (:data)"
- }
- it("generates auto number | PostgreSQL") {
- ForceDialect.postgres()
- DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER) shouldEqual
- s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || ('{\"id\":' " +
- s"|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $TEST_TABLE) || '}')::jsonb)"
- }
- it("generates auto number | SQLite") {
- ForceDialect.sqlite()
- DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER) shouldEqual
- s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', " +
- s"(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $TEST_TABLE)))"
- }
- it("generates auto UUID | PostgreSQL") {
- ForceDialect.postgres()
- val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
- query should startWith (s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\"")
- query should endWith ("\"}')")
- }
- it("generates auto UUID | SQLite") {
- ForceDialect.sqlite()
- val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
- query should startWith (s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '")
- query should endWith ("'))")
- }
- it("generates auto random string | PostgreSQL") {
- try {
- ForceDialect.postgres()
- Configuration.idStringLength = 8
- val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
- query should startWith (s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\"")
- query should endWith ("\"}')")
- query.replace(s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\"", "").replace("\"}')", "")
- .length shouldEqual 8
- } finally {
- Configuration.idStringLength = 16
- }
- }
- it("insert generates auto random string | SQLite") {
- ForceDialect.sqlite()
- val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
- query should startWith (s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '")
- query should endWith ("'))")
- query.replace(s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '", "").replace("'))", "")
- .length shouldEqual Configuration.idStringLength
- }
- it("fails when no dialect is set") {
- a [DocumentException] should be thrownBy DocumentQuery.insert(TEST_TABLE)
- }
- }
-
- describe("save") {
- it("generates correctly") {
- ForceDialect.postgres()
- DocumentQuery.save(TEST_TABLE) shouldEqual
- s"INSERT INTO $TEST_TABLE VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data"
- }
- }
-
- describe("update") {
- it("generates successfully") {
- DocumentQuery.update(TEST_TABLE) shouldEqual s"UPDATE $TEST_TABLE SET data = :data"
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DocumentQueryTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DocumentQueryTest.scala
new file mode 100644
index 0000000..2410773
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/DocumentQueryTest.scala
@@ -0,0 +1,113 @@
+package solutions.bitbadger.documents.scala.query
+
+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 {
+
+ /**
+ * Clear the connection string (resets Dialect)
+ */
+ @AfterEach
+ def cleanUp(): Unit =
+ ForceDialect.none()
+
+ @Test
+ @DisplayName("insert generates no auto ID | PostgreSQL")
+ def insertNoAutoPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"INSERT INTO $TEST_TABLE VALUES (:data)", DocumentQuery.insert(TEST_TABLE))
+
+ @Test
+ @DisplayName("insert generates no auto ID | SQLite")
+ def insertNoAutoSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"INSERT INTO $TEST_TABLE VALUES (:data)", DocumentQuery.insert(TEST_TABLE))
+
+ @Test
+ @DisplayName("insert generates auto number | PostgreSQL")
+ def insertAutoNumberPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || ('{\"id\":' " +
+ s"|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $TEST_TABLE) || '}')::jsonb)",
+ DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER))
+
+ @Test
+ @DisplayName("insert generates auto number | SQLite")
+ def insertAutoNumberSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', " +
+ s"(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $TEST_TABLE)))",
+ DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER))
+
+ @Test
+ @DisplayName("insert generates auto UUID | PostgreSQL")
+ def insertAutoUUIDPostgres(): Unit =
+ ForceDialect.postgres()
+ val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
+ assertTrue(query.startsWith(s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\""),
+ s"Query start not correct (actual: $query)")
+ assertTrue(query.endsWith("\"}')"), "Query end not correct")
+
+ @Test
+ @DisplayName("insert generates auto UUID | SQLite")
+ def insertAutoUUIDSQLite(): Unit =
+ ForceDialect.sqlite()
+ val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
+ assertTrue(query.startsWith(s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '"),
+ s"Query start not correct (actual: $query)")
+ assertTrue(query.endsWith("'))"), "Query end not correct")
+
+ @Test
+ @DisplayName("insert generates auto random string | PostgreSQL")
+ def insertAutoRandomPostgres(): Unit =
+ try {
+ ForceDialect.postgres()
+ Configuration.idStringLength = 8
+ val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
+ assertTrue(query.startsWith(s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\""),
+ s"Query start not correct (actual: $query)")
+ assertTrue(query.endsWith("\"}')"), "Query end not correct")
+ assertEquals(8, query.replace(s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\"", "")
+ .replace("\"}')", "").length,
+ "Random string length incorrect")
+ } finally {
+ Configuration.idStringLength = 16
+ }
+
+ @Test
+ @DisplayName("insert generates auto random string | SQLite")
+ def insertAutoRandomSQLite(): Unit =
+ ForceDialect.sqlite()
+ val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
+ assertTrue(query.startsWith(s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '"),
+ s"Query start not correct (actual: $query)")
+ assertTrue(query.endsWith("'))"), "Query end not correct")
+ assertEquals(Configuration.idStringLength,
+ query.replace(s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '", "").replace("'))", "").length,
+ "Random string length incorrect")
+
+ @Test
+ @DisplayName("insert fails when no dialect is set")
+ def insertFailsUnknown(): Unit =
+ assertThrows(classOf[DocumentException], () => DocumentQuery.insert(TEST_TABLE))
+
+ @Test
+ @DisplayName("save generates correctly")
+ def save(): Unit =
+ ForceDialect.postgres()
+ assertEquals(
+ s"INSERT INTO $TEST_TABLE VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data",
+ DocumentQuery.save(TEST_TABLE), "INSERT ON CONFLICT UPDATE statement not constructed correctly")
+
+ @Test
+ @DisplayName("update generates successfully")
+ 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/ExistsQuerySpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/ExistsQuerySpec.scala
deleted file mode 100644
index d539543..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/ExistsQuerySpec.scala
+++ /dev/null
@@ -1,64 +0,0 @@
-package solutions.bitbadger.documents.scala.query
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{DocumentException, Field}
-import solutions.bitbadger.documents.query.ExistsQuery
-import solutions.bitbadger.documents.scala.ClearConfiguration
-import solutions.bitbadger.documents.support.ForceDialect
-import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
-
-import scala.jdk.CollectionConverters.*
-
-class ExistsQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
-
- describe("byId") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- ExistsQuery.byId(TEST_TABLE) shouldEqual
- s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it"
- }
- it("generates correctly | SQLite") {
- ForceDialect.sqlite()
- ExistsQuery.byId(TEST_TABLE) shouldEqual
- s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it"
- }
- }
-
- describe("byFields") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- ExistsQuery.byFields(TEST_TABLE, List(Field.equal("it", 7, ":test")).asJava) shouldEqual
- s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE (data->>'it')::numeric = :test) AS it"
- }
- it("generates correctly | SQLite") {
- ForceDialect.sqlite()
- ExistsQuery.byFields(TEST_TABLE, List(Field.equal("it", 7, ":test")).asJava) shouldEqual
- s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'it' = :test) AS it"
- }
- }
-
- describe("byContains") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- ExistsQuery.byContains(TEST_TABLE) shouldEqual
- s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data @> :criteria) AS it"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy ExistsQuery.byContains(TEST_TABLE)
- }
- }
-
- describe("byJsonPath") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- ExistsQuery.byJsonPath(TEST_TABLE) shouldEqual
- s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)) AS it"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy ExistsQuery.byJsonPath(TEST_TABLE)
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/ExistsQueryTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/ExistsQueryTest.scala
new file mode 100644
index 0000000..3ab3992
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/ExistsQueryTest.scala
@@ -0,0 +1,77 @@
+package solutions.bitbadger.documents.scala.query
+
+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 {
+
+ /**
+ * Clear the connection string (resets Dialect)
+ */
+ @AfterEach
+ def cleanUp(): Unit =
+ ForceDialect.none()
+
+ @Test
+ @DisplayName("byId generates correctly | PostgreSQL")
+ def byIdPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it",
+ ExistsQuery.byId(TEST_TABLE), "Exists query not constructed correctly")
+
+ @Test
+ @DisplayName("byId generates correctly | SQLite")
+ def byIdSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it",
+ ExistsQuery.byId(TEST_TABLE), "Exists query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | PostgreSQL")
+ def byFieldsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE (data->>'it')::numeric = :test) AS it",
+ ExistsQuery.byFields(TEST_TABLE, List(Field.equal("it", 7, ":test")).asJava),
+ "Exists query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | SQLite")
+ def byFieldsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'it' = :test) AS it",
+ ExistsQuery.byFields(TEST_TABLE, List(Field.equal("it", 7, ":test")).asJava),
+ "Exists query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains generates correctly | PostgreSQL")
+ def byContainsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data @> :criteria) AS it",
+ ExistsQuery.byContains(TEST_TABLE), "Exists query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains fails | SQLite")
+ def byContainsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => ExistsQuery.byContains(TEST_TABLE))
+
+ @Test
+ @DisplayName("byJsonPath generates correctly | PostgreSQL")
+ def byJsonPathPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)) AS it",
+ ExistsQuery.byJsonPath(TEST_TABLE), "Exists query not constructed correctly")
+
+ @Test
+ @DisplayName("byJsonPath fails | SQLite")
+ def byJsonPathSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => ExistsQuery.byJsonPath(TEST_TABLE))
+}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/FindQuerySpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/FindQuerySpec.scala
deleted file mode 100644
index 5d8a585..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/FindQuerySpec.scala
+++ /dev/null
@@ -1,67 +0,0 @@
-package solutions.bitbadger.documents.scala.query
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{DocumentException, Field}
-import solutions.bitbadger.documents.query.FindQuery
-import solutions.bitbadger.documents.scala.ClearConfiguration
-import solutions.bitbadger.documents.support.ForceDialect
-import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
-
-import scala.jdk.CollectionConverters.*
-
-class FindQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
-
- describe("all") {
- it("generates correctly") {
- FindQuery.all(TEST_TABLE) shouldEqual s"SELECT data FROM $TEST_TABLE"
- }
- }
-
- describe("byId") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- FindQuery.byId(TEST_TABLE) shouldEqual s"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id"
- }
- it("generates correctly | SQLite") {
- ForceDialect.sqlite()
- FindQuery.byId(TEST_TABLE) shouldEqual s"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id"
- }
- }
-
- describe("byFields") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- FindQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b"), Field.less("c", 14, ":d")).asJava) shouldEqual
- s"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND (data->>'c')::numeric < :d"
- }
- it("generates correctly | SQLite") {
- ForceDialect.sqlite()
- FindQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b"), Field.less("c", 14, ":d")).asJava) shouldEqual
- s"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND data->>'c' < :d"
- }
- }
-
- describe("byContains") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- FindQuery.byContains(TEST_TABLE) shouldEqual s"SELECT data FROM $TEST_TABLE WHERE data @> :criteria"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy FindQuery.byContains(TEST_TABLE)
- }
- }
-
- describe("byJsonPath") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- FindQuery.byJsonPath(TEST_TABLE) shouldEqual
- s"SELECT data FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy FindQuery.byJsonPath(TEST_TABLE)
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/FindQueryTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/FindQueryTest.scala
new file mode 100644
index 0000000..06e1586
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/FindQueryTest.scala
@@ -0,0 +1,82 @@
+package solutions.bitbadger.documents.scala.query
+
+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.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 {
+
+ /**
+ * Clear the connection string (resets Dialect)
+ */
+ @AfterEach
+ def cleanUp(): Unit =
+ ForceDialect.none()
+
+ @Test
+ @DisplayName("all generates correctly")
+ def all(): Unit =
+ assertEquals(s"SELECT data FROM $TEST_TABLE", FindQuery.all(TEST_TABLE), "Find query not constructed correctly")
+
+ @Test
+ @DisplayName("byId generates correctly | PostgreSQL")
+ def byIdPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id", FindQuery.byId(TEST_TABLE),
+ "Find query not constructed correctly")
+
+ @Test
+ @DisplayName("byId generates correctly | SQLite")
+ def byIdSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id", FindQuery.byId(TEST_TABLE),
+ "Find query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | PostgreSQL")
+ def byFieldsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND (data->>'c')::numeric < :d",
+ FindQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b"), Field.less("c", 14, ":d")).asJava),
+ "Find query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | SQLite")
+ def byFieldsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND data->>'c' < :d",
+ FindQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b"), Field.less("c", 14, ":d")).asJava),
+ "Find query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains generates correctly | PostgreSQL")
+ def byContainsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT data FROM $TEST_TABLE WHERE data @> :criteria", FindQuery.byContains(TEST_TABLE),
+ "Find query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains fails | SQLite")
+ def byContainsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => FindQuery.byContains(TEST_TABLE))
+
+ @Test
+ @DisplayName("byJsonPath generates correctly | PostgreSQL")
+ def byJsonPathPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"SELECT data FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
+ FindQuery.byJsonPath(TEST_TABLE), "Find query not constructed correctly")
+
+ @Test
+ @DisplayName("byJsonPath fails | SQLite")
+ def byJsonPathSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => FindQuery.byJsonPath(TEST_TABLE))
+}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/PatchQuerySpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/PatchQuerySpec.scala
deleted file mode 100644
index a37a8b3..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/PatchQuerySpec.scala
+++ /dev/null
@@ -1,63 +0,0 @@
-package solutions.bitbadger.documents.scala.query
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{DocumentException, Field}
-import solutions.bitbadger.documents.query.PatchQuery
-import solutions.bitbadger.documents.scala.ClearConfiguration
-import solutions.bitbadger.documents.support.ForceDialect
-import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
-
-import scala.jdk.CollectionConverters.*
-
-class PatchQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
-
- describe("byId") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- PatchQuery.byId(TEST_TABLE) shouldEqual s"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'id' = :id"
- }
- it("generates correctly | SQLite") {
- ForceDialect.sqlite()
- PatchQuery.byId(TEST_TABLE) shouldEqual
- s"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id"
- }
- }
-
- describe("byFields") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- PatchQuery.byFields(TEST_TABLE, List(Field.equal("z", "", ":y")).asJava) shouldEqual
- s"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'z' = :y"
- }
- it("generates correctly | SQLite") {
- ForceDialect.sqlite()
- PatchQuery.byFields(TEST_TABLE, List(Field.equal("z", "", ":y")).asJava) shouldEqual
- s"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y"
- }
- }
-
- describe("byContains") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- PatchQuery.byContains(TEST_TABLE) shouldEqual
- s"UPDATE $TEST_TABLE SET data = data || :data WHERE data @> :criteria"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy PatchQuery.byContains(TEST_TABLE)
- }
- }
-
- describe("byJsonPath") {
- it("generates correctly | PostgreSQL") {
- ForceDialect.postgres()
- PatchQuery.byJsonPath(TEST_TABLE) shouldEqual
- s"UPDATE $TEST_TABLE SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)"
- }
- it("fails | SQLite") {
- ForceDialect.sqlite()
- a [DocumentException] should be thrownBy PatchQuery.byJsonPath(TEST_TABLE)
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/PatchQueryTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/PatchQueryTest.scala
new file mode 100644
index 0000000..2741f2d
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/PatchQueryTest.scala
@@ -0,0 +1,75 @@
+package solutions.bitbadger.documents.scala.query
+
+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 {
+
+ /**
+ * Reset the dialect
+ */
+ @AfterEach
+ def cleanUp(): Unit =
+ ForceDialect.none()
+
+ @Test
+ @DisplayName("byId generates correctly | PostgreSQL")
+ def byIdPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'id' = :id", PatchQuery.byId(TEST_TABLE),
+ "Patch query not constructed correctly")
+
+ @Test
+ @DisplayName("byId generates correctly | SQLite")
+ def byIdSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id",
+ PatchQuery.byId(TEST_TABLE), "Patch query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | PostgreSQL")
+ def byFieldsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'z' = :y",
+ PatchQuery.byFields(TEST_TABLE, List(Field.equal("z", "", ":y")).asJava), "Patch query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | SQLite")
+ def byFieldsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y",
+ PatchQuery.byFields(TEST_TABLE, List(Field.equal("z", "", ":y")).asJava), "Patch query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains generates correctly | PostgreSQL")
+ def byContainsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = data || :data WHERE data @> :criteria",
+ PatchQuery.byContains(TEST_TABLE), "Patch query not constructed correctly" )
+
+ @Test
+ @DisplayName("byContains fails | SQLite")
+ def byContainsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => PatchQuery.byContains(TEST_TABLE))
+
+ @Test
+ @DisplayName("byJsonPath generates correctly | PostgreSQL")
+ def byJsonPathPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)",
+ PatchQuery.byJsonPath(TEST_TABLE), "Patch query not constructed correctly")
+
+ @Test
+ @DisplayName("byJsonPath fails | SQLite")
+ 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/QueryUtilsSpec.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/QueryUtilsSpec.scala
deleted file mode 100644
index 0b58169..0000000
--- a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/QueryUtilsSpec.scala
+++ /dev/null
@@ -1,103 +0,0 @@
-package solutions.bitbadger.documents.scala.query
-
-import org.scalatest.funspec.AnyFunSpec
-import org.scalatest.matchers.should.Matchers
-import solutions.bitbadger.documents.{Dialect, Field, FieldMatch}
-import solutions.bitbadger.documents.query.QueryUtils
-import solutions.bitbadger.documents.scala.ClearConfiguration
-import solutions.bitbadger.documents.support.ForceDialect
-
-import scala.jdk.CollectionConverters.*
-
-class QueryUtilsSpec extends AnyFunSpec with ClearConfiguration with Matchers {
-
- describe("statementWhere") {
- it("generates correctly") {
- QueryUtils.statementWhere("x", "y") shouldEqual "x WHERE y"
- }
- }
-
- describe("byId") {
- it("generates a numeric ID query | PostgreSQL") {
- ForceDialect.postgres()
- QueryUtils.byId("test", 9) shouldEqual "test WHERE (data->>'id')::numeric = :id"
- }
- it("generates an alphanumeric ID query | PostgreSQL") {
- ForceDialect.postgres()
- QueryUtils.byId("unit", "18") shouldEqual "unit WHERE data->>'id' = :id"
- }
- it("generates ID query | SQLite") {
- ForceDialect.sqlite()
- QueryUtils.byId("yo", 27) shouldEqual "yo WHERE data->>'id' = :id"
- }
- }
-
- describe("byFields") {
- it("generates default field query | PostgreSQL") {
- ForceDialect.postgres()
- QueryUtils.byFields("this",
- List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava) shouldEqual
- "this WHERE data->>'a' = :the_a AND (data->>'b')::numeric = :b_value"
- }
- it("generates default field query | SQLite") {
- ForceDialect.sqlite()
- QueryUtils.byFields("this",
- List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava) shouldEqual
- "this WHERE data->>'a' = :the_a AND data->>'b' = :b_value"
- }
- it("generates ANY field query | PostgreSQL") {
- ForceDialect.postgres()
- QueryUtils.byFields("that", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava,
- FieldMatch.ANY) shouldEqual
- "that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value"
- }
- it("generates ANY field query | SQLite") {
- ForceDialect.sqlite()
- QueryUtils.byFields("that", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava,
- FieldMatch.ANY) shouldEqual
- "that WHERE data->>'a' = :the_a OR data->>'b' = :b_value"
- }
- }
-
- describe("orderBy") {
- it("generates for no fields") {
- QueryUtils.orderBy(List().asJava, Dialect.POSTGRESQL) shouldEqual ""
- QueryUtils.orderBy(List().asJava, Dialect.SQLITE) shouldEqual ""
- }
- it("generates single, no direction | PostgreSQL") {
- QueryUtils.orderBy(List(Field.named("TestField")).asJava, Dialect.POSTGRESQL) shouldEqual
- " ORDER BY data->>'TestField'"
- }
- it("generates single, no direction | SQLite") {
- QueryUtils.orderBy(List(Field.named("TestField")).asJava, Dialect.SQLITE) shouldEqual
- " ORDER BY data->>'TestField'"
- }
- it("generates multiple with direction | PostgreSQL") {
- QueryUtils.orderBy(
- List(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")).asJava,
- Dialect.POSTGRESQL) shouldEqual
- " ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC"
- }
- it("generates multiple with direction | SQLite") {
- QueryUtils.orderBy(
- List(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")).asJava,
- Dialect.SQLITE) shouldEqual
- " ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC"
- }
- it("generates numeric ordering | PostgreSQL") {
- QueryUtils.orderBy(List(Field.named("n:Test")).asJava, Dialect.POSTGRESQL) shouldEqual
- " ORDER BY (data->>'Test')::numeric"
- }
- it("generates numeric ordering | SQLite") {
- QueryUtils.orderBy(List(Field.named("n:Test")).asJava, Dialect.SQLITE) shouldEqual " ORDER BY data->>'Test'"
- }
- it("generates case-insensitive ordering | PostgreSQL") {
- QueryUtils.orderBy(List(Field.named("i:Test.Field DESC NULLS FIRST")).asJava, Dialect.POSTGRESQL) shouldEqual
- " ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST"
- }
- it("generates case-insensitive ordering | SQLite") {
- QueryUtils.orderBy(List(Field.named("i:Test.Field ASC NULLS LAST")).asJava, Dialect.SQLITE) shouldEqual
- " ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST"
- }
- }
-}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/QueryUtilsTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/QueryUtilsTest.scala
new file mode 100644
index 0000000..3076708
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/QueryUtilsTest.scala
@@ -0,0 +1,138 @@
+package solutions.bitbadger.documents.scala.query
+
+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 {
+
+ /**
+ * Clear the connection string (resets Dialect)
+ */
+ @AfterEach
+ def cleanUp(): Unit =
+ ForceDialect.none()
+
+ @Test
+ @DisplayName("statementWhere generates correctly")
+ def statementWhere(): Unit =
+ assertEquals("x WHERE y", QueryUtils.statementWhere("x", "y"), "Statements not combined correctly")
+
+ @Test
+ @DisplayName("byId generates a numeric ID query | PostgreSQL")
+ def byIdNumericPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("test WHERE (data->>'id')::numeric = :id", QueryUtils.byId("test", 9))
+
+ @Test
+ @DisplayName("byId generates an alphanumeric ID query | PostgreSQL")
+ def byIdAlphaPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("unit WHERE data->>'id' = :id", QueryUtils.byId("unit", "18"))
+
+ @Test
+ @DisplayName("byId generates ID query | SQLite")
+ def byIdSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("yo WHERE data->>'id' = :id", QueryUtils.byId("yo", 27))
+
+ @Test
+ @DisplayName("byFields generates default field query | PostgreSQL")
+ def byFieldsMultipleDefaultPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("this WHERE data->>'a' = :the_a AND (data->>'b')::numeric = :b_value",
+ QueryUtils.byFields("this", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava))
+
+ @Test
+ @DisplayName("byFields generates default field query | SQLite")
+ def byFieldsMultipleDefaultSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("this WHERE data->>'a' = :the_a AND data->>'b' = :b_value",
+ QueryUtils.byFields("this", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava))
+
+ @Test
+ @DisplayName("byFields generates ANY field query | PostgreSQL")
+ def byFieldsMultipleAnyPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value",
+ QueryUtils.byFields("that", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava,
+ FieldMatch.ANY))
+
+ @Test
+ @DisplayName("byFields generates ANY field query | SQLite")
+ def byFieldsMultipleAnySQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("that WHERE data->>'a' = :the_a OR data->>'b' = :b_value",
+ QueryUtils.byFields("that", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava,
+ FieldMatch.ANY))
+
+ @Test
+ @DisplayName("orderBy generates for no fields")
+ def orderByNone(): Unit =
+ assertEquals("", QueryUtils.orderBy(List().asJava, Dialect.POSTGRESQL),
+ "ORDER BY should have been blank (PostgreSQL)")
+ assertEquals("", QueryUtils.orderBy(List().asJava, Dialect.SQLITE), "ORDER BY should have been blank (SQLite)")
+
+ @Test
+ @DisplayName("orderBy generates single, no direction | PostgreSQL")
+ def orderBySinglePostgres(): Unit =
+ assertEquals(" ORDER BY data->>'TestField'",
+ QueryUtils.orderBy(List(Field.named("TestField")).asJava, Dialect.POSTGRESQL),
+ "ORDER BY not constructed correctly")
+
+ @Test
+ @DisplayName("orderBy generates single, no direction | SQLite")
+ def orderBySingleSQLite(): Unit =
+ assertEquals(" ORDER BY data->>'TestField'",
+ QueryUtils.orderBy(List(Field.named("TestField")).asJava, Dialect.SQLITE),
+ "ORDER BY not constructed correctly")
+
+ @Test
+ @DisplayName("orderBy generates multiple with direction | PostgreSQL")
+ def orderByMultiplePostgres(): Unit =
+ assertEquals(" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
+ QueryUtils.orderBy(
+ List(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")).asJava,
+ Dialect.POSTGRESQL),
+ "ORDER BY not constructed correctly")
+
+ @Test
+ @DisplayName("orderBy generates multiple with direction | SQLite")
+ def orderByMultipleSQLite(): Unit =
+ assertEquals(" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
+ QueryUtils.orderBy(
+ List(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")).asJava,
+ Dialect.SQLITE),
+ "ORDER BY not constructed correctly")
+
+ @Test
+ @DisplayName("orderBy generates numeric ordering | PostgreSQL")
+ def orderByNumericPostgres(): Unit =
+ assertEquals(" ORDER BY (data->>'Test')::numeric",
+ QueryUtils.orderBy(List(Field.named("n:Test")).asJava, Dialect.POSTGRESQL), "ORDER BY not constructed correctly")
+
+ @Test
+ @DisplayName("orderBy generates numeric ordering | SQLite")
+ def orderByNumericSQLite(): Unit =
+ assertEquals(" ORDER BY data->>'Test'", QueryUtils.orderBy(List(Field.named("n:Test")).asJava, Dialect.SQLITE),
+ "ORDER BY not constructed correctly")
+
+ @Test
+ @DisplayName("orderBy generates case-insensitive ordering | PostgreSQL")
+ def orderByCIPostgres(): Unit =
+ assertEquals(" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST",
+ QueryUtils.orderBy(List(Field.named("i:Test.Field DESC NULLS FIRST")).asJava, Dialect.POSTGRESQL),
+ "ORDER BY not constructed correctly")
+
+ @Test
+ @DisplayName("orderBy generates case-insensitive ordering | SQLite")
+ def orderByCISQLite(): Unit =
+ 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/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/RemoveFieldsQueryTest.scala
new file mode 100644
index 0000000..e5e93e0
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/RemoveFieldsQueryTest.scala
@@ -0,0 +1,85 @@
+package solutions.bitbadger.documents.scala.query
+
+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 {
+
+ /**
+ * Reset the dialect
+ */
+ @AfterEach
+ def cleanUp(): Unit =
+ ForceDialect.none()
+
+ @Test
+ @DisplayName("byId generates correctly | PostgreSQL")
+ def byIdPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data->>'id' = :id",
+ RemoveFieldsQuery.byId(TEST_TABLE, List(Parameter(":name", ParameterType.STRING, "{a,z}")).asJava),
+ "Remove Fields query not constructed correctly")
+
+ @Test
+ @DisplayName("byId generates correctly | SQLite")
+ def byIdSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = json_remove(data, :name0, :name1) WHERE data->>'id' = :id",
+ RemoveFieldsQuery.byId(TEST_TABLE,
+ List(Parameter(":name0", ParameterType.STRING, "a"),Parameter(":name1", ParameterType.STRING, "z")).asJava),
+ "Remove Field query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | PostgreSQL")
+ def byFieldsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data->>'f' > :g",
+ RemoveFieldsQuery.byFields(TEST_TABLE, List(Parameter(":name", ParameterType.STRING, "{b,c}")).asJava,
+ List(Field.greater("f", "", ":g")).asJava),
+ "Remove Field query not constructed correctly")
+
+ @Test
+ @DisplayName("byFields generates correctly | SQLite")
+ def byFieldsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = json_remove(data, :name0, :name1) WHERE data->>'f' > :g",
+ RemoveFieldsQuery.byFields(TEST_TABLE,
+ List(Parameter(":name0", ParameterType.STRING, "b"), Parameter(":name1", ParameterType.STRING, "c")).asJava,
+ List(Field.greater("f", "", ":g")).asJava),
+ "Remove Field query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains generates correctly | PostgreSQL")
+ def byContainsPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE data @> :criteria",
+ RemoveFieldsQuery.byContains(TEST_TABLE, List(Parameter(":name", ParameterType.STRING, "{m,n}")).asJava),
+ "Remove Field query not constructed correctly")
+
+ @Test
+ @DisplayName("byContains fails | SQLite")
+ def byContainsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => RemoveFieldsQuery.byContains(TEST_TABLE, List().asJava))
+
+ @Test
+ @DisplayName("byJsonPath generates correctly | PostgreSQL")
+ def byJsonPathPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals(s"UPDATE $TEST_TABLE SET data = data - :name::text[] WHERE jsonb_path_exists(data, :path::jsonpath)",
+ RemoveFieldsQuery.byJsonPath(TEST_TABLE, List(Parameter(":name", ParameterType.STRING, "{o,p}")).asJava),
+ "Remove Field query not constructed correctly")
+
+ @Test
+ @DisplayName("byJsonPath fails | SQLite")
+ def byJsonPathSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => RemoveFieldsQuery.byJsonPath(TEST_TABLE, List().asJava))
+}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/WhereTest.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/WhereTest.scala
new file mode 100644
index 0000000..26acf2c
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/query/WhereTest.scala
@@ -0,0 +1,143 @@
+package solutions.bitbadger.documents.scala.query
+
+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 {
+
+ /**
+ * Clear the connection string (resets Dialect)
+ */
+ @AfterEach
+ def cleanUp (): Unit =
+ ForceDialect.none()
+
+ @Test
+ @DisplayName("byFields is blank when given no fields")
+ def byFieldsBlankIfEmpty(): Unit =
+ assertEquals("", Where.byFields(List().asJava))
+
+ @Test
+ @DisplayName("byFields generates one numeric field | PostgreSQL")
+ def byFieldsOneFieldPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("(data->>'it')::numeric = :that", Where.byFields(List(Field.equal("it", 9, ":that")).asJava))
+
+ @Test
+ @DisplayName("byFields generates one alphanumeric field | PostgreSQL")
+ def byFieldsOneAlphaFieldPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->>'it' = :that", Where.byFields(List(Field.equal("it", "", ":that")).asJava))
+
+ @Test
+ @DisplayName("byFields generates one field | SQLite")
+ def byFieldsOneFieldSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("data->>'it' = :that", Where.byFields(List(Field.equal("it", "", ":that")).asJava))
+
+ @Test
+ @DisplayName("byFields generates multiple fields w/ default match | PostgreSQL")
+ def byFieldsMultipleDefaultPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->>'1' = :one AND (data->>'2')::numeric = :two AND data->>'3' = :three",
+ Where.byFields(
+ List(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")).asJava))
+
+ @Test
+ @DisplayName("byFields generates multiple fields w/ default match | SQLite")
+ def byFieldsMultipleDefaultSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("data->>'1' = :one AND data->>'2' = :two AND data->>'3' = :three",
+ Where.byFields(
+ List(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")).asJava))
+
+ @Test
+ @DisplayName("byFields generates multiple fields w/ ANY match | PostgreSQL")
+ def byFieldsMultipleAnyPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->>'1' = :one OR (data->>'2')::numeric = :two OR data->>'3' = :three",
+ Where.byFields(
+ List(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")).asJava,
+ FieldMatch.ANY))
+
+ @Test
+ @DisplayName("byFields generates multiple fields w/ ANY match | SQLite")
+ def byFieldsMultipleAnySQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("data->>'1' = :one OR data->>'2' = :two OR data->>'3' = :three",
+ Where.byFields(
+ List(Field.equal("1", "", ":one"), Field.equal("2", 0L, ":two"), Field.equal("3", "", ":three")).asJava,
+ FieldMatch.ANY))
+
+ @Test
+ @DisplayName("byId generates defaults for alphanumeric key | PostgreSQL")
+ def byIdDefaultAlphaPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->>'id' = :id", Where.byId())
+
+ @Test
+ @DisplayName("byId generates defaults for numeric key | PostgreSQL")
+ def byIdDefaultNumericPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("(data->>'id')::numeric = :id", Where.byId(":id", 5))
+
+ @Test
+ @DisplayName("byId generates defaults | SQLite")
+ def byIdDefaultSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("data->>'id' = :id", Where.byId())
+
+ @Test
+ @DisplayName("byId generates named ID | PostgreSQL")
+ def byIdDefaultNamedPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data->>'id' = :key", Where.byId(":key"))
+
+ @Test
+ @DisplayName("byId generates named ID | SQLite")
+ def byIdDefaultNamedSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertEquals("data->>'id' = :key", Where.byId(":key"))
+
+ @Test
+ @DisplayName("jsonContains generates defaults | PostgreSQL")
+ def jsonContainsDefaultPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data @> :criteria", Where.jsonContains())
+
+ @Test
+ @DisplayName("jsonContains generates named parameter | PostgreSQL")
+ def jsonContainsNamedPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("data @> :it", Where.jsonContains(":it"))
+
+ @Test
+ @DisplayName("jsonContains fails | SQLite")
+ def jsonContainsFailsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => Where.jsonContains())
+
+ @Test
+ @DisplayName("jsonPathMatches generates defaults | PostgreSQL")
+ def jsonPathMatchDefaultPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("jsonb_path_exists(data, :path::jsonpath)", Where.jsonPathMatches())
+
+ @Test
+ @DisplayName("jsonPathMatches generates named parameter | PostgreSQL")
+ def jsonPathMatchNamedPostgres(): Unit =
+ ForceDialect.postgres()
+ assertEquals("jsonb_path_exists(data, :jp::jsonpath)", Where.jsonPathMatches(":jp"))
+
+ @Test
+ @DisplayName("jsonPathMatches fails | SQLite")
+ def jsonPathFailsSQLite(): Unit =
+ ForceDialect.sqlite()
+ assertThrows(classOf[DocumentException], () => Where.jsonPathMatches())
+}
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/JsonDocument.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/JsonDocument.scala
new file mode 100644
index 0000000..861eeab
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/JsonDocument.scala
@@ -0,0 +1,20 @@
+package solutions.bitbadger.documents.scala.support
+
+import solutions.bitbadger.documents.jvm.Document
+import solutions.bitbadger.documents.support.ThrowawayDatabase
+import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
+
+class JsonDocument(val id: String = "", val value: String = "", val numValue: Int = 0, val sub: SubDocument = null)
+
+object JsonDocument:
+
+ /** Documents to use for testing */
+ private val testDocuments = List(
+ JsonDocument("one", "FIRST!", 0, null),
+ JsonDocument("two", "another", 10, SubDocument("green", "blue")),
+ JsonDocument("three", "", 4, null),
+ JsonDocument("four", "purple", 17, SubDocument("green", "red")),
+ JsonDocument("five", "purple", 18, null))
+
+ def load(db: ThrowawayDatabase, tableName: String = TEST_TABLE): Unit =
+ testDocuments.foreach { it => Document.insert(tableName, it, db.getConn) }
diff --git a/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/SubDocument.scala b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/SubDocument.scala
new file mode 100644
index 0000000..9ec17d5
--- /dev/null
+++ b/src/jvm/src/test/scala/solutions/bitbadger/documents/scala/support/SubDocument.scala
@@ -0,0 +1,3 @@
+package solutions.bitbadger.documents.scala.support
+
+class SubDocument(val foo: String = "", val bar: String = "")