From eda312fe3bac573954da21d822d919572bf4c1a8 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sat, 15 Mar 2025 14:09:05 -0400 Subject: [PATCH] WIP on Java query tests --- .../documents/java/query/CountQueryTest.java | 87 ++++++++++++ .../java/query/DefinitionQueryTest.java | 133 ++++++++++++++++++ .../src/test/kotlin/query/CountQueryTest.kt | 53 ++++--- .../test/kotlin/query/DefinitionQueryTest.kt | 66 +++++---- .../src/test/kotlin/support/ForceDialect.kt | 24 ++++ 5 files changed, 304 insertions(+), 59 deletions(-) create mode 100644 src/jvm/src/test/java/solutions/bitbadger/documents/java/query/CountQueryTest.java create mode 100644 src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DefinitionQueryTest.java create mode 100644 src/jvm/src/test/kotlin/support/ForceDialect.kt diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/CountQueryTest.java b/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/CountQueryTest.java new file mode 100644 index 0000000..1389ba9 --- /dev/null +++ b/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/CountQueryTest.java @@ -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)); + } +} diff --git a/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DefinitionQueryTest.java b/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DefinitionQueryTest.java new file mode 100644 index 0000000..f5e0cd2 --- /dev/null +++ b/src/jvm/src/test/java/solutions/bitbadger/documents/java/query/DefinitionQueryTest.java @@ -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)); + } +} diff --git a/src/jvm/src/test/kotlin/query/CountQueryTest.kt b/src/jvm/src/test/kotlin/query/CountQueryTest.kt index 9d2de61..9c3d413 100644 --- a/src/jvm/src/test/kotlin/query/CountQueryTest.kt +++ b/src/jvm/src/test/kotlin/query/CountQueryTest.kt @@ -4,10 +4,10 @@ import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test 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.Field +import solutions.bitbadger.documents.support.ForceDialect +import solutions.bitbadger.documents.support.TEST_TABLE import kotlin.test.assertEquals /** @@ -16,75 +16,72 @@ import kotlin.test.assertEquals @DisplayName("JVM | Kotlin | Query | CountQuery") class CountQueryTest { - /** Test table name */ - private val tbl = "test_table" - /** * Clear the connection string (resets Dialect) */ @AfterEach fun cleanUp() { - Configuration.dialectValue = null + ForceDialect.none() } @Test @DisplayName("all generates correctly") 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 - @DisplayName("byFields generates correctly (PostgreSQL)") + @DisplayName("byFields generates correctly | PostgreSQL") fun byFieldsPostgres() { - Configuration.dialectValue = Dialect.POSTGRESQL + ForceDialect.postgres() assertEquals( - "SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0", - CountQuery.byFields(tbl, listOf(Field.equal("test", "", ":field0"))), + "SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0", + CountQuery.byFields(TEST_TABLE, listOf(Field.equal("test", "", ":field0"))), "Count query not constructed correctly" ) } @Test - @DisplayName("byFields generates correctly (PostgreSQL)") + @DisplayName("byFields generates correctly | SQLite") fun byFieldsSQLite() { - Configuration.dialectValue = Dialect.SQLITE + ForceDialect.sqlite() assertEquals( - "SELECT COUNT(*) AS it FROM $tbl WHERE data->>'test' = :field0", - CountQuery.byFields(tbl, listOf(Field.equal("test", "", ":field0"))), + "SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE data->>'test' = :field0", + CountQuery.byFields(TEST_TABLE, listOf(Field.equal("test", "", ":field0"))), "Count query not constructed correctly" ) } @Test - @DisplayName("byContains generates correctly (PostgreSQL)") + @DisplayName("byContains generates correctly | PostgreSQL") fun byContainsPostgres() { - Configuration.dialectValue = Dialect.POSTGRESQL + ForceDialect.postgres() 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" ) } @Test - @DisplayName("byContains fails (SQLite)") + @DisplayName("byContains fails | SQLite") fun byContainsSQLite() { - Configuration.dialectValue = Dialect.SQLITE - assertThrows { CountQuery.byContains(tbl) } + ForceDialect.sqlite() + assertThrows { CountQuery.byContains(TEST_TABLE) } } @Test - @DisplayName("byJsonPath generates correctly (PostgreSQL)") + @DisplayName("byJsonPath generates correctly | PostgreSQL") fun byJsonPathPostgres() { - Configuration.dialectValue = Dialect.POSTGRESQL + ForceDialect.postgres() assertEquals( - "SELECT COUNT(*) AS it FROM $tbl WHERE jsonb_path_exists(data, :path::jsonpath)", - CountQuery.byJsonPath(tbl), "Count query not constructed correctly" + "SELECT COUNT(*) AS it FROM $TEST_TABLE WHERE jsonb_path_exists(data, :path::jsonpath)", + CountQuery.byJsonPath(TEST_TABLE), "Count query not constructed correctly" ) } @Test - @DisplayName("byJsonPath fails (SQLite)") + @DisplayName("byJsonPath fails | SQLite") fun byJsonPathSQLite() { - Configuration.dialectValue = Dialect.SQLITE - assertThrows { CountQuery.byJsonPath(tbl) } + ForceDialect.sqlite() + assertThrows { CountQuery.byJsonPath(TEST_TABLE) } } } diff --git a/src/jvm/src/test/kotlin/query/DefinitionQueryTest.kt b/src/jvm/src/test/kotlin/query/DefinitionQueryTest.kt index 71d1fef..a0ccf41 100644 --- a/src/jvm/src/test/kotlin/query/DefinitionQueryTest.kt +++ b/src/jvm/src/test/kotlin/query/DefinitionQueryTest.kt @@ -4,10 +4,11 @@ import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test 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.DocumentIndex +import solutions.bitbadger.documents.support.ForceDialect +import solutions.bitbadger.documents.support.TEST_TABLE import kotlin.test.assertEquals /** @@ -16,15 +17,12 @@ import kotlin.test.assertEquals @DisplayName("JVM | Kotlin | Query | DefinitionQuery") class DefinitionQueryTest { - /** Test table name */ - private val tbl = "test_table" - /** * Clear the connection string (resets Dialect) */ @AfterEach fun cleanUp() { - Configuration.dialectValue = null + ForceDialect.none() } @Test @@ -36,23 +34,29 @@ class DefinitionQueryTest { ) @Test - @DisplayName("ensureTable generates correctly (PostgreSQL)") + @DisplayName("ensureTable generates correctly | PostgreSQL") fun ensureTablePostgres() { - Configuration.dialectValue = Dialect.POSTGRESQL - assertEquals("CREATE TABLE IF NOT EXISTS $tbl (data JSONB NOT NULL)", DefinitionQuery.ensureTable(tbl)) + ForceDialect.postgres() + assertEquals( + "CREATE TABLE IF NOT EXISTS $TEST_TABLE (data JSONB NOT NULL)", + DefinitionQuery.ensureTable(TEST_TABLE) + ) } @Test - @DisplayName("ensureTable generates correctly (SQLite)") + @DisplayName("ensureTable generates correctly | SQLite") fun ensureTableSQLite() { - Configuration.dialectValue = Dialect.SQLITE - assertEquals("CREATE TABLE IF NOT EXISTS $tbl (data TEXT NOT NULL)", DefinitionQuery.ensureTable(tbl)) + ForceDialect.sqlite() + assertEquals( + "CREATE TABLE IF NOT EXISTS $TEST_TABLE (data TEXT NOT NULL)", + DefinitionQuery.ensureTable(TEST_TABLE) + ) } @Test @DisplayName("ensureTable fails when no dialect is set") fun ensureTableFailsUnknown() { - assertThrows { DefinitionQuery.ensureTable(tbl) } + assertThrows { DefinitionQuery.ensureTable(TEST_TABLE) } } @Test @@ -68,8 +72,8 @@ class DefinitionQueryTest { @DisplayName("ensureKey generates correctly without schema") fun ensureKeyWithoutSchema() = assertEquals( - "CREATE UNIQUE INDEX IF NOT EXISTS idx_${tbl}_key ON $tbl ((data->>'id'))", - DefinitionQuery.ensureKey(tbl, Dialect.SQLITE), + "CREATE UNIQUE INDEX IF NOT EXISTS idx_${TEST_TABLE}_key ON $TEST_TABLE ((data->>'id'))", + DefinitionQuery.ensureKey(TEST_TABLE, Dialect.SQLITE), "CREATE INDEX for key statement without schema not constructed correctly" ) @@ -86,49 +90,49 @@ class DefinitionQueryTest { ) @Test - @DisplayName("ensureIndexOn generates nested PostgreSQL field") + @DisplayName("ensureIndexOn generates nested field | PostgreSQL") fun ensureIndexOnNestedPostgres() = assertEquals( - "CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data#>>'{a,b,c}'))", - DefinitionQuery.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.POSTGRESQL), + "CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data#>>'{a,b,c}'))", + DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", listOf("a.b.c"), Dialect.POSTGRESQL), "CREATE INDEX for nested PostgreSQL field incorrect" ) @Test - @DisplayName("ensureIndexOn generates nested SQLite field") + @DisplayName("ensureIndexOn generates nested field | SQLite") fun ensureIndexOnNestedSQLite() = assertEquals( - "CREATE INDEX IF NOT EXISTS idx_${tbl}_nest ON $tbl ((data->'a'->'b'->>'c'))", - DefinitionQuery.ensureIndexOn(tbl, "nest", listOf("a.b.c"), Dialect.SQLITE), + "CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_nest ON $TEST_TABLE ((data->'a'->'b'->>'c'))", + DefinitionQuery.ensureIndexOn(TEST_TABLE, "nest", listOf("a.b.c"), Dialect.SQLITE), "CREATE INDEX for nested SQLite field incorrect" ) @Test - @DisplayName("ensureDocumentIndexOn generates Full for PostgreSQL") + @DisplayName("ensureDocumentIndexOn generates Full | PostgreSQL") fun ensureDocumentIndexOnFullPostgres() { - Configuration.dialectValue = Dialect.POSTGRESQL + ForceDialect.postgres() assertEquals( - "CREATE INDEX IF NOT EXISTS idx_${tbl}_document ON $tbl USING GIN (data)", - DefinitionQuery.ensureDocumentIndexOn(tbl, DocumentIndex.FULL), + "CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data)", + DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL), "CREATE INDEX for full document index incorrect" ) } @Test - @DisplayName("ensureDocumentIndexOn generates Optimized for PostgreSQL") + @DisplayName("ensureDocumentIndexOn generates Optimized | PostgreSQL") fun ensureDocumentIndexOnOptimizedPostgres() { - Configuration.dialectValue = Dialect.POSTGRESQL + ForceDialect.postgres() assertEquals( - "CREATE INDEX IF NOT EXISTS idx_${tbl}_document ON $tbl USING GIN (data jsonb_path_ops)", - DefinitionQuery.ensureDocumentIndexOn(tbl, DocumentIndex.OPTIMIZED), + "CREATE INDEX IF NOT EXISTS idx_${TEST_TABLE}_document ON $TEST_TABLE USING GIN (data jsonb_path_ops)", + DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.OPTIMIZED), "CREATE INDEX for optimized document index incorrect" ) } @Test - @DisplayName("ensureDocumentIndexOn fails for SQLite") + @DisplayName("ensureDocumentIndexOn fails | SQLite") fun ensureDocumentIndexOnFailsSQLite() { - Configuration.dialectValue = Dialect.SQLITE - assertThrows { DefinitionQuery.ensureDocumentIndexOn(tbl, DocumentIndex.FULL) } + ForceDialect.sqlite() + assertThrows { DefinitionQuery.ensureDocumentIndexOn(TEST_TABLE, DocumentIndex.FULL) } } } diff --git a/src/jvm/src/test/kotlin/support/ForceDialect.kt b/src/jvm/src/test/kotlin/support/ForceDialect.kt new file mode 100644 index 0000000..681361c --- /dev/null +++ b/src/jvm/src/test/kotlin/support/ForceDialect.kt @@ -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 + } +} \ No newline at end of file