Initial Development #1
@ -3,7 +3,7 @@ package solutions.bitbadger.documents.groovy
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import solutions.bitbadger.documents.AutoId
|
||||
import solutions.bitbadger.documents.DocumentException
|
||||
//import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.groovy.support.*
|
||||
|
||||
import static groovy.test.GroovyAssert.*
|
||||
|
@ -0,0 +1,86 @@
|
||||
package solutions.bitbadger.documents.groovy.query
|
||||
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
//import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.query.CountQuery
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
|
||||
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
import static groovy.test.GroovyAssert.*
|
||||
|
||||
/**
|
||||
* Unit tests for the `Count` object
|
||||
*/
|
||||
@DisplayName('JVM | Groovy | Query | CountQuery')
|
||||
class CountQueryTest {
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
void cleanUp() {
|
||||
ForceDialect.none()
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('all generates correctly')
|
||||
void all() {
|
||||
assertEquals('Count query not constructed correctly', "SELECT COUNT(*) AS it FROM $TEST_TABLE".toString(),
|
||||
CountQuery.all(TEST_TABLE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byFields generates correctly | PostgreSQL')
|
||||
void byFieldsPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('Count query not constructed correctly',
|
||||
"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0".toString(),
|
||||
CountQuery.byFields(TEST_TABLE, List.of(Field.equal('test', '', ':field0'))))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byFields generates correctly | SQLite')
|
||||
void byFieldsSQLite() {
|
||||
ForceDialect.sqlite()
|
||||
assertEquals('Count query not constructed correctly',
|
||||
"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0".toString(),
|
||||
CountQuery.byFields(TEST_TABLE, List.of(Field.equal('test', '', ':field0'))))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byContains generates correctly | PostgreSQL')
|
||||
void byContainsPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('Count query not constructed correctly',
|
||||
"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data @> :criteria".toString(),
|
||||
CountQuery.byContains(TEST_TABLE))
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @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('Count query not constructed correctly',
|
||||
"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)".toString(),
|
||||
CountQuery.byJsonPath(TEST_TABLE))
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @Test
|
||||
// @DisplayName('byJsonPath fails | SQLite')
|
||||
// void byJsonPathSQLite() {
|
||||
// ForceDialect.sqlite()
|
||||
// assertThrows(DocumentException) { CountQuery.byJsonPath(TEST_TABLE) }
|
||||
// }
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
package solutions.bitbadger.documents.groovy.query
|
||||
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import solutions.bitbadger.documents.Dialect
|
||||
//import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.DocumentIndex
|
||||
import solutions.bitbadger.documents.query.DefinitionQuery
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
|
||||
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
import static groovy.test.GroovyAssert.*
|
||||
|
||||
/**
|
||||
* Unit tests for the `Definition` object
|
||||
*/
|
||||
@DisplayName('JVM | 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 statement not constructed correctly',
|
||||
'CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)',
|
||||
DefinitionQuery.ensureTableFor('my.table', 'JSONB'))
|
||||
}
|
||||
|
||||
@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))
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @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 INDEX for key statement with schema not constructed correctly',
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'id'))",
|
||||
DefinitionQuery.ensureKey('test.table', Dialect.POSTGRESQL))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('ensureKey generates correctly without schema')
|
||||
void ensureKeyWithoutSchema() {
|
||||
assertEquals('CREATE INDEX for key statement without schema not constructed correctly',
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_${TEST_TABLE}_key ON $TEST_TABLE ((data->>'id'))".toString(),
|
||||
DefinitionQuery.ensureKey(TEST_TABLE, Dialect.SQLITE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('ensureIndexOn generates multiple fields and directions')
|
||||
void ensureIndexOnMultipleFields() {
|
||||
assertEquals('CREATE INDEX for multiple field statement not constructed correctly',
|
||||
"CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table ((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)"
|
||||
.toString(),
|
||||
DefinitionQuery.ensureIndexOn('test.table', 'gibberish', List.of('taco', 'guac DESC', 'salsa ASC'),
|
||||
Dialect.POSTGRESQL))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('ensureIndexOn generates nested field | PostgreSQL')
|
||||
void ensureIndexOnNestedPostgres() {
|
||||
assertEquals('CREATE INDEX for nested PostgreSQL field incorrect',
|
||||
"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))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('ensureIndexOn generates nested field | SQLite')
|
||||
void ensureIndexOnNestedSQLite() {
|
||||
assertEquals('CREATE INDEX for nested SQLite field incorrect',
|
||||
"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))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('ensureDocumentIndexOn generates Full | PostgreSQL')
|
||||
void ensureDocumentIndexOnFullPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('CREATE INDEX for full document index incorrect',
|
||||
"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data)".toString(),
|
||||
DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('ensureDocumentIndexOn generates Optimized | PostgreSQL')
|
||||
void ensureDocumentIndexOnOptimizedPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('CREATE INDEX for optimized document index incorrect',
|
||||
"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))
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @Test
|
||||
// @DisplayName('ensureDocumentIndexOn fails | SQLite')
|
||||
// void ensureDocumentIndexOnFailsSQLite() {
|
||||
// ForceDialect.sqlite()
|
||||
// assertThrows(DocumentException) { DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL) }
|
||||
// }
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package solutions.bitbadger.documents.groovy.query
|
||||
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
//import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.query.DeleteQuery
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
|
||||
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
import static groovy.test.GroovyAssert.*
|
||||
|
||||
/**
|
||||
* Unit tests for the `Delete` object
|
||||
*/
|
||||
@DisplayName('JVM | 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 query not constructed correctly',
|
||||
"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id".toString(), DeleteQuery.byId(TEST_TABLE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byId generates correctly | SQLite')
|
||||
void byIdSQLite() {
|
||||
ForceDialect.sqlite()
|
||||
assertEquals('Delete query not constructed correctly',
|
||||
"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id".toString(), DeleteQuery.byId(TEST_TABLE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byFields generates correctly | PostgreSQL')
|
||||
void byFieldsPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('Delete query not constructed correctly',
|
||||
"DELETE FROM $TEST_TABLE WHERE data->>'a' = :b".toString(),
|
||||
DeleteQuery.byFields(TEST_TABLE, List.of(Field.equal('a', '', ':b'))))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byFields generates correctly | SQLite')
|
||||
void byFieldsSQLite() {
|
||||
ForceDialect.sqlite()
|
||||
assertEquals('Delete query not constructed correctly',
|
||||
"DELETE FROM $TEST_TABLE WHERE data->>'a' = :b".toString(),
|
||||
DeleteQuery.byFields(TEST_TABLE, List.of(Field.equal('a', '', ':b'))))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byContains generates correctly | PostgreSQL')
|
||||
void byContainsPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('Delete query not constructed correctly',
|
||||
"DELETE FROM $TEST_TABLE WHERE data @> :criteria".toString(), DeleteQuery.byContains(TEST_TABLE))
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @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 query not constructed correctly',
|
||||
"DELETE FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)".toString(),
|
||||
DeleteQuery.byJsonPath(TEST_TABLE))
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @Test
|
||||
// @DisplayName('byJsonPath fails | SQLite')
|
||||
// void byJsonPathSQLite() {
|
||||
// ForceDialect.sqlite()
|
||||
// assertThrows(DocumentException) { DeleteQuery.byJsonPath(TEST_TABLE) }
|
||||
// }
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
package solutions.bitbadger.documents.groovy.query
|
||||
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import solutions.bitbadger.documents.AutoId
|
||||
import solutions.bitbadger.documents.Configuration
|
||||
//import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.query.DocumentQuery
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
|
||||
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
import static groovy.test.GroovyAssert.*
|
||||
|
||||
/**
|
||||
* Unit tests for the `Document` object
|
||||
*/
|
||||
@DisplayName('JVM | 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 start not correct (actual: $query)",
|
||||
query.startsWith("INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\""))
|
||||
assertTrue('Query end not correct', query.endsWith("\"}')"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('insert generates auto UUID | SQLite')
|
||||
void insertAutoUUIDSQLite() {
|
||||
ForceDialect.sqlite()
|
||||
def query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
|
||||
assertTrue("Query start not correct (actual: $query)",
|
||||
query.startsWith("INSERT INTO $TEST_TABLE VALUES (json_set(:data, '\$.id', '"))
|
||||
assertTrue('Query end not correct', query.endsWith("'))"))
|
||||
}
|
||||
|
||||
@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 start not correct (actual: $query)",
|
||||
query.startsWith("INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\""))
|
||||
assertTrue('Query end not correct', query.endsWith("\"}')"))
|
||||
assertEquals('Random string length incorrect', 8,
|
||||
query.replace("INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\"", '')
|
||||
.replace("\"}')", '').length())
|
||||
} 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 start not correct (actual: $query)",
|
||||
query.startsWith("INSERT INTO $TEST_TABLE VALUES (json_set(:data, '\$.id', '"))
|
||||
assertTrue('Query end not correct', query.endsWith("'))"))
|
||||
assertEquals('Random string length incorrect', Configuration.idStringLength,
|
||||
query.replace("INSERT INTO $TEST_TABLE VALUES (json_set(:data, '\$.id', '", '').replace("'))", '')
|
||||
.length())
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @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 ON CONFLICT UPDATE statement not constructed correctly',
|
||||
"INSERT INTO $TEST_TABLE VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data"
|
||||
.toString(),
|
||||
DocumentQuery.save(TEST_TABLE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('update generates successfully')
|
||||
void update() {
|
||||
assertEquals('Update query not constructed correctly', "UPDATE $TEST_TABLE SET data = :data".toString(),
|
||||
DocumentQuery.update(TEST_TABLE))
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package solutions.bitbadger.documents.groovy.query
|
||||
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
//import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.query.FindQuery
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
|
||||
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
import static groovy.test.GroovyAssert.*
|
||||
|
||||
/**
|
||||
* Unit tests for the `Find` object
|
||||
*/
|
||||
@DisplayName('JVM | Groovy | Query | FindQuery')
|
||||
class FindQueryTest {
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
void cleanUp() {
|
||||
ForceDialect.none()
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('all generates correctly')
|
||||
void all() {
|
||||
assertEquals('Find query not constructed correctly', "SELECT data FROM $TEST_TABLE".toString(),
|
||||
FindQuery.all(TEST_TABLE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byId generates correctly | PostgreSQL')
|
||||
void byIdPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('Find query not constructed correctly',
|
||||
"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id".toString(), FindQuery.byId(TEST_TABLE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byId generates correctly | SQLite')
|
||||
void byIdSQLite() {
|
||||
ForceDialect.sqlite()
|
||||
assertEquals('Find query not constructed correctly',
|
||||
"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id".toString(), FindQuery.byId(TEST_TABLE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byFields generates correctly | PostgreSQL')
|
||||
void byFieldsPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('Find query not constructed correctly',
|
||||
"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'))))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byFields generates correctly | SQLite')
|
||||
void byFieldsSQLite() {
|
||||
ForceDialect.sqlite()
|
||||
assertEquals('Find query not constructed correctly',
|
||||
"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'))))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byContains generates correctly | PostgreSQL')
|
||||
void byContainsPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('Find query not constructed correctly',
|
||||
"SELECT data FROM $TEST_TABLE WHERE data @> :criteria".toString(), FindQuery.byContains(TEST_TABLE))
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @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('Find query not constructed correctly',
|
||||
"SELECT data FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)".toString(),
|
||||
FindQuery.byJsonPath(TEST_TABLE))
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @Test
|
||||
// @DisplayName('byJsonPath fails | SQLite')
|
||||
// void byJsonPathSQLite() {
|
||||
// ForceDialect.sqlite()
|
||||
// assertThrows(DocumentException) { FindQuery.byJsonPath(TEST_TABLE) }
|
||||
// }
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package solutions.bitbadger.documents.groovy.query
|
||||
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
//import solutions.bitbadger.documents.DocumentException
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.query.PatchQuery
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
|
||||
import static solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
import static groovy.test.GroovyAssert.*
|
||||
|
||||
/**
|
||||
* Unit tests for the `Patch` object
|
||||
*/
|
||||
@DisplayName('JVM | Groovy | Query | PatchQuery')
|
||||
class PatchQueryTest {
|
||||
|
||||
/**
|
||||
* Reset the dialect
|
||||
*/
|
||||
@AfterEach
|
||||
void cleanUp() {
|
||||
ForceDialect.none()
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byId generates correctly | PostgreSQL')
|
||||
void byIdPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('Patch query not constructed correctly',
|
||||
"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'id' = :id".toString(),
|
||||
PatchQuery.byId(TEST_TABLE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byId generates correctly | SQLite')
|
||||
void byIdSQLite() {
|
||||
ForceDialect.sqlite()
|
||||
assertEquals('Patch query not constructed correctly',
|
||||
"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id".toString(),
|
||||
PatchQuery.byId(TEST_TABLE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byFields generates correctly | PostgreSQL')
|
||||
void byFieldsPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('Patch query not constructed correctly',
|
||||
"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'z' = :y".toString(),
|
||||
PatchQuery.byFields(TEST_TABLE, List.of(Field.equal('z', '', ':y'))))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byFields generates correctly | SQLite')
|
||||
void byFieldsSQLite() {
|
||||
ForceDialect.sqlite()
|
||||
assertEquals('Patch query not constructed correctly',
|
||||
"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'))))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('byContains generates correctly | PostgreSQL')
|
||||
void byContainsPostgres() {
|
||||
ForceDialect.postgres()
|
||||
assertEquals('Patch query not constructed correctly',
|
||||
"UPDATE $TEST_TABLE SET data = data || :data WHERE data @> :criteria".toString(),
|
||||
PatchQuery.byContains(TEST_TABLE))
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @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('Patch query not constructed correctly',
|
||||
"UPDATE $TEST_TABLE SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)".toString(),
|
||||
PatchQuery.byJsonPath(TEST_TABLE))
|
||||
}
|
||||
|
||||
// TODO: resolve java.base open issue
|
||||
// @Test
|
||||
// @DisplayName('byJsonPath fails | SQLite')
|
||||
// void byJsonPathSQLite() {
|
||||
// ForceDialect.sqlite()
|
||||
// assertThrows(DocumentException) { PatchQuery.byJsonPath(TEST_TABLE) }
|
||||
// }
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
package solutions.bitbadger.documents.groovy.query
|
||||
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import solutions.bitbadger.documents.Dialect
|
||||
import solutions.bitbadger.documents.Field
|
||||
import solutions.bitbadger.documents.FieldMatch
|
||||
import solutions.bitbadger.documents.query.QueryUtils
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
|
||||
import static groovy.test.GroovyAssert.*
|
||||
|
||||
/**
|
||||
* Unit tests for the `QueryUtils` class
|
||||
*/
|
||||
@DisplayName('JVM | Groovy | Query | QueryUtils')
|
||||
class QueryUtilsTest {
|
||||
|
||||
/**
|
||||
* Clear the connection string (resets Dialect)
|
||||
*/
|
||||
@AfterEach
|
||||
void cleanUp() {
|
||||
ForceDialect.none()
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('statementWhere generates correctly')
|
||||
void statementWhere() {
|
||||
assertEquals('Statements not combined correctly', 'x WHERE y', QueryUtils.statementWhere('x', 'y'))
|
||||
}
|
||||
|
||||
@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('ORDER BY should have been blank (PostgreSQL)', '', QueryUtils.orderBy(List.of(),
|
||||
Dialect.POSTGRESQL))
|
||||
assertEquals('ORDER BY should have been blank (SQLite)', '', QueryUtils.orderBy(List.of(), Dialect.SQLITE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('orderBy generates single, no direction | PostgreSQL')
|
||||
void orderBySinglePostgres() {
|
||||
assertEquals('ORDER BY not constructed correctly', " ORDER BY data->>'TestField'",
|
||||
QueryUtils.orderBy(List.of(Field.named('TestField')), Dialect.POSTGRESQL))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('orderBy generates single, no direction | SQLite')
|
||||
void orderBySingleSQLite() {
|
||||
assertEquals('ORDER BY not constructed correctly', " ORDER BY data->>'TestField'",
|
||||
QueryUtils.orderBy(List.of(Field.named('TestField')), Dialect.SQLITE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('orderBy generates multiple with direction | PostgreSQL')
|
||||
void orderByMultiplePostgres() {
|
||||
assertEquals('ORDER BY not constructed correctly',
|
||||
" 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))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('orderBy generates multiple with direction | SQLite')
|
||||
void orderByMultipleSQLite() {
|
||||
assertEquals('ORDER BY not constructed correctly',
|
||||
" 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))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('orderBy generates numeric ordering | PostgreSQL')
|
||||
void orderByNumericPostgres() {
|
||||
assertEquals('ORDER BY not constructed correctly', " ORDER BY (data->>'Test')::numeric",
|
||||
QueryUtils.orderBy(List.of(Field.named('n:Test')), Dialect.POSTGRESQL))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('orderBy generates numeric ordering | SQLite')
|
||||
void orderByNumericSQLite() {
|
||||
assertEquals('ORDER BY not constructed correctly', " ORDER BY data->>'Test'",
|
||||
QueryUtils.orderBy(List.of(Field.named('n:Test')), Dialect.SQLITE))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('orderBy generates case-insensitive ordering | PostgreSQL')
|
||||
void orderByCIPostgres() {
|
||||
assertEquals('ORDER BY not constructed correctly', " ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST",
|
||||
QueryUtils.orderBy(List.of(Field.named('i:Test.Field DESC NULLS FIRST')), Dialect.POSTGRESQL))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName('orderBy generates case-insensitive ordering | SQLite')
|
||||
void orderByCISQLite() {
|
||||
assertEquals('ORDER BY not constructed correctly',
|
||||
" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST",
|
||||
QueryUtils.orderBy(List.of(Field.named('i:Test.Field ASC NULLS LAST')), Dialect.SQLITE))
|
||||
}
|
||||
}
|
@ -29,10 +29,10 @@ class AutoIdSpec extends AnyFunSpec with Matchers {
|
||||
|
||||
describe("needsAutoId") {
|
||||
it("fails for null document") {
|
||||
an [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.DISABLED, null, "id")
|
||||
a [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.DISABLED, null, "id")
|
||||
}
|
||||
it("fails for missing ID property") {
|
||||
an [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.UUID, IntIdClass(0), "Id")
|
||||
a [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.UUID, IntIdClass(0), "Id")
|
||||
}
|
||||
it("returns false if disabled") {
|
||||
AutoId.needsAutoId(AutoId.DISABLED, "", "") shouldBe false
|
||||
@ -62,7 +62,7 @@ class AutoIdSpec extends AnyFunSpec with Matchers {
|
||||
AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(2), "id") shouldBe false
|
||||
}
|
||||
it("fails for Number strategy and non-number ID") {
|
||||
an [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.NUMBER, StringIdClass(""), "id")
|
||||
a [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.NUMBER, StringIdClass(""), "id")
|
||||
}
|
||||
it("returns true for UUID strategy and blank ID") {
|
||||
AutoId.needsAutoId(AutoId.UUID, StringIdClass(""), "id") shouldBe true
|
||||
@ -71,7 +71,7 @@ class AutoIdSpec extends AnyFunSpec with Matchers {
|
||||
AutoId.needsAutoId(AutoId.UUID, StringIdClass("howdy"), "id") shouldBe false
|
||||
}
|
||||
it("fails for UUID strategy and non-string ID") {
|
||||
an [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.UUID, IntIdClass(5), "id")
|
||||
a [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.UUID, IntIdClass(5), "id")
|
||||
}
|
||||
it("returns true for Random String strategy and blank ID") {
|
||||
AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass(""), "id") shouldBe true
|
||||
@ -80,7 +80,7 @@ class AutoIdSpec extends AnyFunSpec with Matchers {
|
||||
AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass("full"), "id") shouldBe false
|
||||
}
|
||||
it("fails for Random String strategy and non-string ID") {
|
||||
an [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id")
|
||||
a [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class ConfigurationSpec extends AnyFunSpec with Matchers {
|
||||
describe("dialect") {
|
||||
it("is derived from connection string") {
|
||||
try {
|
||||
an [DocumentException] should be thrownBy Configuration.dialect()
|
||||
a [DocumentException] should be thrownBy Configuration.dialect()
|
||||
Configuration.setConnectionString("jdbc:postgresql:db")
|
||||
Configuration.dialect() shouldEqual Dialect.POSTGRESQL
|
||||
} finally {
|
||||
|
@ -13,7 +13,7 @@ class FieldSpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("withParameterName") {
|
||||
it("fails for invalid name") {
|
||||
an [DocumentException] should be thrownBy Field.equal("it", "").withParameterName("2424")
|
||||
a [DocumentException] should be thrownBy Field.equal("it", "").withParameterName("2424")
|
||||
}
|
||||
it("works with colon prefix") {
|
||||
val field = Field.equal("abc", "22").withQualifier("me")
|
||||
@ -49,36 +49,36 @@ class FieldSpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("path") {
|
||||
it("generates simple unqualified PostgreSQL field") {
|
||||
Field.greaterOrEqual("SomethingCool", 18)
|
||||
.path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual "data->>'SomethingCool'"
|
||||
Field.greaterOrEqual("SomethingCool", 18).path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual
|
||||
"data->>'SomethingCool'"
|
||||
}
|
||||
it("generates simple qualified PostgreSQL field") {
|
||||
Field.less("SomethingElse", 9).withQualifier("this")
|
||||
.path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual "this.data->>'SomethingElse'"
|
||||
Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual
|
||||
"this.data->>'SomethingElse'"
|
||||
}
|
||||
it("generates nested unqualified PostgreSQL field") {
|
||||
Field.equal("My.Nested.Field", "howdy")
|
||||
.path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual "data#>>'{My,Nested,Field}'"
|
||||
Field.equal("My.Nested.Field", "howdy").path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual
|
||||
"data#>>'{My,Nested,Field}'"
|
||||
}
|
||||
it("generates nested qualified PostgreSQL field") {
|
||||
Field.equal("Nest.Away", "doc").withQualifier("bird")
|
||||
.path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual "bird.data#>>'{Nest,Away}'"
|
||||
Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual
|
||||
"bird.data#>>'{Nest,Away}'"
|
||||
}
|
||||
it("generates simple unqualified SQLite field") {
|
||||
Field.greaterOrEqual("SomethingCool", 18)
|
||||
.path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual "data->>'SomethingCool'"
|
||||
Field.greaterOrEqual("SomethingCool", 18).path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual
|
||||
"data->>'SomethingCool'"
|
||||
}
|
||||
it("generates simple qualified SQLite field") {
|
||||
Field.less("SomethingElse", 9).withQualifier("this")
|
||||
.path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual "this.data->>'SomethingElse'"
|
||||
Field.less("SomethingElse", 9).withQualifier("this").path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual
|
||||
"this.data->>'SomethingElse'"
|
||||
}
|
||||
it("generates nested unqualified SQLite field") {
|
||||
Field.equal("My.Nested.Field", "howdy")
|
||||
.path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual "data->'My'->'Nested'->>'Field'"
|
||||
Field.equal("My.Nested.Field", "howdy").path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual
|
||||
"data->'My'->'Nested'->>'Field'"
|
||||
}
|
||||
it("generates nested qualified SQLite field") {
|
||||
Field.equal("Nest.Away", "doc").withQualifier("bird")
|
||||
.path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual "bird.data->'Nest'->>'Away'"
|
||||
Field.equal("Nest.Away", "doc").withQualifier("bird").path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual
|
||||
"bird.data->'Nest'->>'Away'"
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,8 +105,8 @@ class FieldSpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
}
|
||||
it("generates BETWEEN w/o qualifier, alphanumeric range | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
Field.between("city", "Atlanta", "Chicago", ":city")
|
||||
.toWhere shouldEqual "data->>'city' BETWEEN :citymin AND :citymax"
|
||||
Field.between("city", "Atlanta", "Chicago", ":city").toWhere shouldEqual
|
||||
"data->>'city' BETWEEN :citymin AND :citymax"
|
||||
}
|
||||
it("generates BETWEEN w/o qualifier | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
@ -114,43 +114,43 @@ class FieldSpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
}
|
||||
it("generates BETWEEN w/ qualifier, numeric range | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
Field.between("age", 13, 17, "@age").withQualifier("test")
|
||||
.toWhere shouldEqual "(test.data->>'age')::numeric BETWEEN @agemin AND @agemax"
|
||||
Field.between("age", 13, 17, "@age").withQualifier("test").toWhere shouldEqual
|
||||
"(test.data->>'age')::numeric BETWEEN @agemin AND @agemax"
|
||||
}
|
||||
it("generates BETWEEN w/ qualifier, alphanumeric range | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
Field.between("city", "Atlanta", "Chicago", ":city").withQualifier("unit")
|
||||
.toWhere shouldEqual "unit.data->>'city' BETWEEN :citymin AND :citymax"
|
||||
Field.between("city", "Atlanta", "Chicago", ":city").withQualifier("unit").toWhere shouldEqual
|
||||
"unit.data->>'city' BETWEEN :citymin AND :citymax"
|
||||
}
|
||||
it("generates BETWEEN w/ qualifier | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
Field.between("age", 13, 17, "@age").withQualifier("my")
|
||||
.toWhere shouldEqual "my.data->>'age' BETWEEN @agemin AND @agemax"
|
||||
Field.between("age", 13, 17, "@age").withQualifier("my").toWhere shouldEqual
|
||||
"my.data->>'age' BETWEEN @agemin AND @agemax"
|
||||
}
|
||||
it("generates IN/any, numeric values | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
Field.any("even", List(2, 4, 6).asJava, ":nbr")
|
||||
.toWhere shouldEqual "(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)"
|
||||
Field.any("even", List(2, 4, 6).asJava, ":nbr").toWhere shouldEqual
|
||||
"(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)"
|
||||
}
|
||||
it("generates IN/any, alphanumeric values | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
Field.any("test", List("Atlanta", "Chicago").asJava, ":city")
|
||||
.toWhere shouldEqual "data->>'test' IN (:city_0, :city_1)"
|
||||
Field.any("test", List("Atlanta", "Chicago").asJava, ":city").toWhere shouldEqual
|
||||
"data->>'test' IN (:city_0, :city_1)"
|
||||
}
|
||||
it("generates IN/any | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
Field.any("test", List("Atlanta", "Chicago").asJava, ":city")
|
||||
.toWhere shouldEqual "data->>'test' IN (:city_0, :city_1)"
|
||||
Field.any("test", List("Atlanta", "Chicago").asJava, ":city").toWhere shouldEqual
|
||||
"data->>'test' IN (:city_0, :city_1)"
|
||||
}
|
||||
it("generates inArray | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
Field.inArray("even", "tbl", List(2, 4, 6, 8).asJava, ":it")
|
||||
.toWhere shouldEqual "data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]"
|
||||
Field.inArray("even", "tbl", List(2, 4, 6, 8).asJava, ":it").toWhere shouldEqual
|
||||
"data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]"
|
||||
}
|
||||
it("generates inArray | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
Field.inArray("test", "tbl", List("Atlanta", "Chicago").asJava, ":city")
|
||||
.toWhere shouldEqual "EXISTS (SELECT 1 FROM json_each(tbl.data, '$.test') WHERE value IN (:city_0, :city_1))"
|
||||
Field.inArray("test", "tbl", List("Atlanta", "Chicago").asJava, ":city").toWhere shouldEqual
|
||||
"EXISTS (SELECT 1 FROM json_each(tbl.data, '$.test') WHERE value IN (:city_0, :city_1))"
|
||||
}
|
||||
it("generates others w/o qualifier | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
@ -170,8 +170,8 @@ class FieldSpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
}
|
||||
it("generates parameter w/ qualifier | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
Field.lessOrEqual("le_field", 18, ":it").withQualifier("q")
|
||||
.toWhere shouldEqual "(q.data->>'le_field')::numeric <= :it"
|
||||
Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere shouldEqual
|
||||
"(q.data->>'le_field')::numeric <= :it"
|
||||
}
|
||||
it("generates parameter w/ qualifier | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
@ -391,7 +391,7 @@ class FieldSpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("static constructors") {
|
||||
it("fail for invalid parameter name") {
|
||||
an [DocumentException] should be thrownBy Field.equal("a", "b", "that ain't it, Jack...")
|
||||
a [DocumentException] should be thrownBy Field.equal("a", "b", "that ain't it, Jack...")
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,12 +403,12 @@ class FieldSpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
Field.nameToPath("Simple", Dialect.SQLITE, FieldFormat.SQL) shouldEqual "data->>'Simple'"
|
||||
}
|
||||
it("creates a nested PostgreSQL SQL name") {
|
||||
(Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.SQL)
|
||||
shouldEqual "data#>>'{A,Long,Path,to,the,Property}'")
|
||||
Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual
|
||||
"data#>>'{A,Long,Path,to,the,Property}'"
|
||||
}
|
||||
it("creates a nested SQLite SQL name") {
|
||||
(Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.SQL)
|
||||
shouldEqual "data->'A'->'Long'->'Path'->'to'->'the'->>'Property'")
|
||||
Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.SQL) shouldEqual
|
||||
"data->'A'->'Long'->'Path'->'to'->'the'->>'Property'"
|
||||
}
|
||||
it("creates a simple PostgreSQL JSON name") {
|
||||
Field.nameToPath("Simple", Dialect.POSTGRESQL, FieldFormat.JSON) shouldEqual "data->'Simple'"
|
||||
@ -417,12 +417,12 @@ class FieldSpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
Field.nameToPath("Simple", Dialect.SQLITE, FieldFormat.JSON) shouldEqual "data->'Simple'"
|
||||
}
|
||||
it("creates a nested PostgreSQL JSON name") {
|
||||
(Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.JSON)
|
||||
shouldEqual "data#>'{A,Long,Path,to,the,Property}'")
|
||||
Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.JSON) shouldEqual
|
||||
"data#>'{A,Long,Path,to,the,Property}'"
|
||||
}
|
||||
it("creates a nested SQLite JSON name") {
|
||||
(Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.JSON)
|
||||
shouldEqual "data->'A'->'Long'->'Path'->'to'->'the'->'Property'")
|
||||
Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.JSON) shouldEqual
|
||||
"data->'A'->'Long'->'Path'->'to'->'the'->'Property'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ class ParameterSpec extends AnyFunSpec with Matchers {
|
||||
p.getValue should be (null)
|
||||
}
|
||||
it("fails with incorrect prefix") {
|
||||
an [DocumentException] should be thrownBy Parameter("it", ParameterType.JSON, "")
|
||||
a [DocumentException] should be thrownBy Parameter("it", ParameterType.JSON, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
package solutions.bitbadger.documents.scala.query
|
||||
|
||||
import org.scalatest.funspec.AnyFunSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import solutions.bitbadger.documents.{DocumentException, Field}
|
||||
import solutions.bitbadger.documents.query.CountQuery
|
||||
import solutions.bitbadger.documents.scala.ClearConfiguration
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
class CountQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("all") {
|
||||
it("generates correctly") {
|
||||
CountQuery.all(TEST_TABLE) shouldEqual s"SELECT COUNT(*) AS it FROM $TEST_TABLE"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byFields") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
CountQuery.byFields(TEST_TABLE, List(Field.equal("test", "", ":field0")).asJava) shouldEqual
|
||||
s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0"
|
||||
}
|
||||
it("generates correctly | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
CountQuery.byFields(TEST_TABLE, List(Field.equal("test", "", ":field0")).asJava) shouldEqual
|
||||
s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byContains") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
CountQuery.byContains(TEST_TABLE) shouldEqual s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data @> :criteria"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy CountQuery.byContains(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
|
||||
describe("byJsonPath") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
CountQuery.byJsonPath(TEST_TABLE) shouldEqual
|
||||
s"SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy CountQuery.byJsonPath(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package solutions.bitbadger.documents.scala.query
|
||||
|
||||
import org.scalatest.funspec.AnyFunSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import solutions.bitbadger.documents.{Dialect, DocumentException, DocumentIndex}
|
||||
import solutions.bitbadger.documents.query.DefinitionQuery
|
||||
import solutions.bitbadger.documents.scala.ClearConfiguration
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
class DefinitionQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("ensureTableFor") {
|
||||
it("generates correctly") {
|
||||
DefinitionQuery.ensureTableFor("my.table", "JSONB") shouldEqual
|
||||
"CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)"
|
||||
}
|
||||
}
|
||||
|
||||
describe("ensureTable") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
DefinitionQuery.ensureTable(TEST_TABLE) shouldEqual
|
||||
s"CREATE TABLE IF NOT EXISTS $TEST_TABLE (data JSONB NOT NULL)"
|
||||
}
|
||||
it("generates correctly | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
DefinitionQuery.ensureTable(TEST_TABLE) shouldEqual
|
||||
s"CREATE TABLE IF NOT EXISTS $TEST_TABLE (data TEXT NOT NULL)"
|
||||
}
|
||||
it("fails when no dialect is set") {
|
||||
a [DocumentException] should be thrownBy DefinitionQuery.ensureTable(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
|
||||
describe("ensureKey") {
|
||||
it("generates correctly with schema") {
|
||||
DefinitionQuery.ensureKey("test.table", Dialect.POSTGRESQL) shouldEqual
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'id'))"
|
||||
}
|
||||
it("generates correctly without schema") {
|
||||
DefinitionQuery.ensureKey(TEST_TABLE, Dialect.SQLITE) shouldEqual
|
||||
s"CREATE UNIQUE INDEX IF NOT EXISTS idx_${TEST_TABLE}_key ON $TEST_TABLE ((data->>'id'))"
|
||||
}
|
||||
}
|
||||
|
||||
describe("ensureIndexOn") {
|
||||
it("generates multiple fields and directions") {
|
||||
DefinitionQuery.ensureIndexOn("test.table", "gibberish", List("taco", "guac DESC", "salsa ASC").asJava,
|
||||
Dialect.POSTGRESQL) shouldEqual
|
||||
"CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table ((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)"
|
||||
}
|
||||
it("generates nested field | PostgreSQL") {
|
||||
DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", List("a.b.c").asJava, Dialect.POSTGRESQL) shouldEqual
|
||||
s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data#>>'{a,b,c}'))"
|
||||
}
|
||||
it("generates nested field | SQLite") {
|
||||
DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", List("a.b.c").asJava, Dialect.SQLITE) shouldEqual
|
||||
s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data->'a'->'b'->>'c'))"
|
||||
}
|
||||
}
|
||||
|
||||
describe("ensureDocumentOn") {
|
||||
it("generates Full | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL) shouldEqual
|
||||
s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data)"
|
||||
}
|
||||
it("generates Optimized | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.OPTIMIZED) shouldEqual
|
||||
s"CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data jsonb_path_ops)"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package solutions.bitbadger.documents.scala.query
|
||||
|
||||
import org.scalatest.funspec.AnyFunSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import solutions.bitbadger.documents.{DocumentException, Field}
|
||||
import solutions.bitbadger.documents.query.DeleteQuery
|
||||
import solutions.bitbadger.documents.scala.ClearConfiguration
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
class DeleteQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("byId") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
DeleteQuery.byId(TEST_TABLE) shouldEqual s"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id"
|
||||
}
|
||||
it("generates correctly | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
DeleteQuery.byId(TEST_TABLE) shouldEqual s"DELETE FROM $TEST_TABLE WHERE data->>'id' = :id"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byFields") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
DeleteQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b")).asJava) shouldEqual
|
||||
s"DELETE FROM $TEST_TABLE WHERE data->>'a' = :b"
|
||||
}
|
||||
it("generates correctly | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
DeleteQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b")).asJava) shouldEqual
|
||||
s"DELETE FROM $TEST_TABLE WHERE data->>'a' = :b"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byContains") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
DeleteQuery.byContains(TEST_TABLE) shouldEqual s"DELETE FROM $TEST_TABLE WHERE data @> :criteria"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy DeleteQuery.byContains(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
|
||||
describe("byJsonPath") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
DeleteQuery.byJsonPath(TEST_TABLE) shouldEqual
|
||||
s"DELETE FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy DeleteQuery.byJsonPath(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package solutions.bitbadger.documents.scala.query
|
||||
|
||||
import org.scalatest.funspec.AnyFunSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import solutions.bitbadger.documents.{AutoId, Configuration, DocumentException}
|
||||
import solutions.bitbadger.documents.query.DocumentQuery
|
||||
import solutions.bitbadger.documents.scala.ClearConfiguration
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
|
||||
class DocumentQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("insert") {
|
||||
it("generates no auto ID | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
DocumentQuery.insert(TEST_TABLE) shouldEqual s"INSERT INTO $TEST_TABLE VALUES (:data)"
|
||||
}
|
||||
it("generates no auto ID | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
DocumentQuery.insert(TEST_TABLE) shouldEqual s"INSERT INTO $TEST_TABLE VALUES (:data)"
|
||||
}
|
||||
it("generates auto number | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER) shouldEqual
|
||||
s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || ('{\"id\":' " +
|
||||
s"|| (SELECT COALESCE(MAX((data->>'id')::numeric), 0) + 1 FROM $TEST_TABLE) || '}')::jsonb)"
|
||||
}
|
||||
it("generates auto number | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
DocumentQuery.insert(TEST_TABLE, AutoId.NUMBER) shouldEqual
|
||||
s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', " +
|
||||
s"(SELECT coalesce(max(data->>'id'), 0) + 1 FROM $TEST_TABLE)))"
|
||||
}
|
||||
it("generates auto UUID | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
|
||||
query should startWith (s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\"")
|
||||
query should endWith ("\"}')")
|
||||
}
|
||||
it("generates auto UUID | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
val query = DocumentQuery.insert(TEST_TABLE, AutoId.UUID)
|
||||
query should startWith (s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '")
|
||||
query should endWith ("'))")
|
||||
}
|
||||
it("generates auto random string | PostgreSQL") {
|
||||
try {
|
||||
ForceDialect.postgres()
|
||||
Configuration.idStringLength = 8
|
||||
val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
|
||||
query should startWith (s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\"")
|
||||
query should endWith ("\"}')")
|
||||
query.replace(s"INSERT INTO $TEST_TABLE VALUES (:data::jsonb || '{\"id\":\"", "").replace("\"}')", "")
|
||||
.length shouldEqual 8
|
||||
} finally {
|
||||
Configuration.idStringLength = 16
|
||||
}
|
||||
}
|
||||
it("insert generates auto random string | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
val query = DocumentQuery.insert(TEST_TABLE, AutoId.RANDOM_STRING)
|
||||
query should startWith (s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '")
|
||||
query should endWith ("'))")
|
||||
query.replace(s"INSERT INTO $TEST_TABLE VALUES (json_set(:data, '$$.id', '", "").replace("'))", "")
|
||||
.length shouldEqual Configuration.idStringLength
|
||||
}
|
||||
it("fails when no dialect is set") {
|
||||
a [DocumentException] should be thrownBy DocumentQuery.insert(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
|
||||
describe("save") {
|
||||
it("generates correctly") {
|
||||
ForceDialect.postgres()
|
||||
DocumentQuery.save(TEST_TABLE) shouldEqual
|
||||
s"INSERT INTO $TEST_TABLE VALUES (:data) ON CONFLICT ((data->>'id')) DO UPDATE SET data = EXCLUDED.data"
|
||||
}
|
||||
}
|
||||
|
||||
describe("update") {
|
||||
it("generates successfully") {
|
||||
DocumentQuery.update(TEST_TABLE) shouldEqual s"UPDATE $TEST_TABLE SET data = :data"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package solutions.bitbadger.documents.scala.query
|
||||
|
||||
import org.scalatest.funspec.AnyFunSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import solutions.bitbadger.documents.{DocumentException, Field}
|
||||
import solutions.bitbadger.documents.query.ExistsQuery
|
||||
import solutions.bitbadger.documents.scala.ClearConfiguration
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
class ExistsQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("byId") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
ExistsQuery.byId(TEST_TABLE) shouldEqual
|
||||
s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it"
|
||||
}
|
||||
it("generates correctly | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
ExistsQuery.byId(TEST_TABLE) shouldEqual
|
||||
s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'id' = :id) AS it"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byFields") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
ExistsQuery.byFields(TEST_TABLE, List(Field.equal("it", 7, ":test")).asJava) shouldEqual
|
||||
s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE (data->>'it')::numeric = :test) AS it"
|
||||
}
|
||||
it("generates correctly | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
ExistsQuery.byFields(TEST_TABLE, List(Field.equal("it", 7, ":test")).asJava) shouldEqual
|
||||
s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data->>'it' = :test) AS it"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byContains") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
ExistsQuery.byContains(TEST_TABLE) shouldEqual
|
||||
s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE data @> :criteria) AS it"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy ExistsQuery.byContains(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
|
||||
describe("byJsonPath") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
ExistsQuery.byJsonPath(TEST_TABLE) shouldEqual
|
||||
s"SELECT EXISTS (SELECT 1 FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)) AS it"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy ExistsQuery.byJsonPath(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package solutions.bitbadger.documents.scala.query
|
||||
|
||||
import org.scalatest.funspec.AnyFunSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import solutions.bitbadger.documents.{DocumentException, Field}
|
||||
import solutions.bitbadger.documents.query.FindQuery
|
||||
import solutions.bitbadger.documents.scala.ClearConfiguration
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
class FindQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("all") {
|
||||
it("generates correctly") {
|
||||
FindQuery.all(TEST_TABLE) shouldEqual s"SELECT data FROM $TEST_TABLE"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byId") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
FindQuery.byId(TEST_TABLE) shouldEqual s"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id"
|
||||
}
|
||||
it("generates correctly | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
FindQuery.byId(TEST_TABLE) shouldEqual s"SELECT data FROM $TEST_TABLE WHERE data->>'id' = :id"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byFields") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
FindQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b"), Field.less("c", 14, ":d")).asJava) shouldEqual
|
||||
s"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND (data->>'c')::numeric < :d"
|
||||
}
|
||||
it("generates correctly | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
FindQuery.byFields(TEST_TABLE, List(Field.equal("a", "", ":b"), Field.less("c", 14, ":d")).asJava) shouldEqual
|
||||
s"SELECT data FROM $TEST_TABLE WHERE data->>'a' = :b AND data->>'c' < :d"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byContains") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
FindQuery.byContains(TEST_TABLE) shouldEqual s"SELECT data FROM $TEST_TABLE WHERE data @> :criteria"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy FindQuery.byContains(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
|
||||
describe("byJsonPath") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
FindQuery.byJsonPath(TEST_TABLE) shouldEqual
|
||||
s"SELECT data FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy FindQuery.byJsonPath(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package solutions.bitbadger.documents.scala.query
|
||||
|
||||
import org.scalatest.funspec.AnyFunSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import solutions.bitbadger.documents.{DocumentException, Field}
|
||||
import solutions.bitbadger.documents.query.PatchQuery
|
||||
import solutions.bitbadger.documents.scala.ClearConfiguration
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
import solutions.bitbadger.documents.support.TypesKt.TEST_TABLE
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
class PatchQuerySpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("byId") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
PatchQuery.byId(TEST_TABLE) shouldEqual s"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'id' = :id"
|
||||
}
|
||||
it("generates correctly | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
PatchQuery.byId(TEST_TABLE) shouldEqual
|
||||
s"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'id' = :id"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byFields") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
PatchQuery.byFields(TEST_TABLE, List(Field.equal("z", "", ":y")).asJava) shouldEqual
|
||||
s"UPDATE $TEST_TABLE SET data = data || :data WHERE data->>'z' = :y"
|
||||
}
|
||||
it("generates correctly | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
PatchQuery.byFields(TEST_TABLE, List(Field.equal("z", "", ":y")).asJava) shouldEqual
|
||||
s"UPDATE $TEST_TABLE SET data = json_patch(data, json(:data)) WHERE data->>'z' = :y"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byContains") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
PatchQuery.byContains(TEST_TABLE) shouldEqual
|
||||
s"UPDATE $TEST_TABLE SET data = data || :data WHERE data @> :criteria"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy PatchQuery.byContains(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
|
||||
describe("byJsonPath") {
|
||||
it("generates correctly | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
PatchQuery.byJsonPath(TEST_TABLE) shouldEqual
|
||||
s"UPDATE $TEST_TABLE SET data = data || :data WHERE jsonb_path_exists(data, :path::jsonpath)"
|
||||
}
|
||||
it("fails | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
a [DocumentException] should be thrownBy PatchQuery.byJsonPath(TEST_TABLE)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package solutions.bitbadger.documents.scala.query
|
||||
|
||||
import org.scalatest.funspec.AnyFunSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import solutions.bitbadger.documents.{Dialect, Field, FieldMatch}
|
||||
import solutions.bitbadger.documents.query.QueryUtils
|
||||
import solutions.bitbadger.documents.scala.ClearConfiguration
|
||||
import solutions.bitbadger.documents.support.ForceDialect
|
||||
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
class QueryUtilsSpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||
|
||||
describe("statementWhere") {
|
||||
it("generates correctly") {
|
||||
QueryUtils.statementWhere("x", "y") shouldEqual "x WHERE y"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byId") {
|
||||
it("generates a numeric ID query | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
QueryUtils.byId("test", 9) shouldEqual "test WHERE (data->>'id')::numeric = :id"
|
||||
}
|
||||
it("generates an alphanumeric ID query | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
QueryUtils.byId("unit", "18") shouldEqual "unit WHERE data->>'id' = :id"
|
||||
}
|
||||
it("generates ID query | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
QueryUtils.byId("yo", 27) shouldEqual "yo WHERE data->>'id' = :id"
|
||||
}
|
||||
}
|
||||
|
||||
describe("byFields") {
|
||||
it("generates default field query | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
QueryUtils.byFields("this",
|
||||
List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava) shouldEqual
|
||||
"this WHERE data->>'a' = :the_a AND (data->>'b')::numeric = :b_value"
|
||||
}
|
||||
it("generates default field query | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
QueryUtils.byFields("this",
|
||||
List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava) shouldEqual
|
||||
"this WHERE data->>'a' = :the_a AND data->>'b' = :b_value"
|
||||
}
|
||||
it("generates ANY field query | PostgreSQL") {
|
||||
ForceDialect.postgres()
|
||||
QueryUtils.byFields("that", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava,
|
||||
FieldMatch.ANY) shouldEqual
|
||||
"that WHERE data->>'a' = :the_a OR (data->>'b')::numeric = :b_value"
|
||||
}
|
||||
it("generates ANY field query | SQLite") {
|
||||
ForceDialect.sqlite()
|
||||
QueryUtils.byFields("that", List(Field.equal("a", "", ":the_a"), Field.equal("b", 0, ":b_value")).asJava,
|
||||
FieldMatch.ANY) shouldEqual
|
||||
"that WHERE data->>'a' = :the_a OR data->>'b' = :b_value"
|
||||
}
|
||||
}
|
||||
|
||||
describe("orderBy") {
|
||||
it("generates for no fields") {
|
||||
QueryUtils.orderBy(List().asJava, Dialect.POSTGRESQL) shouldEqual ""
|
||||
QueryUtils.orderBy(List().asJava, Dialect.SQLITE) shouldEqual ""
|
||||
}
|
||||
it("generates single, no direction | PostgreSQL") {
|
||||
QueryUtils.orderBy(List(Field.named("TestField")).asJava, Dialect.POSTGRESQL) shouldEqual
|
||||
" ORDER BY data->>'TestField'"
|
||||
}
|
||||
it("generates single, no direction | SQLite") {
|
||||
QueryUtils.orderBy(List(Field.named("TestField")).asJava, Dialect.SQLITE) shouldEqual
|
||||
" ORDER BY data->>'TestField'"
|
||||
}
|
||||
it("generates multiple with direction | PostgreSQL") {
|
||||
QueryUtils.orderBy(
|
||||
List(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")).asJava,
|
||||
Dialect.POSTGRESQL) shouldEqual
|
||||
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC"
|
||||
}
|
||||
it("generates multiple with direction | SQLite") {
|
||||
QueryUtils.orderBy(
|
||||
List(Field.named("Nested.Test.Field DESC"), Field.named("AnotherField"), Field.named("It DESC")).asJava,
|
||||
Dialect.SQLITE) shouldEqual
|
||||
" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC"
|
||||
}
|
||||
it("generates numeric ordering | PostgreSQL") {
|
||||
QueryUtils.orderBy(List(Field.named("n:Test")).asJava, Dialect.POSTGRESQL) shouldEqual
|
||||
" ORDER BY (data->>'Test')::numeric"
|
||||
}
|
||||
it("generates numeric ordering | SQLite") {
|
||||
QueryUtils.orderBy(List(Field.named("n:Test")).asJava, Dialect.SQLITE) shouldEqual " ORDER BY data->>'Test'"
|
||||
}
|
||||
it("generates case-insensitive ordering | PostgreSQL") {
|
||||
QueryUtils.orderBy(List(Field.named("i:Test.Field DESC NULLS FIRST")).asJava, Dialect.POSTGRESQL) shouldEqual
|
||||
" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST"
|
||||
}
|
||||
it("generates case-insensitive ordering | SQLite") {
|
||||
QueryUtils.orderBy(List(Field.named("i:Test.Field ASC NULLS LAST")).asJava, Dialect.SQLITE) shouldEqual
|
||||
" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user