Initial Development #1

Merged
danieljsummers merged 88 commits from v1-rc into main 2025-04-16 01:29:20 +00:00
45 changed files with 2247 additions and 1321 deletions
Showing only changes of commit ac1a3940b1 - Show all commits

View File

@ -0,0 +1,26 @@
<component name="libraryTable">
<library name="Maven: scala-sdk-3.1.3" type="Scala">
<properties>
<language-level>Scala_3_1</language-level>
<compiler-classpath>
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-compiler_3/3.1.3/scala3-compiler_3-3.1.3.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-interfaces/3.1.3/scala3-interfaces-3.1.3.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-library_3/3.1.3/scala3-library_3-3.1.3.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala-library/2.13.8/scala-library-2.13.8.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/tasty-core_3/3.1.3/tasty-core_3-3.1.3.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/modules/scala-asm/9.2.0-scala-1/scala-asm-9.2.0-scala-1.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-sbt/compiler-interface/1.3.5/compiler-interface-1.3.5.jar" />
<root url="file://$MAVEN_REPOSITORY$/com/google/protobuf/protobuf-java/3.7.0/protobuf-java-3.7.0.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-sbt/util-interface/1.3.0/util-interface-1.3.0.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/jline/jline-reader/3.19.0/jline-reader-3.19.0.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/jline/jline-terminal/3.19.0/jline-terminal-3.19.0.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/jline/jline-terminal-jna/3.19.0/jline-terminal-jna-3.19.0.jar" />
<root url="file://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar" />
</compiler-classpath>
<compiler-bridge-binary-jar>file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-sbt-bridge/3.1.3/scala3-sbt-bridge-3.1.3.jar</compiler-bridge-binary-jar>
</properties>
<CLASSES />
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -0,0 +1,25 @@
<component name="libraryTable">
<library name="Maven: scala-sdk-3.3.3" type="Scala">
<properties>
<language-level>Scala_3_3</language-level>
<compiler-classpath>
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-compiler_3/3.3.3/scala3-compiler_3-3.3.3.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-interfaces/3.3.3/scala3-interfaces-3.3.3.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-library_3/3.3.3/scala3-library_3-3.3.3.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala-library/2.13.12/scala-library-2.13.12.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/tasty-core_3/3.3.3/tasty-core_3-3.3.3.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/modules/scala-asm/9.5.0-scala-1/scala-asm-9.5.0-scala-1.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-sbt/compiler-interface/1.9.3/compiler-interface-1.9.3.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-sbt/util-interface/1.9.2/util-interface-1.9.2.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/jline/jline-reader/3.19.0/jline-reader-3.19.0.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/jline/jline-terminal/3.19.0/jline-terminal-3.19.0.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/jline/jline-terminal-jna/3.19.0/jline-terminal-jna-3.19.0.jar" />
<root url="file://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.3.1/jna-5.3.1.jar" />
</compiler-classpath>
<compiler-bridge-binary-jar>file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-sbt-bridge/3.3.3/scala3-sbt-bridge-3.3.3.jar</compiler-bridge-binary-jar>
</properties>
<CLASSES />
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -0,0 +1,26 @@
<component name="libraryTable">
<library name="Maven: scala-sdk-3.5.2" type="Scala">
<properties>
<language-level>Scala_3_5</language-level>
<compiler-classpath>
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-compiler_3/3.5.2/scala3-compiler_3-3.5.2.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-interfaces/3.5.2/scala3-interfaces-3.5.2.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-library_3/3.5.2/scala3-library_3-3.5.2.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/scala-library/2.13.14/scala-library-2.13.14.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/tasty-core_3/3.5.2/tasty-core_3-3.5.2.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-lang/modules/scala-asm/9.7.0-scala-2/scala-asm-9.7.0-scala-2.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-sbt/compiler-interface/1.9.6/compiler-interface-1.9.6.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/scala-sbt/util-interface/1.9.8/util-interface-1.9.8.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/jline/jline-reader/3.25.1/jline-reader-3.25.1.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/jline/jline-terminal/3.25.1/jline-terminal-3.25.1.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/jline/jline-native/3.25.1/jline-native-3.25.1.jar" />
<root url="file://$MAVEN_REPOSITORY$/org/jline/jline-terminal-jna/3.25.1/jline-terminal-jna-3.25.1.jar" />
<root url="file://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.14.0/jna-5.14.0.jar" />
</compiler-classpath>
<compiler-bridge-binary-jar>file://$MAVEN_REPOSITORY$/org/scala-lang/scala3-sbt-bridge/3.5.2/scala3-sbt-bridge-3.5.2.jar</compiler-bridge-binary-jar>
</properties>
<CLASSES />
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -37,9 +37,9 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.scalatest</groupId> <groupId>org.scala-lang</groupId>
<artifactId>scalatest_3</artifactId> <artifactId>scala3-library_3</artifactId>
<version>3.2.9</version> <version>3.5.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -134,24 +134,6 @@
<argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine> <argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.scalatest</groupId>
<artifactId>scalatest-maven-plugin</artifactId>
<version>2.2.0</version>
<configuration>
<reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory>
<junitxml>.</junitxml>
<filereports>WDF TestSuite.txt</filereports>
</configuration>
<executions>
<execution>
<id>test</id>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<artifactId>maven-failsafe-plugin</artifactId> <artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version> <version>2.22.2</version>

View File

@ -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()) }
// }
}

View File

@ -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() }
// }
}

View File

@ -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")
}
}
}

View File

@ -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"))
}

View File

@ -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()
}

View File

@ -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)
}
}
}
}

View File

@ -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)
}
}

View File

@ -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]")
}
}
}
}

View File

@ -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")
}
}

View File

@ -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"
}
}
}

View File

@ -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")
}

View File

@ -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"
}
}
}

View File

@ -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")
}

View File

@ -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'"
}
}
}

View File

@ -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")
}

View File

@ -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"
}
}
}

View File

@ -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")
}

View File

@ -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"
}
}
}

View File

@ -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")
}

View File

@ -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, "")
}
}
}

View File

@ -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, ""))
}

View File

@ -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)
}
}
}

View File

@ -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))
}

View File

@ -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)
}
}
}

View File

@ -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))
}

View File

@ -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)
}
}
}

View File

@ -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))
}

View File

@ -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"
}
}
}

View File

@ -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")
}

View File

@ -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)
}
}
}

View File

@ -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))
}

View File

@ -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)
}
}
}

View File

@ -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))
}

View File

@ -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)
}
}
}

View File

@ -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))
}

View File

@ -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"
}
}
}

View File

@ -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")
}

View File

@ -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))
}

View File

@ -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())
}

View File

@ -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) }

View File

@ -0,0 +1,3 @@
package solutions.bitbadger.documents.scala.support
class SubDocument(val foo: String = "", val bar: String = "")