Initial Development #1
@ -0,0 +1,26 @@
|
|||||||
|
package solutions.bitbadger.documents.groovy
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import solutions.bitbadger.documents.DocumentIndex
|
||||||
|
|
||||||
|
import static groovy.test.GroovyAssert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for the `DocumentIndex` enum
|
||||||
|
*/
|
||||||
|
@DisplayName('JVM | Groovy | DocumentIndex')
|
||||||
|
class DocumentIndexTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('FULL uses proper SQL')
|
||||||
|
void fullSQL() {
|
||||||
|
assertEquals('The SQL for Full is incorrect', '', DocumentIndex.FULL.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('OPTIMIZED uses proper SQL')
|
||||||
|
void optimizedSQL() {
|
||||||
|
assertEquals('The SQL for Optimized is incorrect', ' jsonb_path_ops', DocumentIndex.OPTIMIZED.sql)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,600 @@
|
|||||||
|
package solutions.bitbadger.documents.groovy
|
||||||
|
|
||||||
|
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 solutions.bitbadger.documents.support.ForceDialect
|
||||||
|
|
||||||
|
import static groovy.test.GroovyAssert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for the `Field` class
|
||||||
|
*/
|
||||||
|
@DisplayName('JVM | Groovy | Field')
|
||||||
|
class FieldTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the connection string (resets Dialect)
|
||||||
|
*/
|
||||||
|
@AfterEach
|
||||||
|
void cleanUp() {
|
||||||
|
ForceDialect.none()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~ INSTANCE METHODS ~~~
|
||||||
|
|
||||||
|
// TODO: fix java.base open issue
|
||||||
|
// @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('A new Field instance should have been created', field, withParam)
|
||||||
|
assertEquals('Name should have been preserved', field.name, withParam.name)
|
||||||
|
assertEquals('Comparison should have been preserved', field.comparison, withParam.comparison)
|
||||||
|
assertEquals('Parameter name not set correctly', ':test', withParam.parameterName)
|
||||||
|
assertEquals('Qualifier should have been preserved', field.qualifier, withParam.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('withParameterName works with at-sign prefix')
|
||||||
|
void withParamNameAtSign() {
|
||||||
|
def field = Field.equal('def', '44')
|
||||||
|
def withParam = field.withParameterName('@unit')
|
||||||
|
assertNotSame('A new Field instance should have been created', field, withParam)
|
||||||
|
assertEquals('Name should have been preserved', field.name, withParam.name)
|
||||||
|
assertEquals('Comparison should have been preserved', field.comparison, withParam.comparison)
|
||||||
|
assertEquals('Parameter name not set correctly', '@unit', withParam.parameterName)
|
||||||
|
assertEquals('Qualifier should have been preserved', field.qualifier, withParam.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('withQualifier sets qualifier correctly')
|
||||||
|
void withQualifier() {
|
||||||
|
def field = Field.equal('j', 'k')
|
||||||
|
def withQual = field.withQualifier('test')
|
||||||
|
assertNotSame('A new Field instance should have been created', field, withQual)
|
||||||
|
assertEquals('Name should have been preserved', field.name, withQual.name)
|
||||||
|
assertEquals('Comparison should have been preserved', field.comparison, withQual.comparison)
|
||||||
|
assertEquals('Parameter Name should have been preserved', field.parameterName, withQual.parameterName)
|
||||||
|
assertEquals('Qualifier not set correctly', 'test', withQual.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('path generates for simple unqualified PostgreSQL field')
|
||||||
|
void pathPostgresSimpleUnqualified() {
|
||||||
|
assertEquals('Path not correct', "data->>'SomethingCool'",
|
||||||
|
Field.greaterOrEqual('SomethingCool', 18).path(Dialect.POSTGRESQL, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('path generates for simple qualified PostgreSQL field')
|
||||||
|
void pathPostgresSimpleQualified() {
|
||||||
|
assertEquals('Path not correct', "this.data->>'SomethingElse'",
|
||||||
|
Field.less('SomethingElse', 9).withQualifier('this').path(Dialect.POSTGRESQL, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('path generates for nested unqualified PostgreSQL field')
|
||||||
|
void pathPostgresNestedUnqualified() {
|
||||||
|
assertEquals('Path not correct', "data#>>'{My,Nested,Field}'",
|
||||||
|
Field.equal('My.Nested.Field', 'howdy').path(Dialect.POSTGRESQL, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('path generates for nested qualified PostgreSQL field')
|
||||||
|
void pathPostgresNestedQualified() {
|
||||||
|
assertEquals('Path not correct', "bird.data#>>'{Nest,Away}'",
|
||||||
|
Field.equal('Nest.Away', 'doc').withQualifier('bird').path(Dialect.POSTGRESQL, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('path generates for simple unqualified SQLite field')
|
||||||
|
void pathSQLiteSimpleUnqualified() {
|
||||||
|
assertEquals('Path not correct', "data->>'SomethingCool'",
|
||||||
|
Field.greaterOrEqual('SomethingCool', 18).path(Dialect.SQLITE, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('path generates for simple qualified SQLite field')
|
||||||
|
void pathSQLiteSimpleQualified() {
|
||||||
|
assertEquals('Path not correct', "this.data->>'SomethingElse'",
|
||||||
|
Field.less('SomethingElse', 9).withQualifier('this').path(Dialect.SQLITE, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('path generates for nested unqualified SQLite field')
|
||||||
|
void pathSQLiteNestedUnqualified() {
|
||||||
|
assertEquals('Path not correct', "data->'My'->'Nested'->>'Field'",
|
||||||
|
Field.equal('My.Nested.Field', 'howdy').path(Dialect.SQLITE, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('path generates for nested qualified SQLite field')
|
||||||
|
void pathSQLiteNestedQualified() {
|
||||||
|
assertEquals('Path not correct', "bird.data->'Nest'->>'Away'",
|
||||||
|
Field.equal('Nest.Away', 'doc').withQualifier('bird').path(Dialect.SQLITE, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for exists w/o qualifier | PostgreSQL')
|
||||||
|
void toWhereExistsNoQualPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->>'that_field' IS NOT NULL",
|
||||||
|
Field.exists('that_field').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for exists w/o qualifier | SQLite')
|
||||||
|
void toWhereExistsNoQualSQLite() {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->>'that_field' IS NOT NULL",
|
||||||
|
Field.exists('that_field').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for not-exists w/o qualifier | PostgreSQL')
|
||||||
|
void toWhereNotExistsNoQualPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->>'a_field' IS NULL",
|
||||||
|
Field.notExists('a_field').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for not-exists w/o qualifier | SQLite')
|
||||||
|
void toWhereNotExistsNoQualSQLite() {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->>'a_field' IS NULL",
|
||||||
|
Field.notExists('a_field').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for BETWEEN w/o qualifier, numeric range | PostgreSQL')
|
||||||
|
void toWhereBetweenNoQualNumericPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly',
|
||||||
|
"(data->>'age')::numeric BETWEEN @agemin AND @agemax", Field.between('age', 13, 17, '@age').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for BETWEEN w/o qualifier, alphanumeric range | PostgreSQL')
|
||||||
|
void toWhereBetweenNoQualAlphaPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->>'city' BETWEEN :citymin AND :citymax",
|
||||||
|
Field.between('city', 'Atlanta', 'Chicago', ':city').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for BETWEEN w/o qualifier | SQLite')
|
||||||
|
void toWhereBetweenNoQualSQLite() {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->>'age' BETWEEN @agemin AND @agemax",
|
||||||
|
Field.between('age', 13, 17, '@age').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for BETWEEN w/ qualifier, numeric range | PostgreSQL')
|
||||||
|
void toWhereBetweenQualNumericPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly',
|
||||||
|
"(test.data->>'age')::numeric BETWEEN @agemin AND @agemax",
|
||||||
|
Field.between('age', 13, 17, '@age').withQualifier('test').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for BETWEEN w/ qualifier, alphanumeric range | PostgreSQL')
|
||||||
|
void toWhereBetweenQualAlphaPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "unit.data->>'city' BETWEEN :citymin AND :citymax",
|
||||||
|
Field.between('city', 'Atlanta', 'Chicago', ':city').withQualifier('unit').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for BETWEEN w/ qualifier | SQLite')
|
||||||
|
void toWhereBetweenQualSQLite() {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "my.data->>'age' BETWEEN @agemin AND @agemax",
|
||||||
|
Field.between('age', 13, 17, '@age').withQualifier('my').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for IN/any, numeric values | PostgreSQL')
|
||||||
|
void toWhereAnyNumericPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly',
|
||||||
|
"(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)",
|
||||||
|
Field.any('even', List.of(2, 4, 6), ':nbr').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for IN/any, alphanumeric values | PostgreSQL')
|
||||||
|
void toWhereAnyAlphaPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->>'test' IN (:city_0, :city_1)",
|
||||||
|
Field.any('test', List.of('Atlanta', 'Chicago'), ':city').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for IN/any | SQLite')
|
||||||
|
void toWhereAnySQLite() {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->>'test' IN (:city_0, :city_1)",
|
||||||
|
Field.any('test', List.of('Atlanta', 'Chicago'), ':city').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for inArray | PostgreSQL')
|
||||||
|
void toWhereInArrayPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]",
|
||||||
|
Field.inArray('even', 'tbl', List.of(2, 4, 6, 8), ':it').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for inArray | SQLite')
|
||||||
|
void toWhereInArraySQLite() {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly',
|
||||||
|
"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())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for others w/o qualifier | PostgreSQL')
|
||||||
|
void toWhereOtherNoQualPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->>'some_field' = :value",
|
||||||
|
Field.equal('some_field', '', ':value').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates for others w/o qualifier | SQLite')
|
||||||
|
void toWhereOtherNoQualSQLite() {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "data->>'some_field' = :value",
|
||||||
|
Field.equal('some_field', '', ':value').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates no-parameter w/ qualifier | PostgreSQL')
|
||||||
|
void toWhereNoParamWithQualPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "test.data->>'no_field' IS NOT NULL",
|
||||||
|
Field.exists('no_field').withQualifier('test').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates no-parameter w/ qualifier | SQLite')
|
||||||
|
void toWhereNoParamWithQualSQLite() {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "test.data->>'no_field' IS NOT NULL",
|
||||||
|
Field.exists('no_field').withQualifier('test').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates parameter w/ qualifier | PostgreSQL')
|
||||||
|
void toWhereParamWithQualPostgres() {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "(q.data->>'le_field')::numeric <= :it",
|
||||||
|
Field.lessOrEqual('le_field', 18, ':it').withQualifier('q').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('toWhere generates parameter w/ qualifier | SQLite')
|
||||||
|
void toWhereParamWithQualSQLite() {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
assertEquals('Field WHERE clause not generated correctly', "q.data->>'le_field' <= :it",
|
||||||
|
Field.lessOrEqual('le_field', 18, ':it').withQualifier('q').toWhere())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~ STATIC CONSTRUCTOR TESTS ~~~
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('equal constructs a field w/o parameter name')
|
||||||
|
void equalCtor() {
|
||||||
|
def field = Field.equal('Test', 14)
|
||||||
|
assertEquals('Field name not filled correctly', 'Test', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.EQUAL, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 14, field.comparison.value)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('equal constructs a field w/ parameter name')
|
||||||
|
void equalParameterCtor() {
|
||||||
|
def field = Field.equal('Test', 14, ':w')
|
||||||
|
assertEquals('Field name not filled correctly', 'Test', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.EQUAL, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 14, field.comparison.value)
|
||||||
|
assertEquals('Field parameter name not filled correctly', ':w', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('greater constructs a field w/o parameter name')
|
||||||
|
void greaterCtor() {
|
||||||
|
def field = Field.greater('Great', 'night')
|
||||||
|
assertEquals('Field name not filled correctly', 'Great', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.GREATER, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 'night', field.comparison.value)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('greater constructs a field w/ parameter name')
|
||||||
|
void greaterParameterCtor() {
|
||||||
|
def field = Field.greater('Great', 'night', ':yeah')
|
||||||
|
assertEquals('Field name not filled correctly', 'Great', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.GREATER, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 'night', field.comparison.value)
|
||||||
|
assertEquals('Field parameter name not filled correctly', ':yeah', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('greaterOrEqual constructs a field w/o parameter name')
|
||||||
|
void greaterOrEqualCtor() {
|
||||||
|
def field = Field.greaterOrEqual('Nice', 88L)
|
||||||
|
assertEquals('Field name not filled correctly', 'Nice', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.GREATER_OR_EQUAL, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 88L, field.comparison.value)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('greaterOrEqual constructs a field w/ parameter name')
|
||||||
|
void greaterOrEqualParameterCtor() {
|
||||||
|
def field = Field.greaterOrEqual('Nice', 88L, ':nice')
|
||||||
|
assertEquals('Field name not filled correctly', 'Nice', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.GREATER_OR_EQUAL, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 88L, field.comparison.value)
|
||||||
|
assertEquals('Field parameter name not filled correctly', ':nice', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('less constructs a field w/o parameter name')
|
||||||
|
void lessCtor() {
|
||||||
|
def field = Field.less('Lesser', 'seven')
|
||||||
|
assertEquals('Field name not filled correctly', 'Lesser', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.LESS, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 'seven', field.comparison.value)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('less constructs a field w/ parameter name')
|
||||||
|
void lessParameterCtor() {
|
||||||
|
def field = Field.less('Lesser', 'seven', ':max')
|
||||||
|
assertEquals('Field name not filled correctly', 'Lesser', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.LESS, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 'seven', field.comparison.value)
|
||||||
|
assertEquals('Field parameter name not filled correctly', ':max', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('lessOrEqual constructs a field w/o parameter name')
|
||||||
|
void lessOrEqualCtor() {
|
||||||
|
def field = Field.lessOrEqual('Nobody', 'KNOWS')
|
||||||
|
assertEquals('Field name not filled correctly', 'Nobody', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.LESS_OR_EQUAL, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 'KNOWS', field.comparison.value)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('lessOrEqual constructs a field w/ parameter name')
|
||||||
|
void lessOrEqualParameterCtor() {
|
||||||
|
def field = Field.lessOrEqual('Nobody', 'KNOWS', ':nope')
|
||||||
|
assertEquals('Field name not filled correctly', 'Nobody', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.LESS_OR_EQUAL, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 'KNOWS', field.comparison.value)
|
||||||
|
assertEquals('Field parameter name not filled correctly', ':nope', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('notEqual constructs a field w/o parameter name')
|
||||||
|
void notEqualCtor() {
|
||||||
|
def field = Field.notEqual('Park', 'here')
|
||||||
|
assertEquals('Field name not filled correctly', 'Park', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.NOT_EQUAL, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 'here', field.comparison.value)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('notEqual constructs a field w/ parameter name')
|
||||||
|
void notEqualParameterCtor() {
|
||||||
|
def field = Field.notEqual('Park', 'here', ':now')
|
||||||
|
assertEquals('Field name not filled correctly', 'Park', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.NOT_EQUAL, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', 'here', field.comparison.value)
|
||||||
|
assertEquals('Field parameter name not filled correctly', ':now', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('between constructs a field w/o parameter name')
|
||||||
|
void betweenCtor() {
|
||||||
|
def field = Field.between('Age', 18, 49)
|
||||||
|
assertEquals('Field name not filled correctly', 'Age', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.BETWEEN, field.comparison.op)
|
||||||
|
assertEquals('Field comparison min value not filled correctly', 18, field.comparison.value.first)
|
||||||
|
assertEquals('Field comparison max value not filled correctly', 49, field.comparison.value.second, )
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('between constructs a field w/ parameter name')
|
||||||
|
void betweenParameterCtor() {
|
||||||
|
def field = Field.between('Age', 18, 49, ':limit')
|
||||||
|
assertEquals('Field name not filled correctly', 'Age', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.BETWEEN, field.comparison.op)
|
||||||
|
assertEquals('Field comparison min value not filled correctly', 18, field.comparison.value.first)
|
||||||
|
assertEquals('Field comparison max value not filled correctly', 49, field.comparison.value.second)
|
||||||
|
assertEquals('Field parameter name not filled correctly', ':limit', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('any constructs a field w/o parameter name')
|
||||||
|
void anyCtor() {
|
||||||
|
def field = Field.any('Here', List.of(8, 16, 32))
|
||||||
|
assertEquals('Field name not filled correctly', 'Here', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.IN, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', List.of(8, 16, 32), field.comparison.value)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('any constructs a field w/ parameter name')
|
||||||
|
void anyParameterCtor() {
|
||||||
|
def field = Field.any('Here', List.of(8, 16, 32), ':list')
|
||||||
|
assertEquals('Field name not filled correctly', 'Here', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.IN, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', List.of(8, 16, 32), field.comparison.value)
|
||||||
|
assertEquals('Field parameter name not filled correctly', ':list', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('inArray constructs a field w/o parameter name')
|
||||||
|
void inArrayCtor() {
|
||||||
|
def field = Field.inArray('ArrayField', 'table', List.of('z'))
|
||||||
|
assertEquals('Field name not filled correctly', 'ArrayField', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.IN_ARRAY, field.comparison.op)
|
||||||
|
assertEquals('Field comparison table not filled correctly', 'table', field.comparison.value.first)
|
||||||
|
assertEquals('Field comparison values not filled correctly', List.of('z'), field.comparison.value.second)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('inArray constructs a field w/ parameter name')
|
||||||
|
void inArrayParameterCtor() {
|
||||||
|
def field = Field.inArray('ArrayField', 'table', List.of('z'), ':a')
|
||||||
|
assertEquals('Field name not filled correctly', 'ArrayField', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.IN_ARRAY, field.comparison.op)
|
||||||
|
assertEquals('Field comparison table not filled correctly', 'table', field.comparison.value.first)
|
||||||
|
assertEquals('Field comparison values not filled correctly', List.of('z'), field.comparison.value.second)
|
||||||
|
assertEquals('Field parameter name not filled correctly', ':a', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('exists constructs a field')
|
||||||
|
void existsCtor() {
|
||||||
|
def field = Field.exists 'Groovy'
|
||||||
|
assertEquals('Field name not filled correctly', 'Groovy', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.EXISTS, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', '', field.comparison.value)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('notExists constructs a field')
|
||||||
|
void notExistsCtor() {
|
||||||
|
def field = Field.notExists 'Groovy'
|
||||||
|
assertEquals('Field name not filled correctly', 'Groovy', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.NOT_EXISTS, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', '', field.comparison.value)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('named constructs a field')
|
||||||
|
void namedCtor() {
|
||||||
|
def field = Field.named('Tacos')
|
||||||
|
assertEquals('Field name not filled correctly', 'Tacos', field.name)
|
||||||
|
assertEquals('Field comparison operation not filled correctly', Op.EQUAL, field.comparison.op)
|
||||||
|
assertEquals('Field comparison value not filled correctly', '', field.comparison.value)
|
||||||
|
assertNull('The parameter name should have been null', field.parameterName)
|
||||||
|
assertNull('The qualifier should have been null', field.qualifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: fix java.base open issue
|
||||||
|
// @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('Path not constructed correctly', "data->>'Simple'",
|
||||||
|
Field.nameToPath('Simple', Dialect.POSTGRESQL, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('nameToPath creates a simple SQLite SQL name')
|
||||||
|
void nameToPathSQLiteSimpleSQL() {
|
||||||
|
assertEquals('Path not constructed correctly', "data->>'Simple'",
|
||||||
|
Field.nameToPath('Simple', Dialect.SQLITE, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('nameToPath creates a nested PostgreSQL SQL name')
|
||||||
|
void nameToPathPostgresNestedSQL() {
|
||||||
|
assertEquals('Path not constructed correctly', "data#>>'{A,Long,Path,to,the,Property}'",
|
||||||
|
Field.nameToPath('A.Long.Path.to.the.Property', Dialect.POSTGRESQL, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('nameToPath creates a nested SQLite SQL name')
|
||||||
|
void nameToPathSQLiteNestedSQL() {
|
||||||
|
assertEquals('Path not constructed correctly', "data->'A'->'Long'->'Path'->'to'->'the'->>'Property'",
|
||||||
|
Field.nameToPath('A.Long.Path.to.the.Property', Dialect.SQLITE, FieldFormat.SQL))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('nameToPath creates a simple PostgreSQL JSON name')
|
||||||
|
void nameToPathPostgresSimpleJSON() {
|
||||||
|
assertEquals('Path not constructed correctly', "data->'Simple'",
|
||||||
|
Field.nameToPath('Simple', Dialect.POSTGRESQL, FieldFormat.JSON))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('nameToPath creates a simple SQLite JSON name')
|
||||||
|
void nameToPathSQLiteSimpleJSON() {
|
||||||
|
assertEquals('Path not constructed correctly', "data->'Simple'",
|
||||||
|
Field.nameToPath('Simple', Dialect.SQLITE, FieldFormat.JSON))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('nameToPath creates a nested PostgreSQL JSON name')
|
||||||
|
void nameToPathPostgresNestedJSON() {
|
||||||
|
assertEquals('Path not constructed correctly', "data#>'{A,Long,Path,to,the,Property}'",
|
||||||
|
Field.nameToPath('A.Long.Path.to.the.Property', Dialect.POSTGRESQL, FieldFormat.JSON))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('nameToPath creates a nested SQLite JSON name')
|
||||||
|
void nameToPathSQLiteNestedJSON() {
|
||||||
|
assertEquals('Path not constructed correctly', "data->'A'->'Long'->'Path'->'to'->'the'->'Property'",
|
||||||
|
Field.nameToPath('A.Long.Path.to.the.Property', Dialect.SQLITE, FieldFormat.JSON))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package solutions.bitbadger.documents.groovy
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import solutions.bitbadger.documents.Op
|
||||||
|
|
||||||
|
import static groovy.test.GroovyAssert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for the `Op` enum
|
||||||
|
*/
|
||||||
|
@DisplayName('JVM | Groovy | Op')
|
||||||
|
class OpTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('EQUAL uses proper SQL')
|
||||||
|
void equalSQL() {
|
||||||
|
assertEquals('The SQL for equal is incorrect', '=', Op.EQUAL.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('GREATER uses proper SQL')
|
||||||
|
void greaterSQL() {
|
||||||
|
assertEquals('The SQL for greater is incorrect', '>', Op.GREATER.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('GREATER_OR_EQUAL uses proper SQL')
|
||||||
|
void greaterOrEqualSQL() {
|
||||||
|
assertEquals('The SQL for greater-or-equal is incorrect', '>=', Op.GREATER_OR_EQUAL.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('LESS uses proper SQL')
|
||||||
|
void lessSQL() {
|
||||||
|
assertEquals('The SQL for less is incorrect', '<', Op.LESS.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('LESS_OR_EQUAL uses proper SQL')
|
||||||
|
void lessOrEqualSQL() {
|
||||||
|
assertEquals('The SQL for less-or-equal is incorrect', '<=', Op.LESS_OR_EQUAL.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('NOT_EQUAL uses proper SQL')
|
||||||
|
void notEqualSQL() {
|
||||||
|
assertEquals('The SQL for not-equal is incorrect', '<>', Op.NOT_EQUAL.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('BETWEEN uses proper SQL')
|
||||||
|
void betweenSQL() {
|
||||||
|
assertEquals('The SQL for between is incorrect', 'BETWEEN', Op.BETWEEN.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('IN uses proper SQL')
|
||||||
|
void inSQL() {
|
||||||
|
assertEquals('The SQL for in is incorrect', 'IN', Op.IN.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('IN_ARRAY uses proper SQL')
|
||||||
|
void inArraySQL() {
|
||||||
|
assertEquals('The SQL for in-array is incorrect', '??|', Op.IN_ARRAY.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('EXISTS uses proper SQL')
|
||||||
|
void existsSQL() {
|
||||||
|
assertEquals('The SQL for exists is incorrect', 'IS NOT NULL', Op.EXISTS.sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('NOT_EXISTS uses proper SQL')
|
||||||
|
void notExistsSQL() {
|
||||||
|
assertEquals('The SQL for not-exists is incorrect', 'IS NULL', Op.NOT_EXISTS.sql)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package solutions.bitbadger.documents.groovy
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import solutions.bitbadger.documents.ParameterName
|
||||||
|
import static groovy.test.GroovyAssert.assertEquals
|
||||||
|
/**
|
||||||
|
* Unit tests for the `ParameterName` class
|
||||||
|
*/
|
||||||
|
@DisplayName('JVM | Groovy | ParameterName')
|
||||||
|
class ParameterNameTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('derive works when given existing names')
|
||||||
|
void withExisting() {
|
||||||
|
def names = new ParameterName()
|
||||||
|
assertEquals('Name should have been :taco', ':taco', names.derive(':taco'))
|
||||||
|
assertEquals('Counter should not have advanced for named field', ':field0', names.derive(null))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName('derive works when given all anonymous fields')
|
||||||
|
void allAnonymous() {
|
||||||
|
def names = new ParameterName()
|
||||||
|
assertEquals('Anonymous field name should have been returned', ':field0', names.derive(null))
|
||||||
|
assertEquals('Counter should have advanced from previous call', ':field1', names.derive(null))
|
||||||
|
assertEquals('Counter should have advanced from previous call', ':field2', names.derive(null))
|
||||||
|
assertEquals('Counter should have advanced from previous call', ':field3', names.derive(null))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package solutions.bitbadger.documents.groovy
|
||||||
|
|
||||||
|
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 groovy.test.GroovyAssert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for the `Parameter` class
|
||||||
|
*/
|
||||||
|
@DisplayName('JVM | Groovy | Parameter')
|
||||||
|
class ParameterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Construction with colon-prefixed name")
|
||||||
|
void ctorWithColon() {
|
||||||
|
def p = new Parameter(":test", ParameterType.STRING, "ABC")
|
||||||
|
assertEquals("Parameter name was incorrect", ":test", p.name)
|
||||||
|
assertEquals("Parameter type was incorrect", ParameterType.STRING, p.type)
|
||||||
|
assertEquals("Parameter value was incorrect", "ABC", p.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Construction with at-sign-prefixed name")
|
||||||
|
void ctorWithAtSign() {
|
||||||
|
def p = new Parameter("@yo", ParameterType.NUMBER, null)
|
||||||
|
assertEquals("Parameter name was incorrect", "@yo", p.name)
|
||||||
|
assertEquals("Parameter type was incorrect", ParameterType.NUMBER, p.type)
|
||||||
|
assertNull("Parameter value was incorrect", p.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: resolve java.base open issue
|
||||||
|
// @Test
|
||||||
|
// @DisplayName("Construction fails with incorrect prefix")
|
||||||
|
// void ctorFailsForPrefix() {
|
||||||
|
// assertThrows(DocumentException) { new Parameter("it", ParameterType.JSON, "") }
|
||||||
|
// }
|
||||||
|
}
|
@ -452,7 +452,7 @@ class FieldTest {
|
|||||||
assertEquals(Op.BETWEEN, field.comparison.op, "Field comparison operation 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(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(49, field.comparison.value.second, "Field comparison max value not filled correctly")
|
||||||
assertEquals(":limit", field.parameterName, "The parameter name should have been null")
|
assertEquals(":limit", field.parameterName, "Field parameter name not filled correctly")
|
||||||
assertNull(field.qualifier, "The qualifier should have been null")
|
assertNull(field.qualifier, "The qualifier should have been null")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,85 +1,86 @@
|
|||||||
package solutions.bitbadger.documents.scala
|
package solutions.bitbadger.documents.scala
|
||||||
|
|
||||||
import org.scalatest.funspec.AnyFunSpec
|
import org.scalatest.funspec.AnyFunSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
import solutions.bitbadger.documents.scala.support.{ByteIdClass, IntIdClass, LongIdClass, ShortIdClass, StringIdClass}
|
import solutions.bitbadger.documents.scala.support.{ByteIdClass, IntIdClass, LongIdClass, ShortIdClass, StringIdClass}
|
||||||
import solutions.bitbadger.documents.{AutoId, DocumentException}
|
import solutions.bitbadger.documents.{AutoId, DocumentException}
|
||||||
|
|
||||||
class AutoIdSpec extends AnyFunSpec {
|
class AutoIdSpec extends AnyFunSpec with Matchers {
|
||||||
|
|
||||||
describe("generateUUID") {
|
describe("generateUUID") {
|
||||||
it("generates a UUID string") {
|
it("generates a UUID string") {
|
||||||
assert(32 == AutoId.generateUUID().length)
|
AutoId.generateUUID().length shouldEqual 32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("generateRandomString") {
|
describe("generateRandomString") {
|
||||||
it("generates a random hex character string of an even length") {
|
it("generates a random hex character string of an even length") {
|
||||||
assert(8 == AutoId.generateRandomString(8).length)
|
AutoId.generateRandomString(8).length shouldEqual 8
|
||||||
}
|
}
|
||||||
it("generates a random hex character string of an odd length") {
|
it("generates a random hex character string of an odd length") {
|
||||||
assert(11 == AutoId.generateRandomString(11).length)
|
AutoId.generateRandomString(11).length shouldEqual 11
|
||||||
}
|
}
|
||||||
it("generates different random hex character strings") {
|
it("generates different random hex character strings") {
|
||||||
val result1 = AutoId.generateRandomString(16)
|
val result1 = AutoId.generateRandomString(16)
|
||||||
val result2 = AutoId.generateRandomString(16)
|
val result2 = AutoId.generateRandomString(16)
|
||||||
assert(result1 != result2)
|
result1 should not be theSameInstanceAs (result2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("needsAutoId") {
|
describe("needsAutoId") {
|
||||||
it("fails for null document") {
|
it("fails for null document") {
|
||||||
assertThrows[DocumentException] { AutoId.needsAutoId(AutoId.DISABLED, null, "id") }
|
an [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.DISABLED, null, "id")
|
||||||
}
|
}
|
||||||
it("fails for missing ID property") {
|
it("fails for missing ID property") {
|
||||||
assertThrows[DocumentException] { AutoId.needsAutoId(AutoId.UUID, IntIdClass(0), "Id") }
|
an [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.UUID, IntIdClass(0), "Id")
|
||||||
}
|
}
|
||||||
it("returns false if disabled") {
|
it("returns false if disabled") {
|
||||||
assert(!AutoId.needsAutoId(AutoId.DISABLED, "", ""))
|
AutoId.needsAutoId(AutoId.DISABLED, "", "") shouldBe false
|
||||||
}
|
}
|
||||||
it("returns true for Number strategy and byte ID of 0") {
|
it("returns true for Number strategy and byte ID of 0") {
|
||||||
assert(AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(0), "id"))
|
AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(0), "id") shouldBe true
|
||||||
}
|
}
|
||||||
it("returns false for Number strategy and byte ID of non-0") {
|
it("returns false for Number strategy and byte ID of non-0") {
|
||||||
assert(!AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(77), "id"))
|
AutoId.needsAutoId(AutoId.NUMBER, ByteIdClass(77), "id") shouldBe false
|
||||||
}
|
}
|
||||||
it("returns true for Number strategy and short ID of 0") {
|
it("returns true for Number strategy and short ID of 0") {
|
||||||
assert(AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(0), "id"))
|
AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(0), "id") shouldBe true
|
||||||
}
|
}
|
||||||
it("returns false for Number strategy and short ID of non-0") {
|
it("returns false for Number strategy and short ID of non-0") {
|
||||||
assert(!AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(31), "id"))
|
AutoId.needsAutoId(AutoId.NUMBER, ShortIdClass(31), "id") shouldBe false
|
||||||
}
|
}
|
||||||
it("returns true for Number strategy and int ID of 0") {
|
it("returns true for Number strategy and int ID of 0") {
|
||||||
assert(AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(0), "id"))
|
AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(0), "id") shouldBe true
|
||||||
}
|
}
|
||||||
it("returns false for Number strategy and int ID of non-0") {
|
it("returns false for Number strategy and int ID of non-0") {
|
||||||
assert(!AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(6), "id"))
|
AutoId.needsAutoId(AutoId.NUMBER, IntIdClass(6), "id") shouldBe false
|
||||||
}
|
}
|
||||||
it("returns true for Number strategy and long ID of 0") {
|
it("returns true for Number strategy and long ID of 0") {
|
||||||
assert(AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(0), "id"))
|
AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(0), "id") shouldBe true
|
||||||
}
|
}
|
||||||
it("returns false for Number strategy and long ID of non-0") {
|
it("returns false for Number strategy and long ID of non-0") {
|
||||||
assert(!AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(2), "id"))
|
AutoId.needsAutoId(AutoId.NUMBER, LongIdClass(2), "id") shouldBe false
|
||||||
}
|
}
|
||||||
it("fails for Number strategy and non-number ID") {
|
it("fails for Number strategy and non-number ID") {
|
||||||
assertThrows[DocumentException] { AutoId.needsAutoId(AutoId.NUMBER, StringIdClass(""), "id") }
|
an [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.NUMBER, StringIdClass(""), "id")
|
||||||
}
|
}
|
||||||
it("returns true for UUID strategy and blank ID") {
|
it("returns true for UUID strategy and blank ID") {
|
||||||
assert(AutoId.needsAutoId(AutoId.UUID, StringIdClass(""), "id"))
|
AutoId.needsAutoId(AutoId.UUID, StringIdClass(""), "id") shouldBe true
|
||||||
}
|
}
|
||||||
it("returns false for UUID strategy and non-blank ID") {
|
it("returns false for UUID strategy and non-blank ID") {
|
||||||
assert(!AutoId.needsAutoId(AutoId.UUID, StringIdClass("howdy"), "id"))
|
AutoId.needsAutoId(AutoId.UUID, StringIdClass("howdy"), "id") shouldBe false
|
||||||
}
|
}
|
||||||
it("fails for UUID strategy and non-string ID") {
|
it("fails for UUID strategy and non-string ID") {
|
||||||
assertThrows[DocumentException] { AutoId.needsAutoId(AutoId.UUID, IntIdClass(5), "id") }
|
an [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.UUID, IntIdClass(5), "id")
|
||||||
}
|
}
|
||||||
it("returns true for Random String strategy and blank ID") {
|
it("returns true for Random String strategy and blank ID") {
|
||||||
assert(AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass(""), "id"))
|
AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass(""), "id") shouldBe true
|
||||||
}
|
}
|
||||||
it("returns false for Random String strategy and non-blank ID") {
|
it("returns false for Random String strategy and non-blank ID") {
|
||||||
assert(!AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass("full"), "id"))
|
AutoId.needsAutoId(AutoId.RANDOM_STRING, StringIdClass("full"), "id") shouldBe false
|
||||||
}
|
}
|
||||||
it("fails for Random String strategy and non-string ID") {
|
it("fails for Random String strategy and non-string ID") {
|
||||||
assertThrows[DocumentException] { AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id") }
|
an [DocumentException] should be thrownBy AutoId.needsAutoId(AutoId.RANDOM_STRING, ShortIdClass(55), "id")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package solutions.bitbadger.documents.scala
|
||||||
|
|
||||||
|
import org.scalatest.{BeforeAndAfterEach, Suite}
|
||||||
|
import solutions.bitbadger.documents.support.ForceDialect
|
||||||
|
|
||||||
|
trait ClearConfiguration extends BeforeAndAfterEach { this: Suite =>
|
||||||
|
|
||||||
|
override def afterEach(): Unit =
|
||||||
|
try super.afterEach ()
|
||||||
|
finally ForceDialect.none()
|
||||||
|
}
|
@ -1,34 +1,35 @@
|
|||||||
package solutions.bitbadger.documents.scala
|
package solutions.bitbadger.documents.scala
|
||||||
|
|
||||||
import org.scalatest.funspec.AnyFunSpec
|
import org.scalatest.funspec.AnyFunSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
import solutions.bitbadger.documents.{AutoId, Configuration, Dialect, DocumentException}
|
import solutions.bitbadger.documents.{AutoId, Configuration, Dialect, DocumentException}
|
||||||
|
|
||||||
class ConfigurationSpec extends AnyFunSpec {
|
class ConfigurationSpec extends AnyFunSpec with Matchers {
|
||||||
|
|
||||||
describe("idField") {
|
describe("idField") {
|
||||||
it("defaults to `id`") {
|
it("defaults to `id`") {
|
||||||
assert("id" == Configuration.idField)
|
Configuration.idField shouldEqual "id"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("autoIdStrategy") {
|
describe("autoIdStrategy") {
|
||||||
it("defaults to `DISABLED`") {
|
it("defaults to `DISABLED`") {
|
||||||
assert(AutoId.DISABLED == Configuration.autoIdStrategy)
|
Configuration.autoIdStrategy shouldEqual AutoId.DISABLED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("idStringLength") {
|
describe("idStringLength") {
|
||||||
it("defaults to 16") {
|
it("defaults to 16") {
|
||||||
assert(16 == Configuration.idStringLength)
|
Configuration.idStringLength shouldEqual 16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("dialect") {
|
describe("dialect") {
|
||||||
it("is derived from connection string") {
|
it("is derived from connection string") {
|
||||||
try {
|
try {
|
||||||
assertThrows[DocumentException] { Configuration.dialect() }
|
an [DocumentException] should be thrownBy Configuration.dialect()
|
||||||
Configuration.setConnectionString("jdbc:postgresql:db")
|
Configuration.setConnectionString("jdbc:postgresql:db")
|
||||||
assert(Dialect.POSTGRESQL == Configuration.dialect())
|
Configuration.dialect() shouldEqual Dialect.POSTGRESQL
|
||||||
} finally {
|
} finally {
|
||||||
Configuration.setConnectionString(null)
|
Configuration.setConnectionString(null)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
package solutions.bitbadger.documents.scala
|
package solutions.bitbadger.documents.scala
|
||||||
|
|
||||||
import org.scalatest.funspec.AnyFunSpec
|
import org.scalatest.funspec.AnyFunSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
import solutions.bitbadger.documents.{Dialect, DocumentException}
|
import solutions.bitbadger.documents.{Dialect, DocumentException}
|
||||||
|
|
||||||
class DialectSpec extends AnyFunSpec {
|
class DialectSpec extends AnyFunSpec with Matchers {
|
||||||
|
|
||||||
describe("deriveFromConnectionString") {
|
describe("deriveFromConnectionString") {
|
||||||
it("derives PostgreSQL correctly") {
|
it("derives PostgreSQL correctly") {
|
||||||
assert(Dialect.POSTGRESQL == Dialect.deriveFromConnectionString("jdbc:postgresql:db"))
|
Dialect.deriveFromConnectionString("jdbc:postgresql:db") shouldEqual Dialect.POSTGRESQL
|
||||||
}
|
}
|
||||||
it("derives SQLite correctly") {
|
it("derives SQLite correctly") {
|
||||||
assert(Dialect.SQLITE == Dialect.deriveFromConnectionString("jdbc:sqlite:memory"))
|
Dialect.deriveFromConnectionString("jdbc:sqlite:memory") shouldEqual Dialect.SQLITE
|
||||||
}
|
}
|
||||||
it("fails when the connection string is unknown") {
|
it("fails when the connection string is unknown") {
|
||||||
try {
|
try {
|
||||||
@ -18,8 +19,8 @@ class DialectSpec extends AnyFunSpec {
|
|||||||
fail("Dialect derivation should have failed")
|
fail("Dialect derivation should have failed")
|
||||||
} catch {
|
} catch {
|
||||||
case ex: DocumentException =>
|
case ex: DocumentException =>
|
||||||
assert(ex.getMessage != null)
|
ex.getMessage should not be null
|
||||||
assert(ex.getMessage.contains("[SQL Server]"))
|
ex.getMessage should include ("[SQL Server]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package solutions.bitbadger.documents.scala
|
||||||
|
|
||||||
|
import org.scalatest.funspec.AnyFunSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import solutions.bitbadger.documents.DocumentIndex
|
||||||
|
|
||||||
|
class DocumentIndexSpec extends AnyFunSpec with Matchers {
|
||||||
|
|
||||||
|
describe("sql") {
|
||||||
|
it("returns blank for FULL") {
|
||||||
|
DocumentIndex.FULL.getSql shouldEqual ""
|
||||||
|
}
|
||||||
|
it("returns jsonb_path_ops for OPTIMIZED") {
|
||||||
|
DocumentIndex.OPTIMIZED.getSql shouldEqual " jsonb_path_ops"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,20 @@
|
|||||||
package solutions.bitbadger.documents.scala
|
package solutions.bitbadger.documents.scala
|
||||||
|
|
||||||
import org.scalatest.funspec.AnyFunSpec
|
import org.scalatest.funspec.AnyFunSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
import solutions.bitbadger.documents.FieldMatch
|
import solutions.bitbadger.documents.FieldMatch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for the `FieldMatch` enum
|
* Unit tests for the `FieldMatch` enum
|
||||||
*/
|
*/
|
||||||
class FieldMatchSpec extends AnyFunSpec {
|
class FieldMatchSpec extends AnyFunSpec with Matchers {
|
||||||
|
|
||||||
describe("sql") {
|
describe("sql") {
|
||||||
describe("ANY") {
|
it("returns OR for ANY") {
|
||||||
it("should use OR") {
|
FieldMatch.ANY.getSql shouldEqual "OR"
|
||||||
assert("OR" == FieldMatch.ANY.getSql)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
describe("ALL") {
|
|
||||||
it("should use AND") {
|
|
||||||
assert("AND" == FieldMatch.ALL.getSql)
|
|
||||||
}
|
}
|
||||||
|
it("returns AND for ALL") {
|
||||||
|
FieldMatch.ALL.getSql shouldEqual "AND"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,428 @@
|
|||||||
|
package solutions.bitbadger.documents.scala
|
||||||
|
|
||||||
|
import org.scalatest.funspec.AnyFunSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import solutions.bitbadger.documents.support.ForceDialect
|
||||||
|
import solutions.bitbadger.documents.{Dialect, DocumentException, Field, FieldFormat, Op}
|
||||||
|
|
||||||
|
import scala.jdk.CollectionConverters.*
|
||||||
|
|
||||||
|
class FieldSpec extends AnyFunSpec with ClearConfiguration with Matchers {
|
||||||
|
|
||||||
|
// ~~~ INSTANCE METHODS ~~~
|
||||||
|
|
||||||
|
describe("withParameterName") {
|
||||||
|
it("fails for invalid name") {
|
||||||
|
an [DocumentException] should be thrownBy Field.equal("it", "").withParameterName("2424")
|
||||||
|
}
|
||||||
|
it("works with colon prefix") {
|
||||||
|
val field = Field.equal("abc", "22").withQualifier("me")
|
||||||
|
val withParam = field.withParameterName(":test")
|
||||||
|
withParam should not be theSameInstanceAs (field)
|
||||||
|
withParam.getName shouldEqual field.getName
|
||||||
|
withParam.getComparison shouldEqual field.getComparison
|
||||||
|
withParam.getParameterName shouldEqual ":test"
|
||||||
|
withParam.getQualifier shouldEqual field.getQualifier
|
||||||
|
}
|
||||||
|
it("works with at-sign prefix") {
|
||||||
|
val field = Field.equal("def", "44")
|
||||||
|
val withParam = field.withParameterName("@unit")
|
||||||
|
withParam should not be theSameInstanceAs (field)
|
||||||
|
withParam.getName shouldEqual field.getName
|
||||||
|
withParam.getComparison shouldEqual field.getComparison
|
||||||
|
withParam.getParameterName shouldEqual "@unit"
|
||||||
|
withParam.getQualifier shouldEqual field.getQualifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("withQualifier") {
|
||||||
|
it("sets qualifier correctly") {
|
||||||
|
val field = Field.equal("j", "k")
|
||||||
|
val withQual = field.withQualifier("test")
|
||||||
|
withQual should not be theSameInstanceAs (field)
|
||||||
|
withQual.getName shouldEqual field.getName
|
||||||
|
withQual.getComparison shouldEqual field.getComparison
|
||||||
|
withQual.getParameterName shouldEqual field.getParameterName
|
||||||
|
withQual.getQualifier shouldEqual "test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("path") {
|
||||||
|
it("generates simple unqualified PostgreSQL field") {
|
||||||
|
Field.greaterOrEqual("SomethingCool", 18)
|
||||||
|
.path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual "data->>'SomethingCool'"
|
||||||
|
}
|
||||||
|
it("generates simple qualified PostgreSQL field") {
|
||||||
|
Field.less("SomethingElse", 9).withQualifier("this")
|
||||||
|
.path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual "this.data->>'SomethingElse'"
|
||||||
|
}
|
||||||
|
it("generates nested unqualified PostgreSQL field") {
|
||||||
|
Field.equal("My.Nested.Field", "howdy")
|
||||||
|
.path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual "data#>>'{My,Nested,Field}'"
|
||||||
|
}
|
||||||
|
it("generates nested qualified PostgreSQL field") {
|
||||||
|
Field.equal("Nest.Away", "doc").withQualifier("bird")
|
||||||
|
.path(Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual "bird.data#>>'{Nest,Away}'"
|
||||||
|
}
|
||||||
|
it("generates simple unqualified SQLite field") {
|
||||||
|
Field.greaterOrEqual("SomethingCool", 18)
|
||||||
|
.path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual "data->>'SomethingCool'"
|
||||||
|
}
|
||||||
|
it("generates simple qualified SQLite field") {
|
||||||
|
Field.less("SomethingElse", 9).withQualifier("this")
|
||||||
|
.path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual "this.data->>'SomethingElse'"
|
||||||
|
}
|
||||||
|
it("generates nested unqualified SQLite field") {
|
||||||
|
Field.equal("My.Nested.Field", "howdy")
|
||||||
|
.path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual "data->'My'->'Nested'->>'Field'"
|
||||||
|
}
|
||||||
|
it("generates nested qualified SQLite field") {
|
||||||
|
Field.equal("Nest.Away", "doc").withQualifier("bird")
|
||||||
|
.path(Dialect.SQLITE, FieldFormat.SQL) shouldEqual "bird.data->'Nest'->>'Away'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("toWhere") {
|
||||||
|
it("generates exists w/o qualifier | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.exists("that_field").toWhere shouldEqual "data->>'that_field' IS NOT NULL"
|
||||||
|
}
|
||||||
|
it("generates exists w/o qualifier | SQLite") {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
Field.exists("that_field").toWhere shouldEqual "data->>'that_field' IS NOT NULL"
|
||||||
|
}
|
||||||
|
it("generates not-exists w/o qualifier | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.notExists("a_field").toWhere shouldEqual "data->>'a_field' IS NULL"
|
||||||
|
}
|
||||||
|
it("generates not-exists w/o qualifier | SQLite") {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
Field.notExists("a_field").toWhere shouldEqual "data->>'a_field' IS NULL"
|
||||||
|
}
|
||||||
|
it("generates BETWEEN w/o qualifier, numeric range | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.between("age", 13, 17, "@age").toWhere shouldEqual "(data->>'age')::numeric BETWEEN @agemin AND @agemax"
|
||||||
|
}
|
||||||
|
it("generates BETWEEN w/o qualifier, alphanumeric range | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.between("city", "Atlanta", "Chicago", ":city")
|
||||||
|
.toWhere shouldEqual "data->>'city' BETWEEN :citymin AND :citymax"
|
||||||
|
}
|
||||||
|
it("generates BETWEEN w/o qualifier | SQLite") {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
Field.between("age", 13, 17, "@age").toWhere shouldEqual "data->>'age' BETWEEN @agemin AND @agemax"
|
||||||
|
}
|
||||||
|
it("generates BETWEEN w/ qualifier, numeric range | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.between("age", 13, 17, "@age").withQualifier("test")
|
||||||
|
.toWhere shouldEqual "(test.data->>'age')::numeric BETWEEN @agemin AND @agemax"
|
||||||
|
}
|
||||||
|
it("generates BETWEEN w/ qualifier, alphanumeric range | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.between("city", "Atlanta", "Chicago", ":city").withQualifier("unit")
|
||||||
|
.toWhere shouldEqual "unit.data->>'city' BETWEEN :citymin AND :citymax"
|
||||||
|
}
|
||||||
|
it("generates BETWEEN w/ qualifier | SQLite") {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
Field.between("age", 13, 17, "@age").withQualifier("my")
|
||||||
|
.toWhere shouldEqual "my.data->>'age' BETWEEN @agemin AND @agemax"
|
||||||
|
}
|
||||||
|
it("generates IN/any, numeric values | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.any("even", List(2, 4, 6).asJava, ":nbr")
|
||||||
|
.toWhere shouldEqual "(data->>'even')::numeric IN (:nbr_0, :nbr_1, :nbr_2)"
|
||||||
|
}
|
||||||
|
it("generates IN/any, alphanumeric values | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.any("test", List("Atlanta", "Chicago").asJava, ":city")
|
||||||
|
.toWhere shouldEqual "data->>'test' IN (:city_0, :city_1)"
|
||||||
|
}
|
||||||
|
it("generates IN/any | SQLite") {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
Field.any("test", List("Atlanta", "Chicago").asJava, ":city")
|
||||||
|
.toWhere shouldEqual "data->>'test' IN (:city_0, :city_1)"
|
||||||
|
}
|
||||||
|
it("generates inArray | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.inArray("even", "tbl", List(2, 4, 6, 8).asJava, ":it")
|
||||||
|
.toWhere shouldEqual "data->'even' ??| ARRAY[:it_0, :it_1, :it_2, :it_3]"
|
||||||
|
}
|
||||||
|
it("generates inArray | SQLite") {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
Field.inArray("test", "tbl", List("Atlanta", "Chicago").asJava, ":city")
|
||||||
|
.toWhere shouldEqual "EXISTS (SELECT 1 FROM json_each(tbl.data, '$.test') WHERE value IN (:city_0, :city_1))"
|
||||||
|
}
|
||||||
|
it("generates others w/o qualifier | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.equal("some_field", "", ":value").toWhere shouldEqual "data->>'some_field' = :value"
|
||||||
|
}
|
||||||
|
it("generates others w/o qualifier | SQLite") {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
Field.equal("some_field", "", ":value").toWhere shouldEqual "data->>'some_field' = :value"
|
||||||
|
}
|
||||||
|
it("generates no-parameter w/ qualifier | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.exists("no_field").withQualifier("test").toWhere shouldEqual "test.data->>'no_field' IS NOT NULL"
|
||||||
|
}
|
||||||
|
it("generates no-parameter w/ qualifier | SQLite") {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
Field.exists("no_field").withQualifier("test").toWhere shouldEqual "test.data->>'no_field' IS NOT NULL"
|
||||||
|
}
|
||||||
|
it("generates parameter w/ qualifier | PostgreSQL") {
|
||||||
|
ForceDialect.postgres()
|
||||||
|
Field.lessOrEqual("le_field", 18, ":it").withQualifier("q")
|
||||||
|
.toWhere shouldEqual "(q.data->>'le_field')::numeric <= :it"
|
||||||
|
}
|
||||||
|
it("generates parameter w/ qualifier | SQLite") {
|
||||||
|
ForceDialect.sqlite()
|
||||||
|
Field.lessOrEqual("le_field", 18, ":it").withQualifier("q").toWhere shouldEqual "q.data->>'le_field' <= :it"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~ STATIC CONSTRUCTOR TESTS ~~~
|
||||||
|
|
||||||
|
describe("equal") {
|
||||||
|
it("constructs a field w/o parameter name") {
|
||||||
|
val field = Field.equal("Test", 14)
|
||||||
|
field.getName shouldEqual "Test"
|
||||||
|
field.getComparison.getOp shouldEqual Op.EQUAL
|
||||||
|
field.getComparison.getValue shouldEqual 14
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
it("constructs a field w/ parameter name") {
|
||||||
|
val field = Field.equal("Test", 14, ":w")
|
||||||
|
field.getName shouldEqual "Test"
|
||||||
|
field.getComparison.getOp shouldEqual Op.EQUAL
|
||||||
|
field.getComparison.getValue shouldEqual 14
|
||||||
|
field.getParameterName shouldEqual ":w"
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("greater") {
|
||||||
|
it("constructs a field w/o parameter name") {
|
||||||
|
val field = Field.greater("Great", "night")
|
||||||
|
field.getName shouldEqual "Great"
|
||||||
|
field.getComparison.getOp shouldEqual Op.GREATER
|
||||||
|
field.getComparison.getValue shouldEqual "night"
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
it("constructs a field w/ parameter name") {
|
||||||
|
val field = Field.greater("Great", "night", ":yeah")
|
||||||
|
field.getName shouldEqual "Great"
|
||||||
|
field.getComparison.getOp shouldEqual Op.GREATER
|
||||||
|
field.getComparison.getValue shouldEqual "night"
|
||||||
|
field.getParameterName shouldEqual ":yeah"
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("greaterOrEqual") {
|
||||||
|
it("constructs a field w/o parameter name") {
|
||||||
|
val field = Field.greaterOrEqual("Nice", 88L)
|
||||||
|
field.getName shouldEqual "Nice"
|
||||||
|
field.getComparison.getOp shouldEqual Op.GREATER_OR_EQUAL
|
||||||
|
field.getComparison.getValue shouldEqual 88L
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
it("constructs a field w/ parameter name") {
|
||||||
|
val field = Field.greaterOrEqual("Nice", 88L, ":nice")
|
||||||
|
field.getName shouldEqual "Nice"
|
||||||
|
field.getComparison.getOp shouldEqual Op.GREATER_OR_EQUAL
|
||||||
|
field.getComparison.getValue shouldEqual 88L
|
||||||
|
field.getParameterName shouldEqual ":nice"
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("less") {
|
||||||
|
it("constructs a field w/o parameter name") {
|
||||||
|
val field = Field.less("Lesser", "seven")
|
||||||
|
field.getName shouldEqual "Lesser"
|
||||||
|
field.getComparison.getOp shouldEqual Op.LESS
|
||||||
|
field.getComparison.getValue shouldEqual "seven"
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
it("constructs a field w/ parameter name") {
|
||||||
|
val field = Field.less("Lesser", "seven", ":max")
|
||||||
|
field.getName shouldEqual "Lesser"
|
||||||
|
field.getComparison.getOp shouldEqual Op.LESS
|
||||||
|
field.getComparison.getValue shouldEqual "seven"
|
||||||
|
field.getParameterName shouldEqual ":max"
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("lessOrEqual") {
|
||||||
|
it("constructs a field w/o parameter name") {
|
||||||
|
val field = Field.lessOrEqual("Nobody", "KNOWS")
|
||||||
|
field.getName shouldEqual "Nobody"
|
||||||
|
field.getComparison.getOp shouldEqual Op.LESS_OR_EQUAL
|
||||||
|
field.getComparison.getValue shouldEqual "KNOWS"
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
it("constructs a field w/ parameter name") {
|
||||||
|
val field = Field.lessOrEqual("Nobody", "KNOWS", ":nope")
|
||||||
|
field.getName shouldEqual "Nobody"
|
||||||
|
field.getComparison.getOp shouldEqual Op.LESS_OR_EQUAL
|
||||||
|
field.getComparison.getValue shouldEqual "KNOWS"
|
||||||
|
field.getParameterName shouldEqual ":nope"
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("notEqual") {
|
||||||
|
it("constructs a field w/o parameter name") {
|
||||||
|
val field = Field.notEqual("Park", "here")
|
||||||
|
field.getName shouldEqual "Park"
|
||||||
|
field.getComparison.getOp shouldEqual Op.NOT_EQUAL
|
||||||
|
field.getComparison.getValue shouldEqual "here"
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
it("constructs a field w/ parameter name") {
|
||||||
|
val field = Field.notEqual("Park", "here", ":now")
|
||||||
|
field.getName shouldEqual "Park"
|
||||||
|
field.getComparison.getOp shouldEqual Op.NOT_EQUAL
|
||||||
|
field.getComparison.getValue shouldEqual "here"
|
||||||
|
field.getParameterName shouldEqual ":now"
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("between") {
|
||||||
|
it("constructs a field w/o parameter name") {
|
||||||
|
val field = Field.between("Age", 18, 49)
|
||||||
|
field.getName shouldEqual "Age"
|
||||||
|
field.getComparison.getOp shouldEqual Op.BETWEEN
|
||||||
|
field.getComparison.getValue.getFirst shouldEqual 18
|
||||||
|
field.getComparison.getValue.getSecond shouldEqual 49
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
it("constructs a field w/ parameter name") {
|
||||||
|
val field = Field.between("Age", 18, 49, ":limit")
|
||||||
|
field.getName shouldEqual "Age"
|
||||||
|
field.getComparison.getOp shouldEqual Op.BETWEEN
|
||||||
|
field.getComparison.getValue.getFirst shouldEqual 18
|
||||||
|
field.getComparison.getValue.getSecond shouldEqual 49
|
||||||
|
field.getParameterName shouldEqual ":limit"
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("any") {
|
||||||
|
it("constructs a field w/o parameter name") {
|
||||||
|
val field = Field.any("Here", List(8, 16, 32).asJava)
|
||||||
|
field.getName shouldEqual "Here"
|
||||||
|
field.getComparison.getOp shouldEqual Op.IN
|
||||||
|
field.getComparison.getValue should be (List(8, 16, 32).asJava)
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
it("constructs a field w/ parameter name") {
|
||||||
|
val field = Field.any("Here", List(8, 16, 32).asJava, ":list")
|
||||||
|
field.getName shouldEqual "Here"
|
||||||
|
field.getComparison.getOp shouldEqual Op.IN
|
||||||
|
field.getComparison.getValue should be (List(8, 16, 32).asJava)
|
||||||
|
field.getParameterName shouldEqual ":list"
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("inArray") {
|
||||||
|
it("constructs a field w/o parameter name") {
|
||||||
|
val field = Field.inArray("ArrayField", "table", List("z").asJava)
|
||||||
|
field.getName shouldEqual "ArrayField"
|
||||||
|
field.getComparison.getOp shouldEqual Op.IN_ARRAY
|
||||||
|
field.getComparison.getValue.getFirst shouldEqual "table"
|
||||||
|
field.getComparison.getValue.getSecond should be (List("z").asJava)
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
it("constructs a field w/ parameter name") {
|
||||||
|
val field = Field.inArray("ArrayField", "table", List("z").asJava, ":a")
|
||||||
|
field.getName shouldEqual "ArrayField"
|
||||||
|
field.getComparison.getOp shouldEqual Op.IN_ARRAY
|
||||||
|
field.getComparison.getValue.getFirst shouldEqual "table"
|
||||||
|
field.getComparison.getValue.getSecond should be (List("z").asJava)
|
||||||
|
field.getParameterName shouldEqual ":a"
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("exists") {
|
||||||
|
it("constructs a field") {
|
||||||
|
val field = Field.exists("Groovy")
|
||||||
|
field.getName shouldEqual "Groovy"
|
||||||
|
field.getComparison.getOp shouldEqual Op.EXISTS
|
||||||
|
field.getComparison.getValue shouldEqual ""
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("notExists") {
|
||||||
|
it("constructs a field") {
|
||||||
|
val field = Field.notExists("Groovy")
|
||||||
|
field.getName shouldEqual "Groovy"
|
||||||
|
field.getComparison.getOp shouldEqual Op.NOT_EXISTS
|
||||||
|
field.getComparison.getValue shouldEqual ""
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("named") {
|
||||||
|
it("named constructs a field") {
|
||||||
|
val field = Field.named("Tacos")
|
||||||
|
field.getName shouldEqual "Tacos"
|
||||||
|
field.getComparison.getOp shouldEqual Op.EQUAL
|
||||||
|
field.getComparison.getValue shouldEqual ""
|
||||||
|
field.getParameterName should be (null)
|
||||||
|
field.getQualifier should be (null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("static constructors") {
|
||||||
|
it("fail for invalid parameter name") {
|
||||||
|
an [DocumentException] should be thrownBy Field.equal("a", "b", "that ain't it, Jack...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("nameToPath") {
|
||||||
|
it("creates a simple PostgreSQL SQL name") {
|
||||||
|
Field.nameToPath("Simple", Dialect.POSTGRESQL, FieldFormat.SQL) shouldEqual "data->>'Simple'"
|
||||||
|
}
|
||||||
|
it("creates a simple SQLite SQL name") {
|
||||||
|
Field.nameToPath("Simple", Dialect.SQLITE, FieldFormat.SQL) shouldEqual "data->>'Simple'"
|
||||||
|
}
|
||||||
|
it("creates a nested PostgreSQL SQL name") {
|
||||||
|
(Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.SQL)
|
||||||
|
shouldEqual "data#>>'{A,Long,Path,to,the,Property}'")
|
||||||
|
}
|
||||||
|
it("creates a nested SQLite SQL name") {
|
||||||
|
(Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.SQL)
|
||||||
|
shouldEqual "data->'A'->'Long'->'Path'->'to'->'the'->>'Property'")
|
||||||
|
}
|
||||||
|
it("creates a simple PostgreSQL JSON name") {
|
||||||
|
Field.nameToPath("Simple", Dialect.POSTGRESQL, FieldFormat.JSON) shouldEqual "data->'Simple'"
|
||||||
|
}
|
||||||
|
it("creates a simple SQLite JSON name") {
|
||||||
|
Field.nameToPath("Simple", Dialect.SQLITE, FieldFormat.JSON) shouldEqual "data->'Simple'"
|
||||||
|
}
|
||||||
|
it("creates a nested PostgreSQL JSON name") {
|
||||||
|
(Field.nameToPath("A.Long.Path.to.the.Property", Dialect.POSTGRESQL, FieldFormat.JSON)
|
||||||
|
shouldEqual "data#>'{A,Long,Path,to,the,Property}'")
|
||||||
|
}
|
||||||
|
it("creates a nested SQLite JSON name") {
|
||||||
|
(Field.nameToPath("A.Long.Path.to.the.Property", Dialect.SQLITE, FieldFormat.JSON)
|
||||||
|
shouldEqual "data->'A'->'Long'->'Path'->'to'->'the'->'Property'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package solutions.bitbadger.documents.scala
|
||||||
|
|
||||||
|
import org.scalatest.funspec.AnyFunSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import solutions.bitbadger.documents.Op
|
||||||
|
|
||||||
|
class OpSpec extends AnyFunSpec with Matchers {
|
||||||
|
|
||||||
|
describe("sql") {
|
||||||
|
it("returns = for EQUAL") {
|
||||||
|
Op.EQUAL.getSql shouldEqual "="
|
||||||
|
}
|
||||||
|
it("returns > for GREATER") {
|
||||||
|
Op.GREATER.getSql shouldEqual ">"
|
||||||
|
}
|
||||||
|
it("returns >= for GREATER_OR_EQUAL") {
|
||||||
|
Op.GREATER_OR_EQUAL.getSql shouldEqual ">="
|
||||||
|
}
|
||||||
|
it("returns < for LESS") {
|
||||||
|
Op.LESS.getSql shouldEqual "<"
|
||||||
|
}
|
||||||
|
it("returns <= for LESS_OR_EQUAL") {
|
||||||
|
Op.LESS_OR_EQUAL.getSql shouldEqual "<="
|
||||||
|
}
|
||||||
|
it("returns <> for NOT_EQUAL") {
|
||||||
|
Op.NOT_EQUAL.getSql shouldEqual "<>"
|
||||||
|
}
|
||||||
|
it("returns BETWEEN for BETWEEN") {
|
||||||
|
Op.BETWEEN.getSql shouldEqual "BETWEEN"
|
||||||
|
}
|
||||||
|
it("returns IN for IN") {
|
||||||
|
Op.IN.getSql shouldEqual "IN"
|
||||||
|
}
|
||||||
|
it("returns ??| for IN_ARRAY") {
|
||||||
|
Op.IN_ARRAY.getSql shouldEqual "??|"
|
||||||
|
}
|
||||||
|
it("returns IS NOT NULL for EXISTS") {
|
||||||
|
Op.EXISTS.getSql shouldEqual "IS NOT NULL"
|
||||||
|
}
|
||||||
|
it("returns IS NULL for NOT_EXISTS") {
|
||||||
|
Op.NOT_EXISTS.getSql shouldEqual "IS NULL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package solutions.bitbadger.documents.scala
|
||||||
|
|
||||||
|
import org.scalatest.funspec.AnyFunSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import solutions.bitbadger.documents.ParameterName
|
||||||
|
|
||||||
|
class ParameterNameSpec extends AnyFunSpec with Matchers {
|
||||||
|
|
||||||
|
describe("derive") {
|
||||||
|
it("works when given existing names") {
|
||||||
|
val names = new ParameterName()
|
||||||
|
names.derive(":taco") shouldEqual ":taco"
|
||||||
|
names.derive(null) shouldEqual ":field0"
|
||||||
|
}
|
||||||
|
it("works when given all anonymous fields") {
|
||||||
|
val names = new ParameterName()
|
||||||
|
names.derive(null) shouldEqual ":field0"
|
||||||
|
names.derive(null) shouldEqual ":field1"
|
||||||
|
names.derive(null) shouldEqual ":field2"
|
||||||
|
names.derive(null) shouldEqual ":field3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package solutions.bitbadger.documents.scala
|
||||||
|
|
||||||
|
import org.scalatest.funspec.AnyFunSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import solutions.bitbadger.documents.{DocumentException, Parameter, ParameterType}
|
||||||
|
|
||||||
|
class ParameterSpec extends AnyFunSpec with Matchers {
|
||||||
|
|
||||||
|
describe("constructor") {
|
||||||
|
it("succeeds with colon-prefixed name") {
|
||||||
|
val p = new Parameter(":test", ParameterType.STRING, "ABC")
|
||||||
|
p.getName shouldEqual ":test"
|
||||||
|
p.getType shouldEqual ParameterType.STRING
|
||||||
|
p.getValue shouldEqual "ABC"
|
||||||
|
}
|
||||||
|
it("succeeds with at-sign-prefixed name") {
|
||||||
|
val p = Parameter("@yo", ParameterType.NUMBER, null)
|
||||||
|
p.getName shouldEqual "@yo"
|
||||||
|
p.getType shouldEqual ParameterType.NUMBER
|
||||||
|
p.getValue should be (null)
|
||||||
|
}
|
||||||
|
it("fails with incorrect prefix") {
|
||||||
|
an [DocumentException] should be thrownBy Parameter("it", ParameterType.JSON, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user