Initial Development #1
26
.idea/libraries/Maven__scala_sdk_3_1_3.xml
generated
Normal file
26
.idea/libraries/Maven__scala_sdk_3_1_3.xml
generated
Normal 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>
|
25
.idea/libraries/Maven__scala_sdk_3_3_3.xml
generated
Normal file
25
.idea/libraries/Maven__scala_sdk_3_3_3.xml
generated
Normal 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>
|
26
.idea/libraries/Maven__scala_sdk_3_5_2.xml
generated
Normal file
26
.idea/libraries/Maven__scala_sdk_3_5_2.xml
generated
Normal 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>
|
@ -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>
|
||||||
|
@ -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()) }
|
||||||
|
// }
|
||||||
|
}
|
@ -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() }
|
||||||
|
// }
|
||||||
|
}
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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"))
|
||||||
|
}
|
@ -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()
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")
|
||||||
|
}
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")
|
||||||
|
}
|
@ -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'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")
|
||||||
|
}
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")
|
||||||
|
}
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")
|
||||||
|
}
|
@ -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, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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, ""))
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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))
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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))
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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))
|
||||||
|
}
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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))
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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))
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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))
|
||||||
|
}
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")
|
||||||
|
}
|
@ -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))
|
||||||
|
}
|
@ -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())
|
||||||
|
}
|
@ -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) }
|
@ -0,0 +1,3 @@
|
|||||||
|
package solutions.bitbadger.documents.scala.support
|
||||||
|
|
||||||
|
class SubDocument(val foo: String = "", val bar: String = "")
|
Loading…
x
Reference in New Issue
Block a user