Initial Development #1

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

View File

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

View File

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

View File

@ -4,10 +4,10 @@ import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.Field import solutions.bitbadger.documents.Field
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
@ -16,75 +16,72 @@ import kotlin.test.assertEquals
@DisplayName("JVM | Kotlin | Query | CountQuery") @DisplayName("JVM | Kotlin | Query | CountQuery")
class CountQueryTest { class CountQueryTest {
/** Test table name */
private val tbl = "test_table"
/** /**
* Clear the connection string (resets Dialect) * Clear the connection string (resets Dialect)
*/ */
@AfterEach @AfterEach
fun cleanUp() { fun cleanUp() {
Configuration.dialectValue = null ForceDialect.none()
} }
@Test @Test
@DisplayName("all generates correctly") @DisplayName("all generates correctly")
fun all() = fun all() =
assertEquals("SELECT COUNT(*) AS it FROM $tbl", CountQuery.all(tbl), "Count query not constructed correctly") assertEquals("SELECT COUNT(*) AS it FROM $TEST_TABLE", CountQuery.all(TEST_TABLE), "Count query not constructed correctly")
@Test @Test
@DisplayName("byFields generates correctly (PostgreSQL)") @DisplayName("byFields generates correctly | PostgreSQL")
fun byFieldsPostgres() { fun byFieldsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0", "SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0",
CountQuery.byFields(tbl, listOf(Field.equal("test", "", ":field0"))), CountQuery.byFields(TEST_TABLE, listOf(Field.equal("test", "", ":field0"))),
"Count query not constructed correctly" "Count query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byFields generates correctly (PostgreSQL)") @DisplayName("byFields generates correctly | SQLite")
fun byFieldsSQLite() { fun byFieldsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals( assertEquals(
"SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0", "SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0",
CountQuery.byFields(tbl, listOf(Field.equal("test", "", ":field0"))), CountQuery.byFields(TEST_TABLE, listOf(Field.equal("test", "", ":field0"))),
"Count query not constructed correctly" "Count query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byContains generates correctly (PostgreSQL)") @DisplayName("byContains generates correctly | PostgreSQL")
fun byContainsPostgres() { fun byContainsPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT COUNT(*) AS it FROM $tbl WHERE data @> :criteria", CountQuery.byContains(tbl), "SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data @> :criteria", CountQuery.byContains(TEST_TABLE),
"Count query not constructed correctly" "Count query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byContains fails (SQLite)") @DisplayName("byContains fails | SQLite")
fun byContainsSQLite() { fun byContainsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { CountQuery.byContains(tbl) } assertThrows<DocumentException> { CountQuery.byContains(TEST_TABLE) }
} }
@Test @Test
@DisplayName("byJsonPath generates correctly (PostgreSQL)") @DisplayName("byJsonPath generates correctly | PostgreSQL")
fun byJsonPathPostgres() { fun byJsonPathPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"SELECT COUNT(*) AS it FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)", "SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)",
CountQuery.byJsonPath(tbl), "Count query not constructed correctly" CountQuery.byJsonPath(TEST_TABLE), "Count query not constructed correctly"
) )
} }
@Test @Test
@DisplayName("byJsonPath fails (SQLite)") @DisplayName("byJsonPath fails | SQLite")
fun byJsonPathSQLite() { fun byJsonPathSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { CountQuery.byJsonPath(tbl) } assertThrows<DocumentException> { CountQuery.byJsonPath(TEST_TABLE) }
} }
} }

View File

@ -4,10 +4,11 @@ import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import solutions.bitbadger.documents.Configuration
import solutions.bitbadger.documents.Dialect import solutions.bitbadger.documents.Dialect
import solutions.bitbadger.documents.DocumentException import solutions.bitbadger.documents.DocumentException
import solutions.bitbadger.documents.DocumentIndex import solutions.bitbadger.documents.DocumentIndex
import solutions.bitbadger.documents.support.ForceDialect
import solutions.bitbadger.documents.support.TEST_TABLE
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
@ -16,15 +17,12 @@ import kotlin.test.assertEquals
@DisplayName("JVM | Kotlin | Query | DefinitionQuery") @DisplayName("JVM | Kotlin | Query | DefinitionQuery")
class DefinitionQueryTest { class DefinitionQueryTest {
/** Test table name */
private val tbl = "test_table"
/** /**
* Clear the connection string (resets Dialect) * Clear the connection string (resets Dialect)
*/ */
@AfterEach @AfterEach
fun cleanUp() { fun cleanUp() {
Configuration.dialectValue = null ForceDialect.none()
} }
@Test @Test
@ -36,23 +34,29 @@ class DefinitionQueryTest {
) )
@Test @Test
@DisplayName("ensureTable generates correctly (PostgreSQL)") @DisplayName("ensureTable generates correctly | PostgreSQL")
fun ensureTablePostgres() { fun ensureTablePostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals("CREATE TABLE IF NOT EXISTS $tbl (data JSONB NOT NULL)", DefinitionQuery.ensureTable(tbl)) assertEquals(
"CREATE TABLE IF NOT EXISTS $TEST_TABLE (data JSONB NOT NULL)",
DefinitionQuery.ensureTable(TEST_TABLE)
)
} }
@Test @Test
@DisplayName("ensureTable generates correctly (SQLite)") @DisplayName("ensureTable generates correctly | SQLite")
fun ensureTableSQLite() { fun ensureTableSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertEquals("CREATE TABLE IF NOT EXISTS $tbl (data TEXT NOT NULL)", DefinitionQuery.ensureTable(tbl)) assertEquals(
"CREATE TABLE IF NOT EXISTS $TEST_TABLE (data TEXT NOT NULL)",
DefinitionQuery.ensureTable(TEST_TABLE)
)
} }
@Test @Test
@DisplayName("ensureTable fails when no dialect is set") @DisplayName("ensureTable fails when no dialect is set")
fun ensureTableFailsUnknown() { fun ensureTableFailsUnknown() {
assertThrows<DocumentException> { DefinitionQuery.ensureTable(tbl) } assertThrows<DocumentException> { DefinitionQuery.ensureTable(TEST_TABLE) }
} }
@Test @Test
@ -68,8 +72,8 @@ class DefinitionQueryTest {
@DisplayName("ensureKey generates correctly without schema") @DisplayName("ensureKey generates correctly without schema")
fun ensureKeyWithoutSchema() = fun ensureKeyWithoutSchema() =
assertEquals( assertEquals(
"CREATE UNIQUE INDEX IF NOT EXISTS idx_${tbl}_key ON $tbl ((data->>'id'))", "CREATE UNIQUE INDEX IF NOT EXISTS idx_${TEST_TABLE}_key ON $TEST_TABLE ((data->>'id'))",
DefinitionQuery.ensureKey(tbl, Dialect.SQLITE), DefinitionQuery.ensureKey(TEST_TABLE, Dialect.SQLITE),
"CREATE INDEX for key statement without schema not constructed correctly" "CREATE INDEX for key statement without schema not constructed correctly"
) )
@ -86,49 +90,49 @@ class DefinitionQueryTest {
) )
@Test @Test
@DisplayName("ensureIndexOn generates nested PostgreSQL field") @DisplayName("ensureIndexOn generates nested field | PostgreSQL")
fun ensureIndexOnNestedPostgres() = fun ensureIndexOnNestedPostgres() =
assertEquals( assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data#>>'{a,b,c}'))", "CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data#>>'{a,b,c}'))",
DefinitionQuery.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.POSTGRESQL), DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", listOf("a.b.c"), Dialect.POSTGRESQL),
"CREATE INDEX for nested PostgreSQL field incorrect" "CREATE INDEX for nested PostgreSQL field incorrect"
) )
@Test @Test
@DisplayName("ensureIndexOn generates nested SQLite field") @DisplayName("ensureIndexOn generates nested field | SQLite")
fun ensureIndexOnNestedSQLite() = fun ensureIndexOnNestedSQLite() =
assertEquals( assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data->'a'->'b'->>'c'))", "CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data->'a'->'b'->>'c'))",
DefinitionQuery.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.SQLITE), DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", listOf("a.b.c"), Dialect.SQLITE),
"CREATE INDEX for nested SQLite field incorrect" "CREATE INDEX for nested SQLite field incorrect"
) )
@Test @Test
@DisplayName("ensureDocumentIndexOn generates Full for PostgreSQL") @DisplayName("ensureDocumentIndexOn generates Full | PostgreSQL")
fun ensureDocumentIndexOnFullPostgres() { fun ensureDocumentIndexOnFullPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${tbl}_document ON $tbl USING GIN (data)", "CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data)",
DefinitionQuery.ensureDocumentIndexOn(tbl, DocumentIndex.FULL), DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL),
"CREATE INDEX for full document index incorrect" "CREATE INDEX for full document index incorrect"
) )
} }
@Test @Test
@DisplayName("ensureDocumentIndexOn generates Optimized for PostgreSQL") @DisplayName("ensureDocumentIndexOn generates Optimized | PostgreSQL")
fun ensureDocumentIndexOnOptimizedPostgres() { fun ensureDocumentIndexOnOptimizedPostgres() {
Configuration.dialectValue = Dialect.POSTGRESQL ForceDialect.postgres()
assertEquals( assertEquals(
"CREATE INDEX IF NOT EXISTS idx_${tbl}_document ON $tbl USING GIN (data jsonb_path_ops)", "CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data jsonb_path_ops)",
DefinitionQuery.ensureDocumentIndexOn(tbl, DocumentIndex.OPTIMIZED), DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.OPTIMIZED),
"CREATE INDEX for optimized document index incorrect" "CREATE INDEX for optimized document index incorrect"
) )
} }
@Test @Test
@DisplayName("ensureDocumentIndexOn fails for SQLite") @DisplayName("ensureDocumentIndexOn fails | SQLite")
fun ensureDocumentIndexOnFailsSQLite() { fun ensureDocumentIndexOnFailsSQLite() {
Configuration.dialectValue = Dialect.SQLITE ForceDialect.sqlite()
assertThrows<DocumentException> { DefinitionQuery.ensureDocumentIndexOn(tbl, DocumentIndex.FULL) } assertThrows<DocumentException> { DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL) }
} }
} }

View File

@ -0,0 +1,24 @@
package solutions.bitbadger.documents.support
import solutions.bitbadger.documents.Configuration
/**
* These functions use a dummy connection string to force the given dialect for a given test
*/
object ForceDialect {
@JvmStatic
fun postgres() {
Configuration.connectionString = ":postgresql:"
}
@JvmStatic
fun sqlite() {
Configuration.connectionString = ":sqlite:"
}
@JvmStatic
fun none() {
Configuration.connectionString = null
}
}