Reorg modules; complete impls/tests

This commit is contained in:
2025-03-25 07:20:30 -04:00
parent 815f506339
commit 50188d939e
363 changed files with 11626 additions and 1574 deletions

8
src/groovy/groovy.iml Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="AdditionalModuleElements">
<content url="file://$MODULE_DIR$" dumb="true">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
</content>
</component>
</module>

109
src/groovy/pom.xml Normal file
View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>solutions.bitbadger</groupId>
<artifactId>documents</artifactId>
<version>4.0.0-alpha1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<groupId>solutions.bitbadger.documents</groupId>
<artifactId>groovy</artifactId>
<name>${project.groupId}:${project.artifactId}</name>
<description>Expose a document store interface for PostgreSQL and SQLite (Groovy Library)</description>
<url>https://bitbadger.solutions/open-source/relational-documents/jvm/</url>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>4.1.1</version>
<executions>
<execution>
<goals>
<goal>addSources</goal>
<goal>addTestSources</goal>
<goal>generateStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>compileTests</goal>
<goal>removeStubs</goal>
<goal>removeTestStubs</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy</artifactId>
<version>${groovy.version}</version>
<scope>runtime</scope>
<type>pom</type>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${failsafe.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>solutions.bitbadger.documents</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy</artifactId>
<version>${groovy.version}</version>
</dependency>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-test</artifactId>
<version>${groovy.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-test-junit5</artifactId>
<version>${groovy.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

View File

@@ -0,0 +1,3 @@
module solutions.bitbadger.documents.groovy {
requires solutions.bitbadger.documents.core;
}

View File

@@ -0,0 +1,3 @@
moduleName=Document Extensions for Connection
moduleVersion=4.0.0-alpha1
extensionClasses=solutions.bitbadger.documents.java.extensions.ConnExt

View File

@@ -0,0 +1,163 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.AutoId
import solutions.bitbadger.documents.DocumentException
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `AutoId` enum
*/
@DisplayName('Groovy | AutoId')
class AutoIdTest {
@Test
@DisplayName('Generates a UUID string')
void generateUUID() {
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')
void generateRandomStringEven() {
def result = AutoId.generateRandomString 8
assertEquals 8, result.length(), "There should have been 8 characters in $result"
}
@Test
@DisplayName('Generates a random hex character string of an odd length')
void generateRandomStringOdd() {
def result = AutoId.generateRandomString 11
assertEquals 11, result.length(), "There should have been 11 characters in $result"
}
@Test
@DisplayName('Generates different random hex character strings')
void generateRandomStringIsRandom() {
def result1 = AutoId.generateRandomString 16
def result2 = AutoId.generateRandomString 16
assertNotEquals result1, result2, 'There should have been 2 different strings generated'
}
@Test
@DisplayName('needsAutoId fails for null document')
void needsAutoIdFailsForNullDocument() {
assertThrows(DocumentException) { AutoId.needsAutoId(AutoId.DISABLED, null, 'id') }
}
@Test
@DisplayName('needsAutoId fails for missing ID property')
void needsAutoIdFailsForMissingId() {
assertThrows(DocumentException) { AutoId.needsAutoId(AutoId.UUID, new IntIdClass(0), 'Id') }
}
@Test
@DisplayName('needsAutoId returns false if disabled')
void needsAutoIdFalseIfDisabled() {
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')
void needsAutoIdTrueForByteWithZero() {
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, new ByteIdClass((byte) 0), 'id'),
'Number Auto ID with 0 should return true')
}
@Test
@DisplayName('needsAutoId returns false for Number strategy and byte ID of non-0')
void needsAutoIdFalseForByteWithNonZero() {
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, new ByteIdClass((byte) 77), 'id'),
'Number Auto ID with 77 should return false')
}
@Test
@DisplayName('needsAutoId returns true for Number strategy and short ID of 0')
void needsAutoIdTrueForShortWithZero() {
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, new ShortIdClass((short) 0), 'id'),
'Number Auto ID with 0 should return true')
}
@Test
@DisplayName('needsAutoId returns false for Number strategy and short ID of non-0')
void needsAutoIdFalseForShortWithNonZero() {
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, new ShortIdClass((short) 31), 'id'),
'Number Auto ID with 31 should return false')
}
@Test
@DisplayName('needsAutoId returns true for Number strategy and int ID of 0')
void needsAutoIdTrueForIntWithZero() {
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, new 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')
void needsAutoIdFalseForIntWithNonZero() {
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, new IntIdClass(6), 'id'),
'Number Auto ID with 6 should return false')
}
@Test
@DisplayName('needsAutoId returns true for Number strategy and long ID of 0')
void needsAutoIdTrueForLongWithZero() {
assertTrue(AutoId.needsAutoId(AutoId.NUMBER, new LongIdClass(0L), 'id'),
'Number Auto ID with 0 should return true')
}
@Test
@DisplayName('needsAutoId returns false for Number strategy and long ID of non-0')
void needsAutoIdFalseForLongWithNonZero() {
assertFalse(AutoId.needsAutoId(AutoId.NUMBER, new LongIdClass(2L), 'id'),
'Number Auto ID with 2 should return false')
}
@Test
@DisplayName('needsAutoId fails for Number strategy and non-number ID')
void needsAutoIdFailsForNumberWithStringId() {
assertThrows(DocumentException) { AutoId.needsAutoId(AutoId.NUMBER, new StringIdClass(''), 'id') }
}
@Test
@DisplayName('needsAutoId returns true for UUID strategy and blank ID')
void needsAutoIdTrueForUUIDWithBlank() {
assertTrue(AutoId.needsAutoId(AutoId.UUID, new StringIdClass(''), 'id'),
'UUID Auto ID with blank should return true')
}
@Test
@DisplayName('needsAutoId returns false for UUID strategy and non-blank ID')
void needsAutoIdFalseForUUIDNotBlank() {
assertFalse(AutoId.needsAutoId(AutoId.UUID, new StringIdClass('howdy'), 'id'),
'UUID Auto ID with non-blank should return false')
}
@Test
@DisplayName('needsAutoId fails for UUID strategy and non-string ID')
void needsAutoIdFailsForUUIDNonString() {
assertThrows(DocumentException) { AutoId.needsAutoId(AutoId.UUID, new IntIdClass(5), 'id') }
}
@Test
@DisplayName('needsAutoId returns true for Random String strategy and blank ID')
void needsAutoIdTrueForRandomWithBlank() {
assertTrue(AutoId.needsAutoId(AutoId.RANDOM_STRING, new StringIdClass(''), 'id'),
'Random String Auto ID with blank should return true')
}
@Test
@DisplayName('needsAutoId returns false for Random String strategy and non-blank ID')
void needsAutoIdFalseForRandomNotBlank() {
assertFalse(AutoId.needsAutoId(AutoId.RANDOM_STRING, new 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')
void needsAutoIdFailsForRandomNonString() {
assertThrows(DocumentException) { AutoId.needsAutoId(AutoId.RANDOM_STRING, new ShortIdClass((short) 55), 'id') }
}
}

View File

@@ -0,0 +1,9 @@
package solutions.bitbadger.documents.groovy.tests
class ByteIdClass {
byte id
ByteIdClass(byte id) {
this.id = id
}
}

View File

@@ -0,0 +1,47 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.AutoId
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Configuration` object
*/
@DisplayName('Groovy | Configuration')
class ConfigurationTest {
@Test
@DisplayName('Default ID field is "id"')
void defaultIdField() {
assertEquals 'id', Configuration.idField, 'Default ID field incorrect'
}
@Test
@DisplayName('Default Auto ID strategy is DISABLED')
void defaultAutoId() {
assertEquals AutoId.DISABLED, Configuration.autoIdStrategy, 'Default Auto ID strategy should be DISABLED'
}
@Test
@DisplayName('Default ID string length should be 16')
void defaultIdStringLength() {
assertEquals 16, Configuration.idStringLength, 'Default ID string length should be 16'
}
@Test
@DisplayName('Dialect is derived from connection string')
void dialectIsDerived() {
try {
assertThrows(DocumentException) { Configuration.dialect() }
Configuration.connectionString = 'jdbc:postgresql:db'
assertEquals Dialect.POSTGRESQL, Configuration.dialect()
} finally {
Configuration.connectionString = null
}
}
}

View File

@@ -0,0 +1,81 @@
package solutions.bitbadger.documents.groovy.tests
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.query.CountQuery
import static Types.TEST_TABLE
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Count` object
*/
@DisplayName('Groovy | Query | CountQuery')
class CountQueryTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
void cleanUp() {
ForceDialect.none()
}
@Test
@DisplayName('all generates correctly')
void all() {
assertEquals("SELECT COUNT(*) AS it FROM $TEST_TABLE".toString(), CountQuery.all(TEST_TABLE),
'Count query not constructed correctly')
}
@Test
@DisplayName('byFields generates correctly | PostgreSQL')
void byFieldsPostgres() {
ForceDialect.postgres()
assertEquals("SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0".toString(),
CountQuery.byFields(TEST_TABLE, List.of(Field.equal('test', '', ':field0'))),
'Count query not constructed correctly')
}
@Test
@DisplayName('byFields generates correctly | SQLite')
void byFieldsSQLite() {
ForceDialect.sqlite()
assertEquals("SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0".toString(),
CountQuery.byFields(TEST_TABLE, List.of(Field.equal('test', '', ':field0'))),
'Count query not constructed correctly')
}
@Test
@DisplayName('byContains generates correctly | PostgreSQL')
void byContainsPostgres() {
ForceDialect.postgres()
assertEquals("SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data @> :criteria".toString(),
CountQuery.byContains(TEST_TABLE), 'Count query not constructed correctly')
}
@Test
@DisplayName('byContains fails | SQLite')
void byContainsSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { CountQuery.byContains(TEST_TABLE) }
}
@Test
@DisplayName('byJsonPath generates correctly | PostgreSQL')
void byJsonPathPostgres() {
ForceDialect.postgres()
assertEquals("SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)".toString(),
CountQuery.byJsonPath(TEST_TABLE), 'Count query not constructed correctly')
}
@Test
@DisplayName('byJsonPath fails | SQLite')
void byJsonPathSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { CountQuery.byJsonPath(TEST_TABLE) }
}
}

View File

@@ -0,0 +1,127 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.DocumentIndex
import solutions.bitbadger.documents.query.DefinitionQuery
import static Types.TEST_TABLE
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Definition` object
*/
@DisplayName('Groovy | Query | DefinitionQuery')
class DefinitionQueryTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
void cleanUp() {
ForceDialect.none()
}
@Test
@DisplayName('ensureTableFor generates correctly')
void ensureTableFor() {
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')
void ensureTablePostgres() {
ForceDialect.postgres()
assertEquals("CREATE TABLE IF NOT EXISTS $TEST_TABLE (data JSONB NOT NULL)".toString(),
DefinitionQuery.ensureTable(TEST_TABLE))
}
@Test
@DisplayName('ensureTable generates correctly | SQLite')
void ensureTableSQLite() {
ForceDialect.sqlite()
assertEquals("CREATE TABLE IF NOT EXISTS $TEST_TABLE (data TEXT NOT NULL)".toString(),
DefinitionQuery.ensureTable(TEST_TABLE))
}
@Test
@DisplayName('ensureTable fails when no dialect is set')
void ensureTableFailsUnknown() {
assertThrows(DocumentException) { DefinitionQuery.ensureTable(TEST_TABLE) }
}
@Test
@DisplayName('ensureKey generates correctly with schema')
void ensureKeyWithSchema() {
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')
void ensureKeyWithoutSchema() {
assertEquals(
"CREATE UNIQUE INDEX IF NOT EXISTS idx_${TEST_TABLE}_key ON $TEST_TABLE ((data->>'id'))".toString(),
DefinitionQuery.ensureKey(TEST_TABLE, Dialect.SQLITE),
'CREATE INDEX for key statement without schema not constructed correctly')
}
@Test
@DisplayName('ensureIndexOn generates multiple fields and directions')
void ensureIndexOnMultipleFields() {
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.of('taco', 'guac DESC', 'salsa ASC'),
Dialect.POSTGRESQL),
'CREATE INDEX for multiple field statement not constructed correctly')
}
@Test
@DisplayName('ensureIndexOn generates nested field | PostgreSQL')
void ensureIndexOnNestedPostgres() {
assertEquals("CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data#>>'{a,b,c}'))".toString(),
DefinitionQuery.ensureIndexOn(TEST_TABLE, 'nest', List.of('a.b.c'), Dialect.POSTGRESQL),
'CREATE INDEX for nested PostgreSQL field incorrect')
}
@Test
@DisplayName('ensureIndexOn generates nested field | SQLite')
void ensureIndexOnNestedSQLite() {
assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data->'a'->'b'->>'c'))".toString(),
DefinitionQuery.ensureIndexOn(TEST_TABLE, 'nest', List.of('a.b.c'), Dialect.SQLITE),
'CREATE INDEX for nested SQLite field incorrect')
}
@Test
@DisplayName('ensureDocumentIndexOn generates Full | PostgreSQL')
void ensureDocumentIndexOnFullPostgres() {
ForceDialect.postgres()
assertEquals("CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data)".toString(),
DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL),
'CREATE INDEX for full document index incorrect')
}
@Test
@DisplayName('ensureDocumentIndexOn generates Optimized | PostgreSQL')
void ensureDocumentIndexOnOptimizedPostgres() {
ForceDialect.postgres()
assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data jsonb_path_ops)"
.toString(),
DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.OPTIMIZED),
'CREATE INDEX for optimized document index incorrect')
}
@Test
@DisplayName('ensureDocumentIndexOn fails | SQLite')
void ensureDocumentIndexOnFailsSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL) }
}
}

View File

@@ -0,0 +1,90 @@
package solutions.bitbadger.documents.groovy.tests
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.query.DeleteQuery
import static Types.TEST_TABLE
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Delete` object
*/
@DisplayName('Groovy | Query | DeleteQuery')
class DeleteQueryTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
void cleanUp() {
ForceDialect.none()
}
@Test
@DisplayName('byId generates correctly | PostgreSQL')
void byIdPostgres() {
ForceDialect.postgres()
assertEquals("DELETE FROM $TEST_TABLE WHERE data->>'id' = :id".toString(), DeleteQuery.byId(TEST_TABLE),
'Delete query not constructed correctly')
}
@Test
@DisplayName('byId generates correctly | SQLite')
void byIdSQLite() {
ForceDialect.sqlite()
assertEquals("DELETE FROM $TEST_TABLE WHERE data->>'id' = :id".toString(), DeleteQuery.byId(TEST_TABLE),
'Delete query not constructed correctly')
}
@Test
@DisplayName('byFields generates correctly | PostgreSQL')
void byFieldsPostgres() {
ForceDialect.postgres()
assertEquals("DELETE FROM $TEST_TABLE WHERE data->>'a' = :b".toString(),
DeleteQuery.byFields(TEST_TABLE, List.of(Field.equal('a', '', ':b'))),
'Delete query not constructed correctly')
}
@Test
@DisplayName('byFields generates correctly | SQLite')
void byFieldsSQLite() {
ForceDialect.sqlite()
assertEquals("DELETE FROM $TEST_TABLE WHERE data->>'a' = :b".toString(),
DeleteQuery.byFields(TEST_TABLE, List.of(Field.equal('a', '', ':b'))),
'Delete query not constructed correctly')
}
@Test
@DisplayName('byContains generates correctly | PostgreSQL')
void byContainsPostgres() {
ForceDialect.postgres()
assertEquals("DELETE FROM $TEST_TABLE WHERE data @> :criteria".toString(), DeleteQuery.byContains(TEST_TABLE),
'Delete query not constructed correctly')
}
@Test
@DisplayName('byContains fails | SQLite')
void byContainsSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { DeleteQuery.byContains(TEST_TABLE) }
}
@Test
@DisplayName('byJsonPath generates correctly | PostgreSQL')
void byJsonPathPostgres() {
ForceDialect.postgres()
assertEquals("DELETE FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)".toString(),
DeleteQuery.byJsonPath(TEST_TABLE), 'Delete query not constructed correctly')
}
@Test
@DisplayName('byJsonPath fails | SQLite')
void byJsonPathSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { DeleteQuery.byJsonPath(TEST_TABLE) }
}
}

View File

@@ -0,0 +1,42 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Dialect` enum
*/
@DisplayName('Groovy | Dialect')
class DialectTest {
@Test
@DisplayName('deriveFromConnectionString derives PostgreSQL correctly')
void derivesPostgres() {
assertEquals(Dialect.POSTGRESQL, Dialect.deriveFromConnectionString('jdbc:postgresql:db'),
'Dialect should have been PostgreSQL')
}
@Test
@DisplayName('deriveFromConnectionString derives SQLite correctly')
void derivesSQLite() {
assertEquals(Dialect.SQLITE, Dialect.deriveFromConnectionString('jdbc:sqlite:memory'),
'Dialect should have been SQLite')
}
@Test
@DisplayName('deriveFromConnectionString fails when the connection string is unknown')
void deriveFailsWhenUnknown() {
try {
Dialect.deriveFromConnectionString 'SQL Server'
fail 'Dialect derivation should have failed'
} catch (DocumentException ex) {
assertNotNull ex.message, 'The exception message should not have been null'
assertTrue(ex.message.contains('[SQL Server]'),
'The connection string should have been in the exception message')
}
}
}

View File

@@ -0,0 +1,26 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.DocumentIndex
import static org.junit.jupiter.api.Assertions.assertEquals
/**
* Unit tests for the `DocumentIndex` enum
*/
@DisplayName('Groovy | DocumentIndex')
class DocumentIndexTest {
@Test
@DisplayName('FULL uses proper SQL')
void fullSQL() {
assertEquals '', DocumentIndex.FULL.sql, 'The SQL for Full is incorrect'
}
@Test
@DisplayName('OPTIMIZED uses proper SQL')
void optimizedSQL() {
assertEquals ' jsonb_path_ops', DocumentIndex.OPTIMIZED.sql, 'The SQL for Optimized is incorrect'
}
}

View File

@@ -0,0 +1,135 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.AutoId
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.query.DocumentQuery
import static Types.TEST_TABLE
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Document` object
*/
@DisplayName('Groovy | Query | DocumentQuery')
class DocumentQueryTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
void cleanUp() {
ForceDialect.none()
}
@Test
@DisplayName('insert generates no auto ID | PostgreSQL')
void insertNoAutoPostgres() {
ForceDialect.postgres()
assertEquals("INSERT INTO $TEST_TABLE VALUES (:data)".toString(), DocumentQuery.insert(TEST_TABLE))
}
@Test
@DisplayName('insert generates no auto ID | SQLite')
void insertNoAutoSQLite() {
ForceDialect.sqlite()
assertEquals("INSERT INTO $TEST_TABLE VALUES (:data)".toString(), DocumentQuery.insert(TEST_TABLE))
}
@Test
@DisplayName('insert generates auto number | PostgreSQL')
void insertAutoNumberPostgres() {
ForceDialect.postgres()
assertEquals("INSERT INTO $TEST_TABLE VALUES (:data::jsonb || ('{\"id\":' || (SELECT ".toString() +
"COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $TEST_TABLE) || '}')::jsonb)".toString(),
DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER))
}
@Test
@DisplayName('insert generates auto number | SQLite')
void insertAutoNumberSQLite() {
ForceDialect.sqlite()
assertEquals("INSERT INTO $TEST_TABLE VALUES (json_set(:data, '\$.id', ".toString() +
"(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $TEST_TABLE)))".toString(),
DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER))
}
@Test
@DisplayName('insert generates auto UUID | PostgreSQL')
void insertAutoUUIDPostgres() {
ForceDialect.postgres()
def query = DocumentQuery.insert TEST_TABLE, AutoId.UUID
assertTrue(query.startsWith("INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\""),
"Query start not correct (actual: $query)")
assertTrue query.endsWith("\"}')"), 'Query end not correct'
}
@Test
@DisplayName('insert generates auto UUID | SQLite')
void insertAutoUUIDSQLite() {
ForceDialect.sqlite()
def query = DocumentQuery.insert TEST_TABLE, AutoId.UUID
assertTrue(query.startsWith("INSERT INTO $TEST_TABLE VALUES (json_set(:data, '\$.id', '"),
"Query start not correct (actual: $query)")
assertTrue query.endsWith("'))"), 'Query end not correct'
}
@Test
@DisplayName('insert generates auto random string | PostgreSQL')
void insertAutoRandomPostgres() {
try {
ForceDialect.postgres()
Configuration.idStringLength = 8
def query = DocumentQuery.insert TEST_TABLE, AutoId.RANDOM_STRING
assertTrue(query.startsWith("INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\""),
"Query start not correct (actual: $query)")
assertTrue query.endsWith("\"}')"), 'Query end not correct'
assertEquals(8,
query.replace("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')
void insertAutoRandomSQLite() {
ForceDialect.sqlite()
def query = DocumentQuery.insert TEST_TABLE, AutoId.RANDOM_STRING
assertTrue(query.startsWith("INSERT INTO $TEST_TABLE VALUES (json_set(:data, '\$.id', '"),
"Query start not correct (actual: $query)")
assertTrue query.endsWith("'))"), 'Query end not correct'
assertEquals(Configuration.idStringLength,
query.replace("INSERT INTO $TEST_TABLE VALUES (json_set(:data, '\$.id', '", '').replace("'))", '')
.length(),
'Random string length incorrect')
}
@Test
@DisplayName('insert fails when no dialect is set')
void insertFailsUnknown() {
assertThrows(DocumentException) { DocumentQuery.insert(TEST_TABLE) }
}
@Test
@DisplayName('save generates correctly')
void save() {
ForceDialect.postgres()
assertEquals(
"INSERT INTO $TEST_TABLE VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data"
.toString(),
DocumentQuery.save(TEST_TABLE), 'INSERT ON CONFLICT UPDATE statement not constructed correctly')
}
@Test
@DisplayName('update generates successfully')
void update() {
assertEquals("UPDATE $TEST_TABLE SET data = :data".toString(), DocumentQuery.update(TEST_TABLE),
'Update query not constructed correctly')
}
}

View File

@@ -0,0 +1,26 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.FieldMatch
import static org.junit.jupiter.api.Assertions.assertEquals
/**
* Unit tests for the `FieldMatch` enum
*/
@DisplayName('Groovy | FieldMatch')
class FieldMatchTest {
@Test
@DisplayName('ANY uses proper SQL')
void anySQL() {
assertEquals 'OR', FieldMatch.ANY.sql, 'ANY should use OR'
}
@Test
@DisplayName('ALL uses proper SQL')
void allSQL() {
assertEquals 'AND', FieldMatch.ALL.sql, 'ALL should use AND'
}
}

View File

@@ -0,0 +1,612 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldFormat
import solutions.bitbadger.documents.Op
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Field` class
*/
@DisplayName('Groovy | Field')
class FieldTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
void cleanUp() {
ForceDialect.none()
}
// ~~~ INSTANCE METHODS ~~~
@Test
@DisplayName('withParameterName fails for invalid name')
void withParamNameFails() {
assertThrows(DocumentException) { Field.equal('it', '').withParameterName('2424') }
}
@Test
@DisplayName('withParameterName works with colon prefix')
void withParamNameColon() {
def field = Field.equal('abc', '22').withQualifier 'me'
def withParam = field.withParameterName ':test'
assertNotSame field, withParam, 'A new Field instance should have been created'
assertEquals field.name, withParam.name, 'Name should have been preserved'
assertEquals field.comparison, withParam.comparison, 'Comparison should have been preserved'
assertEquals ':test', withParam.parameterName, 'Parameter name not set correctly'
assertEquals field.qualifier, withParam.qualifier, 'Qualifier should have been preserved'
}
@Test
@DisplayName('withParameterName works with at-sign prefix')
void withParamNameAtSign() {
def field = Field.equal 'def', '44'
def withParam = field.withParameterName '@unit'
assertNotSame field, withParam, 'A new Field instance should have been created'
assertEquals field.name, withParam.name, 'Name should have been preserved'
assertEquals field.comparison, withParam.comparison, 'Comparison should have been preserved'
assertEquals '@unit', withParam.parameterName, 'Parameter name not set correctly'
assertEquals field.qualifier, withParam.qualifier, 'Qualifier should have been preserved'
}
@Test
@DisplayName('withQualifier sets qualifier correctly')
void withQualifier() {
def field = Field.equal 'j', 'k'
def withQual = field.withQualifier 'test'
assertNotSame field, withQual, 'A new Field instance should have been created'
assertEquals field.name, withQual.name, 'Name should have been preserved'
assertEquals field.comparison, withQual.comparison, 'Comparison should have been preserved'
assertEquals field.parameterName, withQual.parameterName, 'Parameter Name should have been preserved'
assertEquals 'test', withQual.qualifier, 'Qualifier not set correctly'
}
@Test
@DisplayName('path generates for simple unqualified PostgreSQL field')
void pathPostgresSimpleUnqualified() {
assertEquals("data->>'SomethingCool'",
Field.greaterOrEqual('SomethingCool', 18).path(Dialect.POSTGRESQL, FieldFormat.SQL), 'Path not correct')
}
@Test
@DisplayName('path generates for simple qualified PostgreSQL field')
void pathPostgresSimpleQualified() {
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')
void pathPostgresNestedUnqualified() {
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')
void pathPostgresNestedQualified() {
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')
void pathSQLiteSimpleUnqualified() {
assertEquals("data->>'SomethingCool'",
Field.greaterOrEqual('SomethingCool', 18).path(Dialect.SQLITE, FieldFormat.SQL), 'Path not correct')
}
@Test
@DisplayName('path generates for simple qualified SQLite field')
void pathSQLiteSimpleQualified() {
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')
void pathSQLiteNestedUnqualified() {
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')
void pathSQLiteNestedQualified() {
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')
void toWhereExistsNoQualPostgres() {
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')
void toWhereExistsNoQualSQLite() {
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')
void toWhereNotExistsNoQualPostgres() {
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')
void toWhereNotExistsNoQualSQLite() {
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')
void toWhereBetweenNoQualNumericPostgres() {
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')
void toWhereBetweenNoQualAlphaPostgres() {
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')
void toWhereBetweenNoQualSQLite() {
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')
void toWhereBetweenQualNumericPostgres() {
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')
void toWhereBetweenQualAlphaPostgres() {
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')
void toWhereBetweenQualSQLite() {
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')
void toWhereAnyNumericPostgres() {
ForceDialect.postgres()
assertEquals("(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)",
Field.any('even', List.of(2, 4, 6), ':nbr').toWhere(), 'Field WHERE clause not generated correctly')
}
@Test
@DisplayName('toWhere generates for IN/any, alphanumeric values | PostgreSQL')
void toWhereAnyAlphaPostgres() {
ForceDialect.postgres()
assertEquals("data->>'test' IN (:city_0, :city_1)",
Field.any('test', List.of('Atlanta', 'Chicago'), ':city').toWhere(),
'Field WHERE clause not generated correctly')
}
@Test
@DisplayName('toWhere generates for IN/any | SQLite')
void toWhereAnySQLite() {
ForceDialect.sqlite()
assertEquals("data->>'test' IN (:city_0, :city_1)",
Field.any('test', List.of('Atlanta', 'Chicago'), ':city').toWhere(),
'Field WHERE clause not generated correctly')
}
@Test
@DisplayName('toWhere generates for inArray | PostgreSQL')
void toWhereInArrayPostgres() {
ForceDialect.postgres()
assertEquals("data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]",
Field.inArray('even', 'tbl', List.of(2, 4, 6, 8), ':it').toWhere(),
'Field WHERE clause not generated correctly')
}
@Test
@DisplayName('toWhere generates for inArray | SQLite')
void toWhereInArraySQLite() {
ForceDialect.sqlite()
assertEquals("EXISTS (SELECT 1 FROM json_each(tbl.data, '\$.test') WHERE value IN (:city_0, :city_1))",
Field.inArray('test', 'tbl', List.of('Atlanta', 'Chicago'), ':city').toWhere(),
'Field WHERE clause not generated correctly')
}
@Test
@DisplayName('toWhere generates for others w/o qualifier | PostgreSQL')
void toWhereOtherNoQualPostgres() {
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')
void toWhereOtherNoQualSQLite() {
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')
void toWhereNoParamWithQualPostgres() {
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')
void toWhereNoParamWithQualSQLite() {
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')
void toWhereParamWithQualPostgres() {
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')
void toWhereParamWithQualSQLite() {
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')
void equalCtor() {
def field = Field.equal 'Test', 14
assertEquals 'Test', field.name, 'Field name not filled correctly'
assertEquals Op.EQUAL, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 14, field.comparison.value, 'Field comparison value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('equal constructs a field w/ parameter name')
void equalParameterCtor() {
def field = Field.equal 'Test', 14, ':w'
assertEquals 'Test', field.name, 'Field name not filled correctly'
assertEquals Op.EQUAL, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 14, field.comparison.value, 'Field comparison value not filled correctly'
assertEquals ':w', field.parameterName, 'Field parameter name not filled correctly'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('greater constructs a field w/o parameter name')
void greaterCtor() {
def field = Field.greater 'Great', 'night'
assertEquals 'Great', field.name, 'Field name not filled correctly'
assertEquals Op.GREATER, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 'night', field.comparison.value, 'Field comparison value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('greater constructs a field w/ parameter name')
void greaterParameterCtor() {
def field = Field.greater 'Great', 'night', ':yeah'
assertEquals 'Great', field.name, 'Field name not filled correctly'
assertEquals Op.GREATER, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 'night', field.comparison.value, 'Field comparison value not filled correctly'
assertEquals ':yeah', field.parameterName, 'Field parameter name not filled correctly'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('greaterOrEqual constructs a field w/o parameter name')
void greaterOrEqualCtor() {
def field = Field.greaterOrEqual 'Nice', 88L
assertEquals 'Nice', field.name, 'Field name not filled correctly'
assertEquals Op.GREATER_OR_EQUAL, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 88L, field.comparison.value, 'Field comparison value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('greaterOrEqual constructs a field w/ parameter name')
void greaterOrEqualParameterCtor() {
def field = Field.greaterOrEqual 'Nice', 88L, ':nice'
assertEquals 'Nice', field.name, 'Field name not filled correctly'
assertEquals Op.GREATER_OR_EQUAL, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 88L, field.comparison.value, 'Field comparison value not filled correctly'
assertEquals ':nice', field.parameterName, 'Field parameter name not filled correctly'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('less constructs a field w/o parameter name')
void lessCtor() {
def field = Field.less 'Lesser', 'seven'
assertEquals 'Lesser', field.name, 'Field name not filled correctly'
assertEquals Op.LESS, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 'seven', field.comparison.value, 'Field comparison value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('less constructs a field w/ parameter name')
void lessParameterCtor() {
def field = Field.less 'Lesser', 'seven', ':max'
assertEquals 'Lesser', field.name, 'Field name not filled correctly'
assertEquals Op.LESS, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 'seven', field.comparison.value, 'Field comparison value not filled correctly'
assertEquals ':max', field.parameterName, 'Field parameter name not filled correctly'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('lessOrEqual constructs a field w/o parameter name')
void lessOrEqualCtor() {
def field = Field.lessOrEqual 'Nobody', 'KNOWS'
assertEquals 'Nobody', field.name, 'Field name not filled correctly'
assertEquals Op.LESS_OR_EQUAL, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 'KNOWS', field.comparison.value, 'Field comparison value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('lessOrEqual constructs a field w/ parameter name')
void lessOrEqualParameterCtor() {
def field = Field.lessOrEqual 'Nobody', 'KNOWS', ':nope'
assertEquals 'Nobody', field.name, 'Field name not filled correctly'
assertEquals Op.LESS_OR_EQUAL, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 'KNOWS', field.comparison.value, 'Field comparison value not filled correctly'
assertEquals ':nope', field.parameterName, 'Field parameter name not filled correctly'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('notEqual constructs a field w/o parameter name')
void notEqualCtor() {
def field = Field.notEqual 'Park', 'here'
assertEquals 'Park', field.name, 'Field name not filled correctly'
assertEquals Op.NOT_EQUAL, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 'here', field.comparison.value, 'Field comparison value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('notEqual constructs a field w/ parameter name')
void notEqualParameterCtor() {
def field = Field.notEqual 'Park', 'here', ':now'
assertEquals 'Park', field.name, 'Field name not filled correctly'
assertEquals Op.NOT_EQUAL, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 'here', field.comparison.value, 'Field comparison value not filled correctly'
assertEquals ':now', field.parameterName, 'Field parameter name not filled correctly'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('between constructs a field w/o parameter name')
void betweenCtor() {
def field = Field.between 'Age', 18, 49
assertEquals 'Age', field.name, 'Field name not filled correctly'
assertEquals Op.BETWEEN, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 18, field.comparison.value.first, 'Field comparison min value not filled correctly'
assertEquals 49, field.comparison.value.second, 'Field comparison max value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('between constructs a field w/ parameter name')
void betweenParameterCtor() {
def field = Field.between 'Age', 18, 49, ':limit'
assertEquals 'Age', field.name, 'Field name not filled correctly'
assertEquals Op.BETWEEN, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 18, field.comparison.value.first, 'Field comparison min value not filled correctly'
assertEquals 49, field.comparison.value.second, 'Field comparison max value not filled correctly'
assertEquals ':limit', field.parameterName, 'Field parameter name not filled correctly'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('any constructs a field w/o parameter name')
void anyCtor() {
def field = Field.any 'Here', List.of(8, 16, 32)
assertEquals 'Here', field.name, 'Field name not filled correctly'
assertEquals Op.IN, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals List.of(8, 16, 32), field.comparison.value, 'Field comparison value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('any constructs a field w/ parameter name')
void anyParameterCtor() {
def field = Field.any 'Here', List.of(8, 16, 32), ':list'
assertEquals 'Here', field.name, 'Field name not filled correctly'
assertEquals Op.IN, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals List.of(8, 16, 32), field.comparison.value, 'Field comparison value not filled correctly'
assertEquals ':list', field.parameterName, 'Field parameter name not filled correctly'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('inArray constructs a field w/o parameter name')
void inArrayCtor() {
def field = Field.inArray 'ArrayField', 'table', List.of('z')
assertEquals 'ArrayField', field.name, 'Field name not filled correctly'
assertEquals Op.IN_ARRAY, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 'table', field.comparison.value.first, 'Field comparison table not filled correctly'
assertEquals List.of('z'), field.comparison.value.second, 'Field comparison values not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('inArray constructs a field w/ parameter name')
void inArrayParameterCtor() {
def field = Field.inArray 'ArrayField', 'table', List.of('z'), ':a'
assertEquals 'ArrayField', field.name, 'Field name not filled correctly'
assertEquals Op.IN_ARRAY, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals 'table', field.comparison.value.first, 'Field comparison table not filled correctly'
assertEquals List.of('z'), field.comparison.value.second, 'Field comparison values not filled correctly'
assertEquals ':a', field.parameterName, 'Field parameter name not filled correctly'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('exists constructs a field')
void existsCtor() {
def field = Field.exists 'Groovy'
assertEquals 'Groovy', field.name, 'Field name not filled correctly'
assertEquals Op.EXISTS, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals '', field.comparison.value, 'Field comparison value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('notExists constructs a field')
void notExistsCtor() {
def field = Field.notExists 'Groovy'
assertEquals 'Groovy', field.name, 'Field name not filled correctly'
assertEquals Op.NOT_EXISTS, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals '', field.comparison.value, 'Field comparison value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('named constructs a field')
void namedCtor() {
def field = Field.named 'Tacos'
assertEquals 'Tacos', field.name, 'Field name not filled correctly'
assertEquals Op.EQUAL, field.comparison.op, 'Field comparison operation not filled correctly'
assertEquals '', field.comparison.value, 'Field comparison value not filled correctly'
assertNull field.parameterName, 'The parameter name should have been null'
assertNull field.qualifier, 'The qualifier should have been null'
}
@Test
@DisplayName('static constructors fail for invalid parameter name')
void staticCtorsFailOnParamName() {
assertThrows(DocumentException) { Field.equal('a', 'b', "that ain't it, Jack...") }
}
@Test
@DisplayName('nameToPath creates a simple PostgreSQL SQL name')
void nameToPathPostgresSimpleSQL() {
assertEquals("data->>'Simple'", Field.nameToPath('Simple', Dialect.POSTGRESQL, FieldFormat.SQL),
'Path not constructed correctly')
}
@Test
@DisplayName('nameToPath creates a simple SQLite SQL name')
void nameToPathSQLiteSimpleSQL() {
assertEquals("data->>'Simple'", Field.nameToPath('Simple', Dialect.SQLITE, FieldFormat.SQL),
'Path not constructed correctly')
}
@Test
@DisplayName('nameToPath creates a nested PostgreSQL SQL name')
void nameToPathPostgresNestedSQL() {
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')
void nameToPathSQLiteNestedSQL() {
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')
void nameToPathPostgresSimpleJSON() {
assertEquals("data->'Simple'", Field.nameToPath('Simple', Dialect.POSTGRESQL, FieldFormat.JSON),
'Path not constructed correctly')
}
@Test
@DisplayName('nameToPath creates a simple SQLite JSON name')
void nameToPathSQLiteSimpleJSON() {
assertEquals("data->'Simple'", Field.nameToPath('Simple', Dialect.SQLITE, FieldFormat.JSON),
'Path not constructed correctly')
}
@Test
@DisplayName('nameToPath creates a nested PostgreSQL JSON name')
void nameToPathPostgresNestedJSON() {
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')
void nameToPathSQLiteNestedJSON() {
assertEquals("data->'A'->'Long'->'Path'->'to'->'the'->'Property'",
Field.nameToPath('A.Long.Path.to.the.Property', Dialect.SQLITE, FieldFormat.JSON),
'Path not constructed correctly')
}
}

View File

@@ -0,0 +1,97 @@
package solutions.bitbadger.documents.groovy.tests
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.query.FindQuery
import static Types.TEST_TABLE
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Find` object
*/
@DisplayName('Groovy | Query | FindQuery')
class FindQueryTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
void cleanUp() {
ForceDialect.none()
}
@Test
@DisplayName('all generates correctly')
void all() {
assertEquals("SELECT data FROM $TEST_TABLE".toString(), FindQuery.all(TEST_TABLE),
'Find query not constructed correctly')
}
@Test
@DisplayName('byId generates correctly | PostgreSQL')
void byIdPostgres() {
ForceDialect.postgres()
assertEquals("SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id".toString(), FindQuery.byId(TEST_TABLE),
'Find query not constructed correctly')
}
@Test
@DisplayName('byId generates correctly | SQLite')
void byIdSQLite() {
ForceDialect.sqlite()
assertEquals("SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id".toString(), FindQuery.byId(TEST_TABLE),
'Find query not constructed correctly')
}
@Test
@DisplayName('byFields generates correctly | PostgreSQL')
void byFieldsPostgres() {
ForceDialect.postgres()
assertEquals("SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND (data->>'c')::numeric < :d".toString(),
FindQuery.byFields(TEST_TABLE, List.of(Field.equal('a', '', ':b'), Field.less('c', 14, ':d'))),
'Find query not constructed correctly')
}
@Test
@DisplayName('byFields generates correctly | SQLite')
void byFieldsSQLite() {
ForceDialect.sqlite()
assertEquals("SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND data->>'c' < :d".toString(),
FindQuery.byFields(TEST_TABLE, List.of(Field.equal('a', '', ':b'), Field.less('c', 14, ':d'))),
'Find query not constructed correctly')
}
@Test
@DisplayName('byContains generates correctly | PostgreSQL')
void byContainsPostgres() {
ForceDialect.postgres()
assertEquals("SELECT data FROM $TEST_TABLE WHERE data @> :criteria".toString(),
FindQuery.byContains(TEST_TABLE), 'Find query not constructed correctly')
}
@Test
@DisplayName('byContains fails | SQLite')
void byContainsSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { FindQuery.byContains(TEST_TABLE) }
}
@Test
@DisplayName('byJsonPath generates correctly | PostgreSQL')
void byJsonPathPostgres() {
ForceDialect.postgres()
assertEquals("SELECT data FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)".toString(),
FindQuery.byJsonPath(TEST_TABLE), 'Find query not constructed correctly')
}
@Test
@DisplayName('byJsonPath fails | SQLite')
void byJsonPathSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { FindQuery.byJsonPath(TEST_TABLE) }
}
}

View File

@@ -0,0 +1,19 @@
package solutions.bitbadger.documents.groovy.tests
import solutions.bitbadger.documents.Configuration
final class ForceDialect {
static void postgres() {
Configuration.connectionString = ":postgresql:"
}
static void sqlite() {
Configuration.connectionString = ":sqlite:"
}
static void none() {
Configuration.connectionString = null
}
}

View File

@@ -0,0 +1,9 @@
package solutions.bitbadger.documents.groovy.tests
class IntIdClass {
int id
IntIdClass(int id) {
this.id = id
}
}

View File

@@ -0,0 +1,9 @@
package solutions.bitbadger.documents.groovy.tests
class LongIdClass {
long id
LongIdClass(long id) {
this.id = id
}
}

View File

@@ -0,0 +1,80 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.Op
import static org.junit.jupiter.api.Assertions.assertEquals
/**
* Unit tests for the `Op` enum
*/
@DisplayName('Groovy | Op')
class OpTest {
@Test
@DisplayName('EQUAL uses proper SQL')
void equalSQL() {
assertEquals '=', Op.EQUAL.sql, 'The SQL for equal is incorrect'
}
@Test
@DisplayName('GREATER uses proper SQL')
void greaterSQL() {
assertEquals '>', Op.GREATER.sql, 'The SQL for greater is incorrect'
}
@Test
@DisplayName('GREATER_OR_EQUAL uses proper SQL')
void greaterOrEqualSQL() {
assertEquals '>=', Op.GREATER_OR_EQUAL.sql, 'The SQL for greater-or-equal is incorrect'
}
@Test
@DisplayName('LESS uses proper SQL')
void lessSQL() {
assertEquals '<', Op.LESS.sql, 'The SQL for less is incorrect'
}
@Test
@DisplayName('LESS_OR_EQUAL uses proper SQL')
void lessOrEqualSQL() {
assertEquals '<=', Op.LESS_OR_EQUAL.sql, 'The SQL for less-or-equal is incorrect'
}
@Test
@DisplayName('NOT_EQUAL uses proper SQL')
void notEqualSQL() {
assertEquals '<>', Op.NOT_EQUAL.sql, 'The SQL for not-equal is incorrect'
}
@Test
@DisplayName('BETWEEN uses proper SQL')
void betweenSQL() {
assertEquals 'BETWEEN', Op.BETWEEN.sql, 'The SQL for between is incorrect'
}
@Test
@DisplayName('IN uses proper SQL')
void inSQL() {
assertEquals 'IN', Op.IN.sql, 'The SQL for in is incorrect'
}
@Test
@DisplayName('IN_ARRAY uses proper SQL')
void inArraySQL() {
assertEquals '??|', Op.IN_ARRAY.sql, 'The SQL for in-array is incorrect'
}
@Test
@DisplayName('EXISTS uses proper SQL')
void existsSQL() {
assertEquals 'IS NOT NULL', Op.EXISTS.sql, 'The SQL for exists is incorrect'
}
@Test
@DisplayName('NOT_EXISTS uses proper SQL')
void notExistsSQL() {
assertEquals 'IS NULL', Op.NOT_EXISTS.sql, 'The SQL for not-exists is incorrect'
}
}

View File

@@ -0,0 +1,32 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.ParameterName
import static org.junit.jupiter.api.Assertions.assertEquals
/**
* Unit tests for the `ParameterName` class
*/
@DisplayName('Groovy | ParameterName')
class ParameterNameTest {
@Test
@DisplayName('derive works when given existing names')
void withExisting() {
def names = new 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')
void allAnonymous() {
def names = new ParameterName()
assertEquals ':field0', names.derive(null), 'Anonymous field name should have been returned'
assertEquals ':field1', names.derive(null), 'Counter should have advanced from previous call'
assertEquals ':field2', names.derive(null), 'Counter should have advanced from previous call'
assertEquals ':field3', names.derive(null), 'Counter should have advanced from previous call'
}
}

View File

@@ -0,0 +1,40 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Parameter
import solutions.bitbadger.documents.ParameterType
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Parameter` class
*/
@DisplayName('Groovy | Parameter')
class ParameterTest {
@Test
@DisplayName('Construction with colon-prefixed name')
void ctorWithColon() {
def p = new Parameter(':test', ParameterType.STRING, 'ABC')
assertEquals ':test', p.name, 'Parameter name was incorrect'
assertEquals ParameterType.STRING, p.type, 'Parameter type was incorrect'
assertEquals 'ABC', p.value, 'Parameter value was incorrect'
}
@Test
@DisplayName('Construction with at-sign-prefixed name')
void ctorWithAtSign() {
def p = new Parameter('@yo', ParameterType.NUMBER, null)
assertEquals '@yo', p.name, 'Parameter name was incorrect'
assertEquals ParameterType.NUMBER, p.type, 'Parameter type was incorrect'
assertNull p.value, 'Parameter value was incorrect'
}
@Test
@DisplayName('Construction fails with incorrect prefix')
void ctorFailsForPrefix() {
assertThrows(DocumentException) { new Parameter('it', ParameterType.JSON, '') }
}
}

View File

@@ -0,0 +1,119 @@
package solutions.bitbadger.documents.groovy.tests
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.java.Parameters
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Parameters` object
*/
@DisplayName('Groovy | Parameters')
class ParametersTest {
/**
* Reset the dialect
*/
@AfterEach
void cleanUp() {
ForceDialect.none()
}
@Test
@DisplayName('nameFields works with no changes')
void nameFieldsNoChange() {
def fields = List.of(Field.equal('a', '', ':test'), Field.exists('q'), Field.equal('b', '', ':me'))
def named = Parameters.nameFields(fields).toList()
assertEquals fields.size(), named.size(), 'There should have been 3 fields in the list'
assertSame fields[0], named[0], 'The first field should be the same'
assertSame fields[1], named[1], 'The second field should be the same'
assertSame fields[2], named[2], 'The third field should be the same'
}
@Test
@DisplayName('nameFields works when changing fields')
void nameFieldsChange() {
def fields = List.of(Field.equal('a', ''), Field.equal('e', '', ':hi'), Field.equal('b', ''),
Field.notExists('z'))
def named = Parameters.nameFields(fields).toList()
assertEquals fields.size(), named.size(), 'There should have been 4 fields in the list'
assertNotSame fields[0], named[0], 'The first field should not be the same'
assertEquals ':field0', named[0].parameterName, 'First parameter name incorrect'
assertSame fields[1], named[1], 'The second field should be the same'
assertNotSame fields[2], named[2], 'The third field should not be the same'
assertEquals ':field1', named[2].parameterName, 'Third parameter name incorrect'
assertSame fields[3], named[3], 'The fourth field should be the same'
}
@Test
@DisplayName('replaceNamesInQuery replaces successfully')
void replaceNamesInQuery() {
def parameters = List.of(new Parameter(':data', ParameterType.JSON, '{}'),
new Parameter(':data_ext', ParameterType.STRING, ''))
def query = 'SELECT data, data_ext FROM tbl WHERE data = :data AND data_ext = :data_ext AND more_data = :data'
assertEquals('SELECT data, data_ext FROM tbl WHERE data = ? AND data_ext = ? AND more_data = ?',
Parameters.replaceNamesInQuery(query, parameters), 'Parameters not replaced correctly')
}
@Test
@DisplayName('fieldNames generates a single parameter (PostgreSQL)')
void fieldNamesSinglePostgres() {
ForceDialect.postgres()
def nameParams = Parameters.fieldNames(List.of('test')).toList()
assertEquals 1, nameParams.size(), 'There should be one name parameter'
assertEquals ':name', nameParams[0].name, 'The parameter name is incorrect'
assertEquals ParameterType.STRING, nameParams[0].type, 'The parameter type is incorrect'
assertEquals '{test}', nameParams[0].value, 'The parameter value is incorrect'
}
@Test
@DisplayName('fieldNames generates multiple parameters (PostgreSQL)')
void fieldNamesMultiplePostgres() {
ForceDialect.postgres()
def nameParams = Parameters.fieldNames(List.of('test', 'this', 'today')).toList()
assertEquals 1, nameParams.size(), 'There should be one name parameter'
assertEquals ':name', nameParams[0].name, 'The parameter name is incorrect'
assertEquals ParameterType.STRING, nameParams[0].type, 'The parameter type is incorrect'
assertEquals '{test,this,today}', nameParams[0].value, 'The parameter value is incorrect'
}
@Test
@DisplayName('fieldNames generates a single parameter (SQLite)')
void fieldNamesSingleSQLite() {
ForceDialect.sqlite()
def nameParams = Parameters.fieldNames(List.of('test')).toList()
assertEquals 1, nameParams.size(), 'There should be one name parameter'
assertEquals ':name0', nameParams[0].name, 'The parameter name is incorrect'
assertEquals ParameterType.STRING, nameParams[0].type, 'The parameter type is incorrect'
assertEquals 'test', nameParams[0].value, 'The parameter value is incorrect'
}
@Test
@DisplayName('fieldNames generates multiple parameters (SQLite)')
void fieldNamesMultipleSQLite() {
ForceDialect.sqlite()
def nameParams = Parameters.fieldNames(List.of('test', 'this', 'today')).toList()
assertEquals 3, nameParams.size(), 'There should be one name parameter'
assertEquals ':name0', nameParams[0].name, 'The first parameter name is incorrect'
assertEquals ParameterType.STRING, nameParams[0].type, 'The first parameter type is incorrect'
assertEquals 'test', nameParams[0].value, 'The first parameter value is incorrect'
assertEquals ':name1', nameParams[1].name, 'The second parameter name is incorrect'
assertEquals ParameterType.STRING, nameParams[1].type, 'The second parameter type is incorrect'
assertEquals 'this', nameParams[1].value, 'The second parameter value is incorrect'
assertEquals ':name2', nameParams[2].name, 'The third parameter name is incorrect'
assertEquals ParameterType.STRING, nameParams[2].type, 'The third parameter type is incorrect'
assertEquals 'today', nameParams[2].value, 'The third parameter value is incorrect'
}
@Test
@DisplayName('fieldNames fails if dialect not set')
void fieldNamesFails() {
assertThrows(DocumentException) { Parameters.fieldNames(List.of()) }
}
}

View File

@@ -0,0 +1,91 @@
package solutions.bitbadger.documents.groovy.tests
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.query.PatchQuery
import static Types.TEST_TABLE
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Patch` object
*/
@DisplayName('Groovy | Query | PatchQuery')
class PatchQueryTest {
/**
* Reset the dialect
*/
@AfterEach
void cleanUp() {
ForceDialect.none()
}
@Test
@DisplayName('byId generates correctly | PostgreSQL')
void byIdPostgres() {
ForceDialect.postgres()
assertEquals("UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'id' = :id".toString(),
PatchQuery.byId(TEST_TABLE), 'Patch query not constructed correctly')
}
@Test
@DisplayName('byId generates correctly | SQLite')
void byIdSQLite() {
ForceDialect.sqlite()
assertEquals("UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id".toString(),
PatchQuery.byId(TEST_TABLE), 'Patch query not constructed correctly')
}
@Test
@DisplayName('byFields generates correctly | PostgreSQL')
void byFieldsPostgres() {
ForceDialect.postgres()
assertEquals("UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'z' = :y".toString(),
PatchQuery.byFields(TEST_TABLE, List.of(Field.equal('z', '', ':y'))),
'Patch query not constructed correctly')
}
@Test
@DisplayName('byFields generates correctly | SQLite')
void byFieldsSQLite() {
ForceDialect.sqlite()
assertEquals("UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y".toString(),
PatchQuery.byFields(TEST_TABLE, List.of(Field.equal('z', '', ':y'))),
'Patch query not constructed correctly')
}
@Test
@DisplayName('byContains generates correctly | PostgreSQL')
void byContainsPostgres() {
ForceDialect.postgres()
assertEquals("UPDATE $TEST_TABLE SET data = data || :data WHERE data @> :criteria".toString(),
PatchQuery.byContains(TEST_TABLE), 'Patch query not constructed correctly')
}
@Test
@DisplayName('byContains fails | SQLite')
void byContainsSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { PatchQuery.byContains(TEST_TABLE) }
}
@Test
@DisplayName('byJsonPath generates correctly | PostgreSQL')
void byJsonPathPostgres() {
ForceDialect.postgres()
assertEquals(
"UPDATE $TEST_TABLE SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)".toString(),
PatchQuery.byJsonPath(TEST_TABLE), 'Patch query not constructed correctly')
}
@Test
@DisplayName('byJsonPath fails | SQLite')
void byJsonPathSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { PatchQuery.byJsonPath(TEST_TABLE) }
}
}

View File

@@ -0,0 +1,162 @@
package solutions.bitbadger.documents.groovy.tests
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch
import solutions.bitbadger.documents.query.QueryUtils
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `QueryUtils` class
*/
@DisplayName('Groovy | Query | QueryUtils')
class QueryUtilsTest {
/**
* Clear the connection string (resets Dialect)
*/
@AfterEach
void cleanUp() {
ForceDialect.none()
}
@Test
@DisplayName('statementWhere generates correctly')
void statementWhere() {
assertEquals 'x WHERE y', QueryUtils.statementWhere('x', 'y'), 'Statements not combined correctly'
}
@Test
@DisplayName('byId generates a numeric ID query | PostgreSQL')
void byIdNumericPostgres() {
ForceDialect.postgres()
assertEquals "test WHERE (data->>'id')::numeric = :id", QueryUtils.byId('test', 9)
}
@Test
@DisplayName('byId generates an alphanumeric ID query | PostgreSQL')
void byIdAlphaPostgres() {
ForceDialect.postgres()
assertEquals "unit WHERE data->>'id' = :id", QueryUtils.byId('unit', '18')
}
@Test
@DisplayName('byId generates ID query | SQLite')
void byIdSQLite() {
ForceDialect.sqlite()
assertEquals "yo WHERE data->>'id' = :id", QueryUtils.byId('yo', 27)
}
@Test
@DisplayName('byFields generates default field query | PostgreSQL')
void byFieldsMultipleDefaultPostgres() {
ForceDialect.postgres()
assertEquals("this WHERE data->>'a' = :the_a AND (data->>'b')::numeric = :b_value",
QueryUtils.byFields('this', List.of(Field.equal('a', '', ':the_a'), Field.equal('b', 0, ':b_value'))))
}
@Test
@DisplayName('byFields generates default field query | SQLite')
void byFieldsMultipleDefaultSQLite() {
ForceDialect.sqlite()
assertEquals("this WHERE data->>'a' = :the_a AND data->>'b' = :b_value",
QueryUtils.byFields('this', List.of(Field.equal('a', '', ':the_a'), Field.equal('b', 0, ':b_value'))))
}
@Test
@DisplayName('byFields generates ANY field query | PostgreSQL')
void byFieldsMultipleAnyPostgres() {
ForceDialect.postgres()
assertEquals("that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value",
QueryUtils.byFields('that', List.of(Field.equal('a', '', ':the_a'), Field.equal('b', 0, ':b_value')),
FieldMatch.ANY))
}
@Test
@DisplayName('byFields generates ANY field query | SQLite')
void byFieldsMultipleAnySQLite() {
ForceDialect.sqlite()
assertEquals("that WHERE data->>'a' = :the_a OR data->>'b' = :b_value",
QueryUtils.byFields('that', List.of(Field.equal('a', '', ':the_a'), Field.equal('b', 0, ':b_value')),
FieldMatch.ANY))
}
@Test
@DisplayName('orderBy generates for no fields')
void orderByNone() {
assertEquals('', QueryUtils.orderBy(List.of(), Dialect.POSTGRESQL),
'ORDER BY should have been blank (PostgreSQL)')
assertEquals '', QueryUtils.orderBy(List.of(), Dialect.SQLITE), 'ORDER BY should have been blank (SQLite)'
}
@Test
@DisplayName('orderBy generates single, no direction | PostgreSQL')
void orderBySinglePostgres() {
assertEquals(" ORDER BY data->>'TestField'",
QueryUtils.orderBy(List.of(Field.named('TestField')), Dialect.POSTGRESQL),
'ORDER BY not constructed correctly')
}
@Test
@DisplayName('orderBy generates single, no direction | SQLite')
void orderBySingleSQLite() {
assertEquals(" ORDER BY data->>'TestField'",
QueryUtils.orderBy(List.of(Field.named('TestField')), Dialect.SQLITE),
'ORDER BY not constructed correctly')
}
@Test
@DisplayName('orderBy generates multiple with direction | PostgreSQL')
void orderByMultiplePostgres() {
assertEquals(" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
QueryUtils.orderBy(List.of(Field.named('Nested.Test.Field DESC'), Field.named('AnotherField'),
Field.named('It DESC')),
Dialect.POSTGRESQL),
'ORDER BY not constructed correctly')
}
@Test
@DisplayName('orderBy generates multiple with direction | SQLite')
void orderByMultipleSQLite() {
assertEquals(" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
QueryUtils.orderBy(List.of(Field.named('Nested.Test.Field DESC'), Field.named('AnotherField'),
Field.named('It DESC')),
Dialect.SQLITE),
'ORDER BY not constructed correctly')
}
@Test
@DisplayName('orderBy generates numeric ordering | PostgreSQL')
void orderByNumericPostgres() {
assertEquals(" ORDER BY (data->>'Test')::numeric",
QueryUtils.orderBy(List.of(Field.named('n:Test')), Dialect.POSTGRESQL),
'ORDER BY not constructed correctly')
}
@Test
@DisplayName('orderBy generates numeric ordering | SQLite')
void orderByNumericSQLite() {
assertEquals(" ORDER BY data->>'Test'", QueryUtils.orderBy(List.of(Field.named('n:Test')), Dialect.SQLITE),
'ORDER BY not constructed correctly')
}
@Test
@DisplayName('orderBy generates case-insensitive ordering | PostgreSQL')
void orderByCIPostgres() {
assertEquals(" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST",
QueryUtils.orderBy(List.of(Field.named('i:Test.Field DESC NULLS FIRST')), Dialect.POSTGRESQL),
'ORDER BY not constructed correctly')
}
@Test
@DisplayName('orderBy generates case-insensitive ordering | SQLite')
void orderByCISQLite() {
assertEquals(" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST",
QueryUtils.orderBy(List.of(Field.named('i:Test.Field ASC NULLS LAST')), Dialect.SQLITE),
'ORDER BY not constructed correctly')
}
}

View File

@@ -0,0 +1,106 @@
package solutions.bitbadger.documents.groovy.tests
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 static Types.TEST_TABLE
import static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `RemoveFields` object
*/
@DisplayName('Groovy | Query | RemoveFieldsQuery')
class RemoveFieldsQueryTest {
/**
* Reset the dialect
*/
@AfterEach
void cleanUp() {
ForceDialect.none()
}
@Test
@DisplayName('byId generates correctly | PostgreSQL')
void byIdPostgres() {
ForceDialect.postgres()
assertEquals("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}'))),
'Remove Fields query not constructed correctly')
}
@Test
@DisplayName('byId generates correctly | SQLite')
void byIdSQLite() {
ForceDialect.sqlite()
assertEquals(
"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'))),
'Remove Field query not constructed correctly')
}
@Test
@DisplayName('byFields generates correctly | PostgreSQL')
void byFieldsPostgres() {
ForceDialect.postgres()
assertEquals("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'))),
'Remove Field query not constructed correctly')
}
@Test
@DisplayName('byFields generates correctly | SQLite')
void byFieldsSQLite() {
ForceDialect.sqlite()
assertEquals("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'))),
'Remove Field query not constructed correctly')
}
@Test
@DisplayName('byContains generates correctly | PostgreSQL')
void byContainsPostgres() {
ForceDialect.postgres()
assertEquals("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}'))),
'Remove Field query not constructed correctly')
}
@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(
"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}'))),
'Remove Field query not constructed correctly')
}
@Test
@DisplayName('byJsonPath fails | SQLite')
void byJsonPathSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { RemoveFieldsQuery.byJsonPath(TEST_TABLE, List.of()) }
}
}

View File

@@ -0,0 +1,9 @@
package solutions.bitbadger.documents.groovy.tests
class ShortIdClass {
short id
ShortIdClass(short id) {
this.id = id
}
}

View File

@@ -0,0 +1,9 @@
package solutions.bitbadger.documents.groovy.tests
class StringIdClass {
String id
StringIdClass(String id) {
this.id = id
}
}

View File

@@ -0,0 +1,6 @@
package solutions.bitbadger.documents.groovy.tests
final class Types {
public static final String TEST_TABLE = 'test_table'
}

View File

@@ -0,0 +1,172 @@
package solutions.bitbadger.documents.groovy.tests
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 static org.junit.jupiter.api.Assertions.*
/**
* Unit tests for the `Where` object
*/
@DisplayName('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')
}
@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')
}
@Test
@DisplayName('jsonPathMatches fails | SQLite')
void jsonPathFailsSQLite() {
ForceDialect.sqlite()
assertThrows(DocumentException) { Where.jsonPathMatches() }
}
}

View File

@@ -0,0 +1,18 @@
package solutions.bitbadger.documents.groovy.tests.integration
class ArrayDocument {
String id
List<String> values
ArrayDocument(String id = '', List<String> values = List.of()) {
this.id = id
this.values = values
}
/** A set of documents used for integration tests */
static List<ArrayDocument> testDocuments = List.of(
new ArrayDocument("first", List.of("a", "b", "c")),
new ArrayDocument("second", List.of("c", "d", "e")),
new ArrayDocument("third", List.of("x", "y", "z")))
}

View File

@@ -0,0 +1,50 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.Field
import static org.junit.jupiter.api.Assertions.*
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
final class CountFunctions {
static void all(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should have been 5 documents in the table'
}
static void byFieldsNumeric(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(3L, db.conn.countByFields(TEST_TABLE, List.of(Field.between('numValue', 10, 20))),
'There should have been 3 matching documents')
}
static void byFieldsAlpha(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(1L, db.conn.countByFields(TEST_TABLE, List.of(Field.between('value', 'aardvark', 'apple'))),
'There should have been 1 matching document')
}
static void byContainsMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(2L, db.conn.countByContains(TEST_TABLE, Map.of('value', 'purple')),
'There should have been 2 matching documents')
}
static void byContainsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(0L, db.conn.countByContains(TEST_TABLE, Map.of('value', 'magenta')),
'There should have been no matching documents')
}
static void byJsonPathMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(2L, db.conn.countByJsonPath(TEST_TABLE, '$.numValue ? (@ < 5)'),
'There should have been 2 matching documents')
}
static void byJsonPathNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(0L, db.conn.countByJsonPath(TEST_TABLE, '$.numValue ? (@ > 100)'),
'There should have been no matching documents')
}
}

View File

@@ -0,0 +1,69 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.Parameter
import solutions.bitbadger.documents.ParameterType
import solutions.bitbadger.documents.java.Results
import solutions.bitbadger.documents.query.CountQuery
import solutions.bitbadger.documents.query.DeleteQuery
import solutions.bitbadger.documents.query.FindQuery
import static org.junit.jupiter.api.Assertions.*
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
final class CustomFunctions {
static void listEmpty(ThrowawayDatabase db) {
JsonDocument.load db
db.conn.deleteByFields TEST_TABLE, List.of(Field.exists(Configuration.idField))
def result = db.conn.customList FindQuery.all(TEST_TABLE), List.of(), JsonDocument, Results.&fromData
assertEquals 0, result.size(), 'There should have been no results'
}
static void listAll(ThrowawayDatabase db) {
JsonDocument.load db
def result = db.conn.customList FindQuery.all(TEST_TABLE), List.of(), JsonDocument, Results.&fromData
assertEquals 5, result.size(), 'There should have been 5 results'
}
static void singleNone(ThrowawayDatabase db) {
assertFalse(db.conn.customSingle(FindQuery.all(TEST_TABLE), List.of(), JsonDocument, Results.&fromData)
.isPresent(),
'There should not have been a document returned')
}
static void singleOne(ThrowawayDatabase db) {
JsonDocument.load db
assertTrue(db.conn.customSingle(FindQuery.all(TEST_TABLE), List.of(), JsonDocument, Results.&fromData)
.isPresent(),
'There should not have been a document returned')
}
static void nonQueryChanges(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), List.of(), Long, Results.&toCount),
'There should have been 5 documents in the table')
db.conn.customNonQuery("DELETE FROM $TEST_TABLE")
assertEquals(0L, db.conn.customScalar(CountQuery.all(TEST_TABLE), List.of(), Long, Results.&toCount),
'There should have been no documents in the table')
}
static void nonQueryNoChanges(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), List.of(), Long, Results.&toCount),
'There should have been 5 documents in the table')
db.conn.customNonQuery(DeleteQuery.byId(TEST_TABLE, 'eighty-two'),
List.of(new Parameter(':id', ParameterType.STRING, 'eighty-two')))
assertEquals(5L, db.conn.customScalar(CountQuery.all(TEST_TABLE), List.of(), Long, Results.&toCount),
'There should still have been 5 documents in the table')
}
static void scalar(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(3L,
db.conn.customScalar("SELECT 3 AS it FROM $TEST_TABLE LIMIT 1", List.of(), Long, Results.&toCount),
'The number 3 should have been returned')
}
}

View File

@@ -0,0 +1,41 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.DocumentIndex
import static org.junit.jupiter.api.Assertions.*
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
final class DefinitionFunctions {
static void ensureTable(ThrowawayDatabase db) {
assertFalse db.dbObjectExists('ensured'), 'The "ensured" table should not exist'
assertFalse db.dbObjectExists('idx_ensured_key'), 'The PK index for the "ensured" table should not exist'
db.conn.ensureTable 'ensured'
assertTrue db.dbObjectExists('ensured'), 'The "ensured" table should exist'
assertTrue db.dbObjectExists('idx_ensured_key'), 'The PK index for the "ensured" table should now exist'
}
static void ensureFieldIndex(ThrowawayDatabase db) {
assertFalse db.dbObjectExists("idx_${TEST_TABLE}_test"), 'The test index should not exist'
db.conn.ensureFieldIndex TEST_TABLE, 'test', List.of('id', 'category')
assertTrue db.dbObjectExists("idx_${TEST_TABLE}_test"), 'The test index should now exist'
}
static void ensureDocumentIndexFull(ThrowawayDatabase db) {
assertFalse db.dbObjectExists('doc_table'), 'The "doc_table" table should not exist'
db.conn.ensureTable 'doc_table'
assertTrue db.dbObjectExists('doc_table'), 'The "doc_table" table should exist'
assertFalse db.dbObjectExists('idx_doc_table_document'), 'The document index should not exist'
db.conn.ensureDocumentIndex 'doc_table', DocumentIndex.FULL
assertTrue db.dbObjectExists('idx_doc_table_document'), 'The document index should exist'
}
static void ensureDocumentIndexOptimized(ThrowawayDatabase db) {
assertFalse db.dbObjectExists('doc_table'), 'The "doc_table" table should not exist'
db.conn.ensureTable 'doc_table'
assertTrue db.dbObjectExists('doc_table'), 'The "doc_table" table should exist'
assertFalse db.dbObjectExists('idx_doc_table_document'), 'The document index should not exist'
db.conn.ensureDocumentIndex 'doc_table', DocumentIndex.OPTIMIZED
assertTrue db.dbObjectExists('idx_doc_table_document'), 'The document index should exist'
}
}

View File

@@ -0,0 +1,65 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.Field
import static org.junit.jupiter.api.Assertions.*
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
final class DeleteFunctions {
static void byIdMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table'
db.conn.deleteById TEST_TABLE, 'four'
assertEquals 4L, db.conn.countAll(TEST_TABLE), 'There should now be 4 documents in the table'
}
static void byIdNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table'
db.conn.deleteById TEST_TABLE, 'negative four'
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should still be 5 documents in the table'
}
static void byFieldsMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table'
db.conn.deleteByFields TEST_TABLE, List.of(Field.notEqual('value', 'purple'))
assertEquals 2L, db.conn.countAll(TEST_TABLE), 'There should now be 2 documents in the table'
}
static void byFieldsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table'
db.conn.deleteByFields TEST_TABLE, List.of(Field.equal('value', 'crimson'))
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should still be 5 documents in the table'
}
static void byContainsMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table'
db.conn.deleteByContains TEST_TABLE, Map.of('value', 'purple')
assertEquals 3L, db.conn.countAll(TEST_TABLE), 'There should now be 3 documents in the table'
}
static void byContainsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table'
db.conn.deleteByContains TEST_TABLE, Map.of('target', 'acquired')
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should still be 5 documents in the table'
}
static void byJsonPathMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table'
db.conn.deleteByJsonPath TEST_TABLE, '$.value ? (@ == "purple")'
assertEquals 3L, db.conn.countAll(TEST_TABLE), 'There should now be 3 documents in the table'
}
static void byJsonPathNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should be 5 documents in the table'
db.conn.deleteByJsonPath TEST_TABLE, '$.numValue ? (@ > 100)'
assertEquals 5L, db.conn.countAll(TEST_TABLE), 'There should still be 5 documents in the table'
}
}

View File

@@ -0,0 +1,129 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.AutoId
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field
import static org.junit.jupiter.api.Assertions.*
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
final class DocumentFunctions {
static void insertDefault(ThrowawayDatabase db) {
assertEquals 0L, db.conn.countAll(TEST_TABLE), 'There should be no documents in the table'
def doc = new JsonDocument('turkey', 'yum', 5, new SubDocument('gobble', 'gobble!'))
db.conn.insert TEST_TABLE, doc
def after = db.conn.findAll TEST_TABLE, JsonDocument
assertEquals 1, after.size(), 'There should be one document in the table'
assertEquals doc.id, after[0].id, 'The document ID was not inserted correctly'
assertEquals doc.value, after[0].value, 'The document value was not inserted correctly'
assertEquals doc.numValue, after[0].numValue, 'The document numValue was not inserted correctly'
assertNotNull doc.sub, "The subdocument was not inserted"
assertEquals doc.sub.foo, after[0].sub.foo, 'The subdocument "foo" property was not inserted correctly'
assertEquals doc.sub.bar, after[0].sub.bar, 'The subdocument "bar" property was not inserted correctly'
}
static void insertDupe(ThrowawayDatabase db) {
db.conn.insert TEST_TABLE, new JsonDocument('a', '', 0, null)
assertThrows(DocumentException, () -> db.conn.insert(TEST_TABLE, new JsonDocument('a', 'b', 22, null)),
'Inserting a document with a duplicate key should have thrown an exception')
}
static void insertNumAutoId(ThrowawayDatabase db) {
try {
Configuration.autoIdStrategy = AutoId.NUMBER
Configuration.idField = 'key'
assertEquals 0L, db.conn.countAll(TEST_TABLE), 'There should be no documents in the table'
db.conn.insert TEST_TABLE, new NumIdDocument(0, 'one')
db.conn.insert TEST_TABLE, new NumIdDocument(0, 'two')
db.conn.insert TEST_TABLE, new NumIdDocument(77, 'three')
db.conn.insert TEST_TABLE, new NumIdDocument(0, 'four')
def after = db.conn.findAll TEST_TABLE, NumIdDocument, List.of(Field.named('key'))
assertEquals 4, after.size(), 'There should have been 4 documents returned'
assertEquals('1|2|77|78',
after*.key*.toString().inject('') { acc, it -> acc == '' ? it : "$acc|$it" }.toString(),
'The IDs were not generated correctly')
} finally {
Configuration.autoIdStrategy = AutoId.DISABLED
Configuration.idField = 'id'
}
}
static void insertUUIDAutoId(ThrowawayDatabase db) {
try {
Configuration.autoIdStrategy = AutoId.UUID
assertEquals 0L, db.conn.countAll(TEST_TABLE), 'There should be no documents in the table'
db.conn.insert TEST_TABLE, new JsonDocument('')
def after = db.conn.findAll TEST_TABLE, JsonDocument
assertEquals 1, after.size(), 'There should have been 1 document returned'
assertEquals 32, after[0].id.length(), 'The ID was not generated correctly'
} finally {
Configuration.autoIdStrategy = AutoId.DISABLED
}
}
static void insertStringAutoId(ThrowawayDatabase db) {
try {
Configuration.autoIdStrategy = AutoId.RANDOM_STRING
assertEquals 0L, db.conn.countAll(TEST_TABLE), 'There should be no documents in the table'
db.conn.insert TEST_TABLE, new JsonDocument('')
Configuration.idStringLength = 21
db.conn.insert TEST_TABLE, new JsonDocument('')
def after = db.conn.findAll TEST_TABLE, JsonDocument
assertEquals 2, after.size(), 'There should have been 2 documents returned'
assertEquals 16, after[0].id.length(), "The first document's ID was not generated correctly"
assertEquals 21, after[1].id.length(), "The second document's ID was not generated correctly"
} finally {
Configuration.autoIdStrategy = AutoId.DISABLED
Configuration.idStringLength = 16
}
}
static void saveMatch(ThrowawayDatabase db) {
JsonDocument.load db
db.conn.save TEST_TABLE, new JsonDocument('two', '', 44)
def tryDoc = db.conn.findById TEST_TABLE, 'two', JsonDocument
assertTrue tryDoc.isPresent(), 'There should have been a document returned'
def doc = tryDoc.get()
assertEquals 'two', doc.id, 'An incorrect document was returned'
assertEquals '', doc.value, 'The "value" field was not updated'
assertEquals 44, doc.numValue, 'The "numValue" field was not updated'
assertNull doc.sub, 'The "sub" field was not updated'
}
static void saveNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
db.conn.save TEST_TABLE, new JsonDocument('test', '', 0, new SubDocument('a', 'b'))
assertTrue(db.conn.findById(TEST_TABLE, 'test', JsonDocument).isPresent(),
'The test document should have been saved')
}
static void updateMatch(ThrowawayDatabase db) {
JsonDocument.load db
db.conn.update TEST_TABLE, 'one', new JsonDocument('one', 'howdy', 8, new SubDocument('y', 'z'))
def tryDoc = db.conn.findById TEST_TABLE, 'one', JsonDocument
assertTrue tryDoc.isPresent(), 'There should have been a document returned'
def doc = tryDoc.get()
assertEquals 'one', doc.id, 'An incorrect document was returned'
assertEquals 'howdy', doc.value, 'The "value" field was not updated'
assertEquals 8, doc.numValue, 'The "numValue" field was not updated'
assertNotNull doc.sub, 'The sub-document should not be null'
assertEquals 'y', doc.sub.foo, 'The sub-document "foo" field was not updated'
assertEquals 'z', doc.sub.bar, 'The sub-document "bar" field was not updated'
}
static void updateNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse db.conn.existsById(TEST_TABLE, 'two-hundred')
db.conn.update TEST_TABLE, 'two-hundred', new JsonDocument('two-hundred', '', 200)
assertFalse db.conn.existsById(TEST_TABLE, 'two-hundred')
}
}

View File

@@ -0,0 +1,55 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.Field
import static org.junit.jupiter.api.Assertions.*
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
final class ExistsFunctions {
static void byIdMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertTrue db.conn.existsById(TEST_TABLE, 'three'), 'The document with ID "three" should exist'
}
static void byIdNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse db.conn.existsById(TEST_TABLE, 'seven'), 'The document with ID "seven" should not exist'
}
static void byFieldsMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertTrue(db.conn.existsByFields(TEST_TABLE, List.of(Field.equal('numValue', 10))),
'Matching documents should have been found')
}
static void byFieldsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse(db.conn.existsByFields(TEST_TABLE, List.of(Field.equal('nothing', 'none'))),
'No matching documents should have been found')
}
static void byContainsMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertTrue(db.conn.existsByContains(TEST_TABLE, Map.of('value', 'purple')),
'Matching documents should have been found')
}
static void byContainsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse(db.conn.existsByContains(TEST_TABLE, Map.of('value', 'violet')),
'Matching documents should not have been found')
}
static void byJsonPathMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertTrue(db.conn.existsByJsonPath(TEST_TABLE, '$.numValue ? (@ == 10)'),
'Matching documents should have been found')
}
static void byJsonPathNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse(db.conn.existsByJsonPath(TEST_TABLE, '$.numValue ? (@ == 10.1)'),
'Matching documents should not have been found')
}
}

View File

@@ -0,0 +1,249 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.FieldMatch
import static org.junit.jupiter.api.Assertions.*
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
final class FindFunctions {
private static String docIds(List<JsonDocument> docs) {
return docs*.id.inject('') { acc, it -> acc == '' ? it : "$acc|$it" }
}
static void allDefault(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals 5, db.conn.findAll(TEST_TABLE, JsonDocument).size(), 'There should have been 5 documents returned'
}
static void allAscending(ThrowawayDatabase db) {
JsonDocument.load db
def docs = db.conn.findAll TEST_TABLE, JsonDocument, List.of(Field.named('id'))
assertEquals 5, docs.size(), 'There should have been 5 documents returned'
assertEquals 'five|four|one|three|two', docIds(docs), 'The documents were not ordered correctly'
}
static void allDescending(ThrowawayDatabase db) {
JsonDocument.load db
def docs = db.conn.findAll TEST_TABLE, JsonDocument, List.of(Field.named('id DESC'))
assertEquals 5, docs.size(), 'There should have been 5 documents returned'
assertEquals 'two|three|one|four|five', docIds(docs), 'The documents were not ordered correctly'
}
static void allNumOrder(ThrowawayDatabase db) {
JsonDocument.load db
def docs = db.conn.findAll(TEST_TABLE, JsonDocument,
List.of(Field.named('sub.foo NULLS LAST'), Field.named('n:numValue')))
assertEquals 5, docs.size(), 'There should have been 5 documents returned'
assertEquals 'two|four|one|three|five', docIds(docs), 'The documents were not ordered correctly'
}
static void allEmpty(ThrowawayDatabase db) {
assertEquals 0, db.conn.findAll(TEST_TABLE, JsonDocument).size(), 'There should have been no documents returned'
}
static void byIdString(ThrowawayDatabase db) {
JsonDocument.load db
def doc = db.conn.findById TEST_TABLE, 'two', JsonDocument
assertTrue doc.isPresent(), 'The document should have been returned'
assertEquals 'two', doc.get().id, 'An incorrect document was returned'
}
static void byIdNumber(ThrowawayDatabase db) {
Configuration.idField = 'key'
try {
db.conn.insert TEST_TABLE, new NumIdDocument(18, 'howdy')
assertTrue(db.conn.findById(TEST_TABLE, 18, NumIdDocument).isPresent(),
'The document should have been returned')
} finally {
Configuration.idField = 'id'
}
}
static void byIdNotFound(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse(db.conn.findById(TEST_TABLE, 'x', JsonDocument).isPresent(),
'There should have been no document returned')
}
static void byFieldsMatch(ThrowawayDatabase db) {
JsonDocument.load db
def docs = db.conn.findByFields(TEST_TABLE,
List.of(Field.any('value', List.of('blue', 'purple')), Field.exists('sub')), JsonDocument,
FieldMatch.ALL)
assertEquals 1, docs.size(), 'There should have been a document returned'
assertEquals 'four', docs[0].id, 'The incorrect document was returned'
}
static void byFieldsMatchOrdered(ThrowawayDatabase db) {
JsonDocument.load db
def docs = db.conn.findByFields(TEST_TABLE, List.of(Field.equal('value', 'purple')), JsonDocument, null,
List.of(Field.named('id')))
assertEquals 2, docs.size(), 'There should have been 2 documents returned'
assertEquals 'five|four', docIds(docs), 'The documents were not ordered correctly'
}
static void byFieldsMatchNumIn(ThrowawayDatabase db) {
JsonDocument.load db
def docs = db.conn.findByFields TEST_TABLE, List.of(Field.any('numValue', List.of(2, 4, 6, 8))), JsonDocument
assertEquals 1, docs.size(), 'There should have been a document returned'
assertEquals 'three', docs[0].id, 'The incorrect document was returned'
}
static void byFieldsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(0, db.conn.findByFields(TEST_TABLE, List.of(Field.greater('numValue', 100)), JsonDocument).size(),
'There should have been no documents returned')
}
static void byFieldsMatchInArray(ThrowawayDatabase db) {
ArrayDocument.testDocuments.forEach { db.conn.insert TEST_TABLE, it }
def docs = db.conn.findByFields(TEST_TABLE, List.of(Field.inArray('values', TEST_TABLE, List.of('c'))),
ArrayDocument)
assertEquals 2, docs.size(), 'There should have been two documents returned'
assertTrue(List.of('first', 'second').contains(docs[0].id),
"An incorrect document was returned (${docs[0].id})")
assertTrue(List.of('first', 'second').contains(docs[1].id),
"An incorrect document was returned (${docs[1].id})")
}
static void byFieldsNoMatchInArray(ThrowawayDatabase db) {
ArrayDocument.testDocuments.forEach { db.conn.insert TEST_TABLE, it }
assertEquals(0,
db.conn.findByFields(TEST_TABLE, List.of(Field.inArray('values', TEST_TABLE, List.of('j'))),
ArrayDocument).size(),
'There should have been no documents returned')
}
static void byContainsMatch(ThrowawayDatabase db) {
JsonDocument.load db
def docs = db.conn.findByContains TEST_TABLE, Map.of('value', 'purple'), JsonDocument
assertEquals 2, docs.size(), 'There should have been 2 documents returned'
assertTrue List.of('four', 'five').contains(docs[0].id), "An incorrect document was returned (${docs[0].id})"
assertTrue List.of('four', 'five').contains(docs[1].id), "An incorrect document was returned (${docs[1].id})"
}
static void byContainsMatchOrdered(ThrowawayDatabase db) {
JsonDocument.load db
def docs = db.conn.findByContains(TEST_TABLE, Map.of('sub', Map.of('foo', 'green')), JsonDocument,
List.of(Field.named('value')))
assertEquals 2, docs.size(), 'There should have been 2 documents returned'
assertEquals 'two|four', docIds(docs), 'The documents were not ordered correctly'
}
static void byContainsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(0, db.conn.findByContains(TEST_TABLE, Map.of('value', 'indigo'), JsonDocument).size(),
'There should have been no documents returned')
}
static void byJsonPathMatch(ThrowawayDatabase db) {
JsonDocument.load db
def docs = db.conn.findByJsonPath TEST_TABLE, '$.numValue ? (@ > 10)', JsonDocument
assertEquals 2, docs.size(), 'There should have been 2 documents returned'
assertTrue List.of('four', 'five').contains(docs[0].id), "An incorrect document was returned (${docs[0].id})"
assertTrue List.of('four', 'five').contains(docs[1].id), "An incorrect document was returned (${docs[1].id})"
}
static void byJsonPathMatchOrdered(ThrowawayDatabase db) {
JsonDocument.load db
def docs = db.conn.findByJsonPath TEST_TABLE, '$.numValue ? (@ > 10)', JsonDocument, List.of(Field.named('id'))
assertEquals 2, docs.size(), 'There should have been 2 documents returned'
assertEquals 'five|four', docIds(docs), 'The documents were not ordered correctly'
}
static void byJsonPathNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertEquals(0, db.conn.findByJsonPath(TEST_TABLE, '$.numValue ? (@ > 100)', JsonDocument).size(),
'There should have been no documents returned')
}
static void firstByFieldsMatchOne(ThrowawayDatabase db) {
JsonDocument.load db
def doc = db.conn.findFirstByFields TEST_TABLE, List.of(Field.equal('value', 'another')), JsonDocument
assertTrue doc.isPresent(), 'There should have been a document returned'
assertEquals 'two', doc.get().id, 'The incorrect document was returned'
}
static void firstByFieldsMatchMany(ThrowawayDatabase db) {
JsonDocument.load db
def doc = db.conn.findFirstByFields TEST_TABLE, List.of(Field.equal('sub.foo', 'green')), JsonDocument
assertTrue doc.isPresent(), 'There should have been a document returned'
assertTrue List.of('two', 'four').contains(doc.get().id), "An incorrect document was returned (${doc.get().id})"
}
static void firstByFieldsMatchOrdered(ThrowawayDatabase db) {
JsonDocument.load db
def doc = db.conn.findFirstByFields(TEST_TABLE, List.of(Field.equal('sub.foo', 'green')), JsonDocument, null,
List.of(Field.named('n:numValue DESC')))
assertTrue doc.isPresent(), 'There should have been a document returned'
assertEquals 'four', doc.get().id, 'An incorrect document was returned'
}
static void firstByFieldsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse(db.conn.findFirstByFields(TEST_TABLE, List.of(Field.equal('value', 'absent')), JsonDocument)
.isPresent(),
'There should have been no document returned')
}
static void firstByContainsMatchOne(ThrowawayDatabase db) {
JsonDocument.load db
def doc = db.conn.findFirstByContains TEST_TABLE, Map.of('value', 'FIRST!'), JsonDocument
assertTrue doc.isPresent(), 'There should have been a document returned'
assertEquals 'one', doc.get().id, 'An incorrect document was returned'
}
static void firstByContainsMatchMany(ThrowawayDatabase db) {
JsonDocument.load db
def doc = db.conn.findFirstByContains TEST_TABLE, Map.of('value', 'purple'), JsonDocument
assertTrue doc.isPresent(), 'There should have been a document returned'
assertTrue(List.of('four', 'five').contains(doc.get().id),
"An incorrect document was returned (${doc.get().id})")
}
static void firstByContainsMatchOrdered(ThrowawayDatabase db) {
JsonDocument.load db
def doc = db.conn.findFirstByContains(TEST_TABLE, Map.of('value', 'purple'), JsonDocument,
List.of(Field.named('sub.bar NULLS FIRST')))
assertTrue doc.isPresent(), 'There should have been a document returned'
assertEquals 'five', doc.get().id, 'An incorrect document was returned'
}
static void firstByContainsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse(db.conn.findFirstByContains(TEST_TABLE, Map.of('value', 'indigo'), JsonDocument).isPresent(),
'There should have been no document returned')
}
static void firstByJsonPathMatchOne(ThrowawayDatabase db) {
JsonDocument.load db
def doc = db.conn.findFirstByJsonPath TEST_TABLE, '$.numValue ? (@ == 10)', JsonDocument
assertTrue doc.isPresent(), 'There should have been a document returned'
assertEquals 'two', doc.get().id, 'An incorrect document was returned'
}
static void firstByJsonPathMatchMany(ThrowawayDatabase db) {
JsonDocument.load db
def doc = db.conn.findFirstByJsonPath TEST_TABLE, '$.numValue ? (@ > 10)', JsonDocument
assertTrue doc.isPresent(), 'There should have been a document returned'
assertTrue(List.of('four', 'five').contains(doc.get().id),
"An incorrect document was returned (${doc.get().id})")
}
static void firstByJsonPathMatchOrdered(ThrowawayDatabase db) {
JsonDocument.load db
def doc = db.conn.findFirstByJsonPath(TEST_TABLE, '$.numValue ? (@ > 10)', JsonDocument,
List.of(Field.named('id DESC')))
assertTrue doc.isPresent(), 'There should have been a document returned'
assertEquals 'four', doc.get().id, 'An incorrect document was returned'
}
static void firstByJsonPathNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse(db.conn.findFirstByJsonPath(TEST_TABLE, '$.numValue ? (@ > 100)', JsonDocument).isPresent(),
'There should have been no document returned')
}
}

View File

@@ -0,0 +1,22 @@
package solutions.bitbadger.documents.groovy.tests.integration
import com.fasterxml.jackson.databind.ObjectMapper
import solutions.bitbadger.documents.DocumentSerializer
/**
* A JSON serializer using Jackson's default options
*/
class JacksonDocumentSerializer implements DocumentSerializer {
private def mapper = new ObjectMapper()
@Override
def <TDoc> String serialize(TDoc document) {
return mapper.writeValueAsString(document)
}
@Override
def <TDoc> TDoc deserialize(String json, Class<TDoc> clazz) {
return mapper.readValue(json, clazz)
}
}

View File

@@ -0,0 +1,28 @@
package solutions.bitbadger.documents.groovy.tests.integration
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
class JsonDocument {
String id
String value
int numValue
SubDocument sub
JsonDocument(String id = null, String value = "", int numValue = 0, SubDocument sub = null) {
this.id = id
this.value = value
this.numValue = numValue
this.sub = sub
}
private static final List<JsonDocument> testDocuments = List.of(
new JsonDocument("one", "FIRST!", 0),
new JsonDocument("two", "another", 10, new SubDocument("green", "blue")),
new JsonDocument("three", "", 4),
new JsonDocument("four", "purple", 17, new SubDocument("green", "red")),
new JsonDocument("five", "purple", 18))
static void load(ThrowawayDatabase db, String tableName = TEST_TABLE) {
testDocuments.forEach { db.conn.insert(tableName, it) }
}
}

View File

@@ -0,0 +1,11 @@
package solutions.bitbadger.documents.groovy.tests.integration
class NumIdDocument {
int key
String value
NumIdDocument(int key = 0, String value = "") {
this.key = key
this.value = value
}
}

View File

@@ -0,0 +1,75 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.Field
import static org.junit.jupiter.api.Assertions.*
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
final class PatchFunctions {
static void byIdMatch(ThrowawayDatabase db) {
JsonDocument.load db
db.conn.patchById TEST_TABLE, 'one', Map.of('numValue', 44)
def doc = db.conn.findById TEST_TABLE, 'one', JsonDocument
assertTrue doc.isPresent(), 'There should have been a document returned'
assertEquals 'one', doc.get().id, 'An incorrect document was returned'
assertEquals 44, doc.get().numValue, 'The document was not patched'
}
static void byIdNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse db.conn.existsById(TEST_TABLE, 'forty-seven'), 'Document with ID "forty-seven" should not exist'
db.conn.patchById TEST_TABLE, 'forty-seven', Map.of('foo', 'green') // no exception = pass
}
static void byFieldsMatch(ThrowawayDatabase db) {
JsonDocument.load db
db.conn.patchByFields TEST_TABLE, List.of(Field.equal('value', 'purple')), Map.of('numValue', 77)
assertEquals(2, db.conn.countByFields(TEST_TABLE, List.of(Field.equal('numValue', 77))),
'There should have been 2 documents with numeric value 77')
}
static void byFieldsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
def fields = List.of Field.equal('value', 'burgundy')
assertFalse db.conn.existsByFields(TEST_TABLE, fields), 'There should be no documents with value of "burgundy"'
db.conn.patchByFields TEST_TABLE, fields, Map.of('foo', 'green') // no exception = pass
}
static void byContainsMatch(ThrowawayDatabase db) {
JsonDocument.load db
def contains = Map.of 'value', 'another'
db.conn.patchByContains TEST_TABLE, contains, Map.of('numValue', 12)
def doc = db.conn.findFirstByContains TEST_TABLE, contains, JsonDocument
assertTrue doc.isPresent(), 'There should have been a document returned'
assertEquals 'two', doc.get().id, 'The incorrect document was returned'
assertEquals 12, doc.get().numValue, 'The document was not updated'
}
static void byContainsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
def contains = Map.of 'value', 'updated'
assertFalse db.conn.existsByContains(TEST_TABLE, contains), 'There should be no matching documents'
db.conn.patchByContains TEST_TABLE, contains, Map.of('sub.foo', 'green') // no exception = pass
}
static void byJsonPathMatch(ThrowawayDatabase db) {
JsonDocument.load db
def path = '$.numValue ? (@ > 10)'
db.conn.patchByJsonPath TEST_TABLE, path, Map.of('value', 'blue')
def docs = db.conn.findByJsonPath TEST_TABLE, path, JsonDocument
assertEquals 2, docs.size(), 'There should have been two documents returned'
docs.forEach {
assertTrue List.of('four', 'five').contains(it.id), "An incorrect document was returned (${it.id})"
assertEquals 'blue', it.value, "The value for ID ${it.id} was incorrect"
}
}
static void byJsonPathNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
def path = '$.numValue ? (@ > 100)'
assertFalse(db.conn.existsByJsonPath(TEST_TABLE, path),
'There should be no documents with numeric values over 100')
db.conn.patchByJsonPath TEST_TABLE, path, Map.of('value', 'blue') // no exception = pass
}
}

View File

@@ -0,0 +1,50 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Parameter
import solutions.bitbadger.documents.ParameterType
import solutions.bitbadger.documents.java.DocumentConfig
import solutions.bitbadger.documents.java.Results
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
/**
* A wrapper for a throwaway PostgreSQL database
*/
class PgDB implements ThrowawayDatabase {
PgDB() {
Configuration.setConnectionString connString('postgres')
Configuration.dbConn().withCloseable { it.customNonQuery "CREATE DATABASE $dbName" }
Configuration.setConnectionString connString(dbName)
conn = Configuration.dbConn()
conn.ensureTable TEST_TABLE
// Use a Jackson-based document serializer for testing
DocumentConfig.serializer = new JacksonDocumentSerializer()
}
void close() {
conn.close()
Configuration.setConnectionString connString('postgres')
Configuration.dbConn().withCloseable { it.customNonQuery "DROP DATABASE $dbName" }
Configuration.setConnectionString null
}
boolean dbObjectExists(String name) {
conn.customScalar('SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = :name) AS it',
List.of(new Parameter(':name', ParameterType.STRING, name)), Boolean, Results.&toExists)
}
/**
* Create a connection string for the given database
*
* @param database The database to which the library should connect
* @return The connection string for the database
*/
private static String connString(String database) {
return "jdbc:postgresql://localhost/$database?user=postgres&password=postgres"
}
}

View File

@@ -0,0 +1,53 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* PostgreSQL integration tests for the `Count` object / `count*` connection extension functions
*/
@DisplayName('Groovy | PostgreSQL: Count')
final class PostgreSQLCountIT {
@Test
@DisplayName('all counts all documents')
void all() {
new PgDB().withCloseable CountFunctions.&all
}
@Test
@DisplayName('byFields counts documents by a numeric value')
void byFieldsNumeric() {
new PgDB().withCloseable CountFunctions.&byFieldsNumeric
}
@Test
@DisplayName('byFields counts documents by a alphanumeric value')
void byFieldsAlpha() {
new PgDB().withCloseable CountFunctions.&byFieldsAlpha
}
@Test
@DisplayName('byContains counts documents when matches are found')
void byContainsMatch() {
new PgDB().withCloseable CountFunctions.&byContainsMatch
}
@Test
@DisplayName('byContains counts documents when no matches are found')
void byContainsNoMatch() {
new PgDB().withCloseable CountFunctions.&byContainsNoMatch
}
@Test
@DisplayName('byJsonPath counts documents when matches are found')
void byJsonPathMatch() {
new PgDB().withCloseable CountFunctions.&byJsonPathMatch
}
@Test
@DisplayName('byJsonPath counts documents when no matches are found')
void byJsonPathNoMatch() {
new PgDB().withCloseable CountFunctions.&byJsonPathNoMatch
}
}

View File

@@ -0,0 +1,53 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* PostgreSQL integration tests for the `Custom` object / `custom*` connection extension functions
*/
@DisplayName('Groovy | PostgreSQL: Custom')
final class PostgreSQLCustomIT {
@Test
@DisplayName('list succeeds with empty list')
void listEmpty() {
new PgDB().withCloseable CustomFunctions.&listEmpty
}
@Test
@DisplayName('list succeeds with a non-empty list')
void listAll() {
new PgDB().withCloseable CustomFunctions.&listAll
}
@Test
@DisplayName('single succeeds when document not found')
void singleNone() {
new PgDB().withCloseable CustomFunctions.&singleNone
}
@Test
@DisplayName('single succeeds when a document is found')
void singleOne() {
new PgDB().withCloseable CustomFunctions.&singleOne
}
@Test
@DisplayName('nonQuery makes changes')
void nonQueryChanges() {
new PgDB().withCloseable CustomFunctions.&nonQueryChanges
}
@Test
@DisplayName('nonQuery makes no changes when where clause matches nothing')
void nonQueryNoChanges() {
new PgDB().withCloseable CustomFunctions.&nonQueryNoChanges
}
@Test
@DisplayName('scalar succeeds')
void scalar() {
new PgDB().withCloseable CustomFunctions.&scalar
}
}

View File

@@ -0,0 +1,35 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* PostgreSQL integration tests for the `Definition` object / `ensure*` connection extension functions
*/
@DisplayName('Groovy | PostgreSQL: Definition')
final class PostgreSQLDefinitionIT {
@Test
@DisplayName('ensureTable creates table and index')
void ensureTable() {
new PgDB().withCloseable DefinitionFunctions.&ensureTable
}
@Test
@DisplayName('ensureFieldIndex creates an index')
void ensureFieldIndex() {
new PgDB().withCloseable DefinitionFunctions.&ensureFieldIndex
}
@Test
@DisplayName('ensureDocumentIndex creates a full index')
void ensureDocumentIndexFull() {
new PgDB().withCloseable DefinitionFunctions.&ensureDocumentIndexFull
}
@Test
@DisplayName('ensureDocumentIndex creates an optimized index')
void ensureDocumentIndexOptimized() {
new PgDB().withCloseable DefinitionFunctions.&ensureDocumentIndexOptimized
}
}

View File

@@ -0,0 +1,59 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* PostgreSQL integration tests for the `Delete` object / `deleteBy*` connection extension functions
*/
@DisplayName('Groovy | PostgreSQL: Delete')
final class PostgreSQLDeleteIT {
@Test
@DisplayName('byId deletes a matching ID')
void byIdMatch() {
new PgDB().withCloseable DeleteFunctions.&byIdMatch
}
@Test
@DisplayName('byId succeeds when no ID matches')
void byIdNoMatch() {
new PgDB().withCloseable DeleteFunctions.&byIdNoMatch
}
@Test
@DisplayName('byFields deletes matching documents')
void byFieldsMatch() {
new PgDB().withCloseable DeleteFunctions.&byFieldsMatch
}
@Test
@DisplayName('byFields succeeds when no documents match')
void byFieldsNoMatch() {
new PgDB().withCloseable DeleteFunctions.&byFieldsNoMatch
}
@Test
@DisplayName('byContains deletes matching documents')
void byContainsMatch() {
new PgDB().withCloseable DeleteFunctions.&byContainsMatch
}
@Test
@DisplayName('byContains succeeds when no documents match')
void byContainsNoMatch() {
new PgDB().withCloseable DeleteFunctions.&byContainsNoMatch
}
@Test
@DisplayName('byJsonPath deletes matching documents')
void byJsonPathMatch() {
new PgDB().withCloseable DeleteFunctions.&byJsonPathMatch
}
@Test
@DisplayName('byJsonPath succeeds when no documents match')
void byJsonPathNoMatch() {
new PgDB().withCloseable DeleteFunctions.&byJsonPathNoMatch
}
}

View File

@@ -0,0 +1,65 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* PostgreSQL integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
*/
@DisplayName('Groovy | PostgreSQL: Document')
final class PostgreSQLDocumentIT {
@Test
@DisplayName('insert works with default values')
void insertDefault() {
new PgDB().withCloseable DocumentFunctions.&insertDefault
}
@Test
@DisplayName('insert fails with duplicate key')
void insertDupe() {
new PgDB().withCloseable DocumentFunctions.&insertDupe
}
@Test
@DisplayName('insert succeeds with numeric auto IDs')
void insertNumAutoId() {
new PgDB().withCloseable DocumentFunctions.&insertNumAutoId
}
@Test
@DisplayName('insert succeeds with UUID auto ID')
void insertUUIDAutoId() {
new PgDB().withCloseable DocumentFunctions.&insertUUIDAutoId
}
@Test
@DisplayName('insert succeeds with random string auto ID')
void insertStringAutoId() {
new PgDB().withCloseable DocumentFunctions.&insertStringAutoId
}
@Test
@DisplayName('save updates an existing document')
void saveMatch() {
new PgDB().withCloseable DocumentFunctions.&saveMatch
}
@Test
@DisplayName('save inserts a new document')
void saveNoMatch() {
new PgDB().withCloseable DocumentFunctions.&saveNoMatch
}
@Test
@DisplayName('update replaces an existing document')
void updateMatch() {
new PgDB().withCloseable DocumentFunctions.&updateMatch
}
@Test
@DisplayName('update succeeds when no document exists')
void updateNoMatch() {
new PgDB().withCloseable DocumentFunctions.&updateNoMatch
}
}

View File

@@ -0,0 +1,59 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* PostgreSQL integration tests for the `Exists` object / `existsBy*` connection extension functions
*/
@DisplayName('Groovy | PostgreSQL: Exists')
final class PostgreSQLExistsIT {
@Test
@DisplayName('byId returns true when a document matches the ID')
void byIdMatch() {
new PgDB().withCloseable ExistsFunctions.&byIdMatch
}
@Test
@DisplayName('byId returns false when no document matches the ID')
void byIdNoMatch() {
new PgDB().withCloseable ExistsFunctions.&byIdNoMatch
}
@Test
@DisplayName('byFields returns true when documents match')
void byFieldsMatch() {
new PgDB().withCloseable ExistsFunctions.&byFieldsMatch
}
@Test
@DisplayName('byFields returns false when no documents match')
void byFieldsNoMatch() {
new PgDB().withCloseable ExistsFunctions.&byFieldsNoMatch
}
@Test
@DisplayName('byContains returns true when documents match')
void byContainsMatch() {
new PgDB().withCloseable ExistsFunctions.&byContainsMatch
}
@Test
@DisplayName('byContains returns false when no documents match')
void byContainsNoMatch() {
new PgDB().withCloseable ExistsFunctions.&byContainsNoMatch
}
@Test
@DisplayName('byJsonPath returns true when documents match')
void byJsonPathMatch() {
new PgDB().withCloseable ExistsFunctions.&byJsonPathMatch
}
@Test
@DisplayName('byJsonPath returns false when no documents match')
void byJsonPathNoMatch() {
new PgDB().withCloseable ExistsFunctions.&byJsonPathNoMatch
}
}

View File

@@ -0,0 +1,203 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* PostgreSQL integration tests for the `Find` object / `find*` connection extension functions
*/
@DisplayName('Groovy | PostgreSQL: Find')
final class PostgreSQLFindIT {
@Test
@DisplayName('all retrieves all documents')
void allDefault() {
new PgDB().withCloseable FindFunctions.&allDefault
}
@Test
@DisplayName('all sorts data ascending')
void allAscending() {
new PgDB().withCloseable FindFunctions.&allAscending
}
@Test
@DisplayName('all sorts data descending')
void allDescending() {
new PgDB().withCloseable FindFunctions.&allDescending
}
@Test
@DisplayName('all sorts data numerically')
void allNumOrder() {
new PgDB().withCloseable FindFunctions.&allNumOrder
}
@Test
@DisplayName('all succeeds with an empty table')
void allEmpty() {
new PgDB().withCloseable FindFunctions.&allEmpty
}
@Test
@DisplayName('byId retrieves a document via a string ID')
void byIdString() {
new PgDB().withCloseable FindFunctions.&byIdString
}
@Test
@DisplayName('byId retrieves a document via a numeric ID')
void byIdNumber() {
new PgDB().withCloseable FindFunctions.&byIdNumber
}
@Test
@DisplayName('byId returns null when a matching ID is not found')
void byIdNotFound() {
new PgDB().withCloseable FindFunctions.&byIdNotFound
}
@Test
@DisplayName('byFields retrieves matching documents')
void byFieldsMatch() {
new PgDB().withCloseable FindFunctions.&byFieldsMatch
}
@Test
@DisplayName('byFields retrieves ordered matching documents')
void byFieldsMatchOrdered() {
new PgDB().withCloseable FindFunctions.&byFieldsMatchOrdered
}
@Test
@DisplayName('byFields retrieves matching documents with a numeric IN clause')
void byFieldsMatchNumIn() {
new PgDB().withCloseable FindFunctions.&byFieldsMatchNumIn
}
@Test
@DisplayName('byFields succeeds when no documents match')
void byFieldsNoMatch() {
new PgDB().withCloseable FindFunctions.&byFieldsNoMatch
}
@Test
@DisplayName('byFields retrieves matching documents with an IN_ARRAY comparison')
void byFieldsMatchInArray() {
new PgDB().withCloseable FindFunctions.&byFieldsMatchInArray
}
@Test
@DisplayName('byFields succeeds when no documents match an IN_ARRAY comparison')
void byFieldsNoMatchInArray() {
new PgDB().withCloseable FindFunctions.&byFieldsNoMatchInArray
}
@Test
@DisplayName('byContains retrieves matching documents')
void byContainsMatch() {
new PgDB().withCloseable FindFunctions.&byContainsMatch
}
@Test
@DisplayName('byContains retrieves ordered matching documents')
void byContainsMatchOrdered() {
new PgDB().withCloseable FindFunctions.&byContainsMatchOrdered
}
@Test
@DisplayName('byContains succeeds when no documents match')
void byContainsNoMatch() {
new PgDB().withCloseable FindFunctions.&byContainsNoMatch
}
@Test
@DisplayName('byJsonPath retrieves matching documents')
void byJsonPathMatch() {
new PgDB().withCloseable FindFunctions.&byJsonPathMatch
}
@Test
@DisplayName('byJsonPath retrieves ordered matching documents')
void byJsonPathMatchOrdered() {
new PgDB().withCloseable FindFunctions.&byJsonPathMatchOrdered
}
@Test
@DisplayName('byJsonPath succeeds when no documents match')
void byJsonPathNoMatch() {
new PgDB().withCloseable FindFunctions.&byJsonPathNoMatch
}
@Test
@DisplayName('firstByFields retrieves a matching document')
void firstByFieldsMatchOne() {
new PgDB().withCloseable FindFunctions.&firstByFieldsMatchOne
}
@Test
@DisplayName('firstByFields retrieves a matching document among many')
void firstByFieldsMatchMany() {
new PgDB().withCloseable FindFunctions.&firstByFieldsMatchMany
}
@Test
@DisplayName('firstByFields retrieves a matching document among many (ordered)')
void firstByFieldsMatchOrdered() {
new PgDB().withCloseable FindFunctions.&firstByFieldsMatchOrdered
}
@Test
@DisplayName('firstByFields returns null when no document matches')
void firstByFieldsNoMatch() {
new PgDB().withCloseable FindFunctions.&firstByFieldsNoMatch
}
@Test
@DisplayName('firstByContains retrieves a matching document')
void firstByContainsMatchOne() {
new PgDB().withCloseable FindFunctions.&firstByContainsMatchOne
}
@Test
@DisplayName('firstByContains retrieves a matching document among many')
void firstByContainsMatchMany() {
new PgDB().withCloseable FindFunctions.&firstByContainsMatchMany
}
@Test
@DisplayName('firstByContains retrieves a matching document among many (ordered)')
void firstByContainsMatchOrdered() {
new PgDB().withCloseable FindFunctions.&firstByContainsMatchOrdered
}
@Test
@DisplayName('firstByContains returns null when no document matches')
void firstByContainsNoMatch() {
new PgDB().withCloseable FindFunctions.&firstByContainsNoMatch
}
@Test
@DisplayName('firstByJsonPath retrieves a matching document')
void firstByJsonPathMatchOne() {
new PgDB().withCloseable FindFunctions.&firstByJsonPathMatchOne
}
@Test
@DisplayName('firstByJsonPath retrieves a matching document among many')
void firstByJsonPathMatchMany() {
new PgDB().withCloseable FindFunctions.&firstByJsonPathMatchMany
}
@Test
@DisplayName('firstByJsonPath retrieves a matching document among many (ordered)')
void firstByJsonPathMatchOrdered() {
new PgDB().withCloseable FindFunctions.&firstByJsonPathMatchOrdered
}
@Test
@DisplayName('firstByJsonPath returns null when no document matches')
void firstByJsonPathNoMatch() {
new PgDB().withCloseable FindFunctions.&firstByJsonPathNoMatch
}
}

View File

@@ -0,0 +1,59 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* PostgreSQL integration tests for the `Patch` object / `patchBy*` connection extension functions
*/
@DisplayName('Groovy | PostgreSQL: Patch')
final class PostgreSQLPatchIT {
@Test
@DisplayName('byId patches an existing document')
void byIdMatch() {
new PgDB().withCloseable PatchFunctions.&byIdMatch
}
@Test
@DisplayName('byId succeeds for a non-existent document')
void byIdNoMatch() {
new PgDB().withCloseable PatchFunctions.&byIdNoMatch
}
@Test
@DisplayName('byFields patches matching document')
void byFieldsMatch() {
new PgDB().withCloseable PatchFunctions.&byFieldsMatch
}
@Test
@DisplayName('byFields succeeds when no documents match')
void byFieldsNoMatch() {
new PgDB().withCloseable PatchFunctions.&byFieldsNoMatch
}
@Test
@DisplayName('byContains patches matching document')
void byContainsMatch() {
new PgDB().withCloseable PatchFunctions.&byContainsMatch
}
@Test
@DisplayName('byContains succeeds when no documents match')
void byContainsNoMatch() {
new PgDB().withCloseable PatchFunctions.&byContainsNoMatch
}
@Test
@DisplayName('byJsonPath patches matching document')
void byJsonPathMatch() {
new PgDB().withCloseable PatchFunctions.&byJsonPathMatch
}
@Test
@DisplayName('byJsonPath succeeds when no documents match')
void byJsonPathNoMatch() {
new PgDB().withCloseable PatchFunctions.&byJsonPathNoMatch
}
}

View File

@@ -0,0 +1,83 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* PostgreSQL integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
*/
@DisplayName('Groovy | PostgreSQL: RemoveFields')
final class PostgreSQLRemoveFieldsIT {
@Test
@DisplayName('byId removes fields from an existing document')
void byIdMatchFields() {
new PgDB().withCloseable RemoveFieldsFunctions.&byIdMatchFields
}
@Test
@DisplayName('byId succeeds when fields do not exist on an existing document')
void byIdMatchNoFields() {
new PgDB().withCloseable RemoveFieldsFunctions.&byIdMatchNoFields
}
@Test
@DisplayName('byId succeeds when no document exists')
void byIdNoMatch() {
new PgDB().withCloseable RemoveFieldsFunctions.&byIdNoMatch
}
@Test
@DisplayName('byFields removes fields from matching documents')
void byFieldsMatchFields() {
new PgDB().withCloseable RemoveFieldsFunctions.&byFieldsMatchFields
}
@Test
@DisplayName('byFields succeeds when fields do not exist on matching documents')
void byFieldsMatchNoFields() {
new PgDB().withCloseable RemoveFieldsFunctions.&byFieldsMatchNoFields
}
@Test
@DisplayName('byFields succeeds when no matching documents exist')
void byFieldsNoMatch() {
new PgDB().withCloseable RemoveFieldsFunctions.&byFieldsNoMatch
}
@Test
@DisplayName('byContains removes fields from matching documents')
void byContainsMatchFields() {
new PgDB().withCloseable RemoveFieldsFunctions.&byContainsMatchFields
}
@Test
@DisplayName('byContains succeeds when fields do not exist on matching documents')
void byContainsMatchNoFields() {
new PgDB().withCloseable RemoveFieldsFunctions.&byContainsMatchNoFields
}
@Test
@DisplayName('byContains succeeds when no matching documents exist')
void byContainsNoMatch() {
new PgDB().withCloseable RemoveFieldsFunctions.&byContainsNoMatch
}
@Test
@DisplayName('byJsonPath removes fields from matching documents')
void byJsonPathMatchFields() {
new PgDB().withCloseable RemoveFieldsFunctions.&byJsonPathMatchFields
}
@Test
@DisplayName('byJsonPath succeeds when fields do not exist on matching documents')
void byJsonPathMatchNoFields() {
new PgDB().withCloseable RemoveFieldsFunctions.&byJsonPathMatchNoFields
}
@Test
@DisplayName('byJsonPath succeeds when no matching documents exist')
void byJsonPathNoMatch() {
new PgDB().withCloseable RemoveFieldsFunctions.&byJsonPathNoMatch
}
}

View File

@@ -0,0 +1,104 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.Field
import static org.junit.jupiter.api.Assertions.*
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
final class RemoveFieldsFunctions {
static void byIdMatchFields(ThrowawayDatabase db) {
JsonDocument.load db
db.conn.removeFieldsById TEST_TABLE, 'two', List.of('sub', 'value')
def doc = db.conn.findById TEST_TABLE, 'two', JsonDocument
assertTrue doc.isPresent(), 'There should have been a document returned'
assertEquals '', doc.get().value, 'The value should have been empty'
assertNull doc.get().sub, 'The sub-document should have been removed'
}
static void byIdMatchNoFields(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse db.conn.existsByFields(TEST_TABLE, List.of(Field.exists('a_field_that_does_not_exist')))
db.conn.removeFieldsById TEST_TABLE, 'one', List.of('a_field_that_does_not_exist') // no exception = pass
}
static void byIdNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse db.conn.existsById(TEST_TABLE, 'fifty')
db.conn.removeFieldsById TEST_TABLE, 'fifty', List.of('sub') // no exception = pass
}
static void byFieldsMatchFields(ThrowawayDatabase db) {
JsonDocument.load db
def fields = List.of Field.equal('numValue', 17)
db.conn.removeFieldsByFields TEST_TABLE, fields, List.of('sub')
def doc = db.conn.findFirstByFields TEST_TABLE, fields, JsonDocument
assertTrue doc.isPresent(), 'The document should have been returned'
assertEquals 'four', doc.get().id, 'An incorrect document was returned'
assertNull doc.get().sub, 'The sub-document should have been removed'
}
static void byFieldsMatchNoFields(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse db.conn.existsByFields(TEST_TABLE, List.of(Field.exists('nada')))
db.conn.removeFieldsByFields TEST_TABLE, List.of(Field.equal('numValue', 17)), List.of('nada') // no exn = pass
}
static void byFieldsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
def fields = List.of Field.notEqual('missing', 'nope')
assertFalse db.conn.existsByFields(TEST_TABLE, fields)
db.conn.removeFieldsByFields TEST_TABLE, fields, List.of('value') // no exception = pass
}
static void byContainsMatchFields(ThrowawayDatabase db) {
JsonDocument.load db
def criteria = Map.of('sub', Map.of('foo', 'green'))
db.conn.removeFieldsByContains TEST_TABLE, criteria, List.of('value')
def docs = db.conn.findByContains TEST_TABLE, criteria, JsonDocument
assertEquals 2, docs.size(), 'There should have been 2 documents returned'
docs.forEach {
assertTrue List.of('two', 'four').contains(it.id), "An incorrect document was returned (${it.id})"
assertEquals '', it.value, 'The value should have been empty'
}
}
static void byContainsMatchNoFields(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse db.conn.existsByFields(TEST_TABLE, List.of(Field.exists('invalid_field')))
db.conn.removeFieldsByContains TEST_TABLE, Map.of('sub', Map.of('foo', 'green')), List.of('invalid_field')
// no exception = pass
}
static void byContainsNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
def contains = Map.of 'value', 'substantial'
assertFalse db.conn.existsByContains(TEST_TABLE, contains)
db.conn.removeFieldsByContains TEST_TABLE, contains, List.of('numValue')
}
static void byJsonPathMatchFields(ThrowawayDatabase db) {
JsonDocument.load db
def path = '$.value ? (@ == "purple")'
db.conn.removeFieldsByJsonPath TEST_TABLE, path, List.of('sub')
def docs = db.conn.findByJsonPath TEST_TABLE, path, JsonDocument
assertEquals 2, docs.size(), 'There should have been 2 documents returned'
docs.forEach {
assertTrue List.of('four', 'five').contains(it.id), "An incorrect document was returned (${it.id})"
assertNull it.sub, 'The sub-document should have been removed'
}
}
static void byJsonPathMatchNoFields(ThrowawayDatabase db) {
JsonDocument.load db
assertFalse db.conn.existsByFields(TEST_TABLE, List.of(Field.exists('submarine')))
db.conn.removeFieldsByJsonPath TEST_TABLE, '$.value ? (@ == "purple")', List.of('submarine') // no exn = pass
}
static void byJsonPathNoMatch(ThrowawayDatabase db) {
JsonDocument.load db
def path = '$.value ? (@ == "mauve")'
assertFalse db.conn.existsByJsonPath(TEST_TABLE, path)
db.conn.removeFieldsByJsonPath TEST_TABLE, path, List.of('value') // no exception = pass
}
}

View File

@@ -0,0 +1,48 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.DocumentException
import static org.junit.jupiter.api.Assertions.assertThrows
/**
* SQLite integration tests for the `Count` object / `count*` connection extension functions
*/
@DisplayName("Groovy | SQLite: Count")
final class SQLiteCountIT {
@Test
@DisplayName("all counts all documents")
void all() {
new SQLiteDB().withCloseable CountFunctions.&all
}
@Test
@DisplayName("byFields counts documents by a numeric value")
void byFieldsNumeric() {
new SQLiteDB().withCloseable CountFunctions.&byFieldsNumeric
}
@Test
@DisplayName("byFields counts documents by a alphanumeric value")
void byFieldsAlpha() {
new SQLiteDB().withCloseable CountFunctions.&byFieldsAlpha
}
@Test
@DisplayName("byContains fails")
void byContainsFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { CountFunctions.byContainsMatch db }
}
}
@Test
@DisplayName("byJsonPath fails")
void byJsonPathFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { CountFunctions.byJsonPathMatch db }
}
}
}

View File

@@ -0,0 +1,53 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* SQLite integration tests for the `Custom` object / `custom*` connection extension functions
*/
@DisplayName('Groovy | SQLite: Custom')
final class SQLiteCustomIT {
@Test
@DisplayName('list succeeds with empty list')
void listEmpty() {
new SQLiteDB().withCloseable CustomFunctions.&listEmpty
}
@Test
@DisplayName('list succeeds with a non-empty list')
void listAll() {
new SQLiteDB().withCloseable CustomFunctions.&listAll
}
@Test
@DisplayName('single succeeds when document not found')
void singleNone() {
new SQLiteDB().withCloseable CustomFunctions.&singleNone
}
@Test
@DisplayName('single succeeds when a document is found')
void singleOne() {
new SQLiteDB().withCloseable CustomFunctions.&singleOne
}
@Test
@DisplayName('nonQuery makes changes')
void nonQueryChanges() {
new SQLiteDB().withCloseable CustomFunctions.&nonQueryChanges
}
@Test
@DisplayName('nonQuery makes no changes when where clause matches nothing')
void nonQueryNoChanges() {
new SQLiteDB().withCloseable CustomFunctions.&nonQueryNoChanges
}
@Test
@DisplayName('scalar succeeds')
void scalar() {
new SQLiteDB().withCloseable CustomFunctions.&scalar
}
}

View File

@@ -0,0 +1,35 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Parameter
import solutions.bitbadger.documents.ParameterType
import solutions.bitbadger.documents.java.DocumentConfig
import solutions.bitbadger.documents.java.Results
import static solutions.bitbadger.documents.groovy.tests.Types.TEST_TABLE
/**
* A wrapper for a throwaway SQLite database
*/
class SQLiteDB implements ThrowawayDatabase {
SQLiteDB() {
Configuration.setConnectionString "jdbc:sqlite:${dbName}.db"
conn = Configuration.dbConn()
conn.ensureTable TEST_TABLE
// Use a Jackson-based document serializer for testing
DocumentConfig.serializer = new JacksonDocumentSerializer()
}
void close() {
conn.close()
new File("${dbName}.db").delete()
Configuration.setConnectionString null
}
boolean dbObjectExists(String name) {
conn.customScalar("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE name = :name) AS it",
List.of(new Parameter(":name", ParameterType.STRING, name)), Boolean, Results.&toExists)
}
}

View File

@@ -0,0 +1,42 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.DocumentException
import static org.junit.jupiter.api.Assertions.assertThrows
/**
* SQLite integration tests for the `Definition` object / `ensure*` connection extension functions
*/
@DisplayName('Groovy | SQLite: Definition')
final class SQLiteDefinitionIT {
@Test
@DisplayName('ensureTable creates table and index')
void ensureTable() {
new SQLiteDB().withCloseable DefinitionFunctions.&ensureTable
}
@Test
@DisplayName('ensureFieldIndex creates an index')
void ensureFieldIndex() {
new SQLiteDB().withCloseable DefinitionFunctions.&ensureFieldIndex
}
@Test
@DisplayName('ensureDocumentIndex fails for full index')
void ensureDocumentIndexFull() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { DefinitionFunctions::ensureDocumentIndexFull db }
}
}
@Test
@DisplayName('ensureDocumentIndex fails for optimized index')
void ensureDocumentIndexOptimized() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { DefinitionFunctions::ensureDocumentIndexOptimized db }
}
}
}

View File

@@ -0,0 +1,54 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.DocumentException
import static org.junit.jupiter.api.Assertions.assertThrows
/**
* SQLite integration tests for the `Delete` object / `deleteBy*` connection extension functions
*/
@DisplayName('Groovy | SQLite: Delete')
final class SQLiteDeleteIT {
@Test
@DisplayName('byId deletes a matching ID')
void byIdMatch() {
new SQLiteDB().withCloseable DeleteFunctions.&byIdMatch
}
@Test
@DisplayName('byId succeeds when no ID matches')
void byIdNoMatch() {
new SQLiteDB().withCloseable DeleteFunctions.&byIdNoMatch
}
@Test
@DisplayName('byFields deletes matching documents')
void byFieldsMatch() {
new SQLiteDB().withCloseable DeleteFunctions.&byFieldsMatch
}
@Test
@DisplayName('byFields succeeds when no documents match')
void byFieldsNoMatch() {
new SQLiteDB().withCloseable DeleteFunctions.&byFieldsNoMatch
}
@Test
@DisplayName('byContains fails')
void byContainsFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { DeleteFunctions.byContainsMatch db }
}
}
@Test
@DisplayName('byJsonPath fails')
void byJsonPathFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { DeleteFunctions.byJsonPathMatch db }
}
}
}

View File

@@ -0,0 +1,65 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
/**
* SQLite integration tests for the `Document` object / `insert`, `save`, `update` connection extension functions
*/
@DisplayName('Groovy | SQLite: Document')
final class SQLiteDocumentIT {
@Test
@DisplayName('insert works with default values')
void insertDefault() {
new SQLiteDB().withCloseable DocumentFunctions.&insertDefault
}
@Test
@DisplayName('insert fails with duplicate key')
void insertDupe() {
new SQLiteDB().withCloseable DocumentFunctions.&insertDupe
}
@Test
@DisplayName('insert succeeds with numeric auto IDs')
void insertNumAutoId() {
new SQLiteDB().withCloseable DocumentFunctions.&insertNumAutoId
}
@Test
@DisplayName('insert succeeds with UUID auto ID')
void insertUUIDAutoId() {
new SQLiteDB().withCloseable DocumentFunctions.&insertUUIDAutoId
}
@Test
@DisplayName('insert succeeds with random string auto ID')
void insertStringAutoId() {
new SQLiteDB().withCloseable DocumentFunctions.&insertStringAutoId
}
@Test
@DisplayName('save updates an existing document')
void saveMatch() {
new SQLiteDB().withCloseable DocumentFunctions.&saveMatch
}
@Test
@DisplayName('save inserts a new document')
void saveNoMatch() {
new SQLiteDB().withCloseable DocumentFunctions.&saveNoMatch
}
@Test
@DisplayName('update replaces an existing document')
void updateMatch() {
new SQLiteDB().withCloseable DocumentFunctions.&updateMatch
}
@Test
@DisplayName('update succeeds when no document exists')
void updateNoMatch() {
new SQLiteDB().withCloseable DocumentFunctions.&updateNoMatch
}
}

View File

@@ -0,0 +1,54 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.DocumentException
import static org.junit.jupiter.api.Assertions.assertThrows
/**
* SQLite integration tests for the `Exists` object / `existsBy*` connection extension functions
*/
@DisplayName('Groovy | SQLite: Exists')
final class SQLiteExistsIT {
@Test
@DisplayName('byId returns true when a document matches the ID')
void byIdMatch() {
new SQLiteDB().withCloseable ExistsFunctions.&byIdMatch
}
@Test
@DisplayName('byId returns false when no document matches the ID')
void byIdNoMatch() {
new SQLiteDB().withCloseable ExistsFunctions.&byIdNoMatch
}
@Test
@DisplayName('byFields returns true when documents match')
void byFieldsMatch() {
new SQLiteDB().withCloseable ExistsFunctions.&byFieldsMatch
}
@Test
@DisplayName('byFields returns false when no documents match')
void byFieldsNoMatch() {
new SQLiteDB().withCloseable ExistsFunctions.&byFieldsNoMatch
}
@Test
@DisplayName('byContains fails')
void byContainsFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { ExistsFunctions.byContainsMatch db }
}
}
@Test
@DisplayName('byJsonPath fails')
void byJsonPathFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { ExistsFunctions.byJsonPathMatch db }
}
}
}

View File

@@ -0,0 +1,154 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.DocumentException
import static org.junit.jupiter.api.Assertions.assertThrows
/**
* SQLite integration tests for the `Find` object / `find*` connection extension functions
*/
@DisplayName('Groovy | SQLite: Find')
final class SQLiteFindIT {
@Test
@DisplayName('all retrieves all documents')
void allDefault() {
new SQLiteDB().withCloseable FindFunctions.&allDefault
}
@Test
@DisplayName('all sorts data ascending')
void allAscending() {
new SQLiteDB().withCloseable FindFunctions.&allAscending
}
@Test
@DisplayName('all sorts data descending')
void allDescending() {
new SQLiteDB().withCloseable FindFunctions.&allDescending
}
@Test
@DisplayName('all sorts data numerically')
void allNumOrder() {
new SQLiteDB().withCloseable FindFunctions.&allNumOrder
}
@Test
@DisplayName('all succeeds with an empty table')
void allEmpty() {
new SQLiteDB().withCloseable FindFunctions.&allEmpty
}
@Test
@DisplayName('byId retrieves a document via a string ID')
void byIdString() {
new SQLiteDB().withCloseable FindFunctions.&byIdString
}
@Test
@DisplayName('byId retrieves a document via a numeric ID')
void byIdNumber() {
new SQLiteDB().withCloseable FindFunctions.&byIdNumber
}
@Test
@DisplayName('byId returns null when a matching ID is not found')
void byIdNotFound() {
new SQLiteDB().withCloseable FindFunctions.&byIdNotFound
}
@Test
@DisplayName('byFields retrieves matching documents')
void byFieldsMatch() {
new SQLiteDB().withCloseable FindFunctions.&byFieldsMatch
}
@Test
@DisplayName('byFields retrieves ordered matching documents')
void byFieldsMatchOrdered() {
new SQLiteDB().withCloseable FindFunctions.&byFieldsMatchOrdered
}
@Test
@DisplayName('byFields retrieves matching documents with a numeric IN clause')
void byFieldsMatchNumIn() {
new SQLiteDB().withCloseable FindFunctions.&byFieldsMatchNumIn
}
@Test
@DisplayName('byFields succeeds when no documents match')
void byFieldsNoMatch() {
new SQLiteDB().withCloseable FindFunctions.&byFieldsNoMatch
}
@Test
@DisplayName('byFields retrieves matching documents with an IN_ARRAY comparison')
void byFieldsMatchInArray() {
new SQLiteDB().withCloseable FindFunctions.&byFieldsMatchInArray
}
@Test
@DisplayName('byFields succeeds when no documents match an IN_ARRAY comparison')
void byFieldsNoMatchInArray() {
new SQLiteDB().withCloseable FindFunctions.&byFieldsNoMatchInArray
}
@Test
@DisplayName('byContains fails')
void byContainsFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { FindFunctions.byContainsMatch db }
}
}
@Test
@DisplayName('byJsonPath fails')
void byJsonPathFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { FindFunctions.byJsonPathMatch db }
}
}
@Test
@DisplayName('firstByFields retrieves a matching document')
void firstByFieldsMatchOne() {
new SQLiteDB().withCloseable FindFunctions.&firstByFieldsMatchOne
}
@Test
@DisplayName('firstByFields retrieves a matching document among many')
void firstByFieldsMatchMany() {
new SQLiteDB().withCloseable FindFunctions.&firstByFieldsMatchMany
}
@Test
@DisplayName('firstByFields retrieves a matching document among many (ordered)')
void firstByFieldsMatchOrdered() {
new SQLiteDB().withCloseable FindFunctions.&firstByFieldsMatchOrdered
}
@Test
@DisplayName('firstByFields returns null when no document matches')
void firstByFieldsNoMatch() {
new SQLiteDB().withCloseable FindFunctions.&firstByFieldsNoMatch
}
@Test
@DisplayName('firstByContains fails')
void firstByContainsFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { FindFunctions.firstByContainsMatchOne db }
}
}
@Test
@DisplayName('firstByJsonPath fails')
void firstByJsonPathFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { FindFunctions.firstByJsonPathMatchOne db }
}
}
}

View File

@@ -0,0 +1,54 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.DocumentException
import static org.junit.jupiter.api.Assertions.assertThrows
/**
* SQLite integration tests for the `Patch` object / `patchBy*` connection extension functions
*/
@DisplayName('Groovy | SQLite: Patch')
final class SQLitePatchIT {
@Test
@DisplayName('byId patches an existing document')
void byIdMatch() {
new SQLiteDB().withCloseable PatchFunctions.&byIdMatch
}
@Test
@DisplayName('byId succeeds for a non-existent document')
void byIdNoMatch() {
new SQLiteDB().withCloseable PatchFunctions.&byIdNoMatch
}
@Test
@DisplayName('byFields patches matching document')
void byFieldsMatch() {
new SQLiteDB().withCloseable PatchFunctions.&byFieldsMatch
}
@Test
@DisplayName('byFields succeeds when no documents match')
void byFieldsNoMatch() {
new SQLiteDB().withCloseable PatchFunctions.&byFieldsNoMatch
}
@Test
@DisplayName('byContains fails')
void byContainsFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { PatchFunctions.byContainsMatch db }
}
}
@Test
@DisplayName('byJsonPath fails')
void byJsonPathFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { PatchFunctions.byJsonPathMatch db }
}
}
}

View File

@@ -0,0 +1,66 @@
package solutions.bitbadger.documents.groovy.tests.integration
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import solutions.bitbadger.documents.DocumentException
import static org.junit.jupiter.api.Assertions.assertThrows
/**
* SQLite integration tests for the `RemoveFields` object / `removeFieldsBy*` connection extension functions
*/
@DisplayName('Groovy | SQLite: RemoveFields')
final class SQLiteRemoveFieldsIT {
@Test
@DisplayName('byId removes fields from an existing document')
void byIdMatchFields() {
new SQLiteDB().withCloseable RemoveFieldsFunctions.&byIdMatchFields
}
@Test
@DisplayName('byId succeeds when fields do not exist on an existing document')
void byIdMatchNoFields() {
new SQLiteDB().withCloseable RemoveFieldsFunctions.&byIdMatchNoFields
}
@Test
@DisplayName('byId succeeds when no document exists')
void byIdNoMatch() {
new SQLiteDB().withCloseable RemoveFieldsFunctions.&byIdNoMatch
}
@Test
@DisplayName('byFields removes fields from matching documents')
void byFieldsMatchFields() {
new SQLiteDB().withCloseable RemoveFieldsFunctions.&byFieldsMatchFields
}
@Test
@DisplayName('byFields succeeds when fields do not exist on matching documents')
void byFieldsMatchNoFields() {
new SQLiteDB().withCloseable RemoveFieldsFunctions.&byFieldsMatchNoFields
}
@Test
@DisplayName('byFields succeeds when no matching documents exist')
void byFieldsNoMatch() {
new SQLiteDB().withCloseable RemoveFieldsFunctions.&byFieldsNoMatch
}
@Test
@DisplayName('byContains fails')
void byContainsFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { RemoveFieldsFunctions.byContainsMatchFields db }
}
}
@Test
@DisplayName('byJsonPath fails')
void byJsonPathFails() {
new SQLiteDB().withCloseable { db ->
assertThrows(DocumentException) { RemoveFieldsFunctions.byJsonPathMatchFields db }
}
}
}

View File

@@ -0,0 +1,11 @@
package solutions.bitbadger.documents.groovy.tests.integration
class SubDocument {
String foo
String bar
SubDocument(String foo = "", String bar = "") {
this.foo = foo
this.bar = bar
}
}

View File

@@ -0,0 +1,24 @@
package solutions.bitbadger.documents.groovy.tests.integration
import solutions.bitbadger.documents.AutoId
import java.sql.Connection
/**
* Common trait for PostgreSQL and SQLite throwaway databases
*/
trait ThrowawayDatabase implements AutoCloseable {
/** The database connection for the throwaway database */
Connection conn
/**
* Determine if a database object exists
*
* @param name The name of the object whose existence should be checked
* @return True if the object exists, false if not
*/
abstract boolean dbObjectExists(String name)
/** The name for the throwaway database */
String dbName = "throwaway_${AutoId.generateRandomString(8)}"
}

View File

@@ -0,0 +1,15 @@
module solutions.bitbadger.documents.groovy.tests {
requires solutions.bitbadger.documents.core;
requires solutions.bitbadger.documents.groovy;
requires com.fasterxml.jackson.databind;
requires java.desktop;
requires java.sql;
requires org.apache.groovy;
requires org.junit.jupiter.api;
exports solutions.bitbadger.documents.groovy.tests;
exports solutions.bitbadger.documents.groovy.tests.integration;
opens solutions.bitbadger.documents.groovy.tests;
opens solutions.bitbadger.documents.groovy.tests.integration;
}