From 66d432c723a84da1c9772eab05a085234239a688 Mon Sep 17 00:00:00 2001 From: Derek Ellis Date: Fri, 12 Aug 2022 13:29:04 -0400 Subject: [PATCH] Add better version catalog support for dialects (#3435) * Add better version catalog support for dialects * Use better api to add dialect dependency Add integration test for version catalogs * Move catalog test into dockerTest * Spotless apply * Correctly resolve dialect module in test --- .../dialect/DialectIntegrationTests.kt | 9 ++ .../sqldelight/gradle/SqlDelightDatabase.kt | 2 +- .../src/test/integration-catalog/build.gradle | 27 ++++ .../test/integration-catalog/settings.gradle | 17 +++ .../sqldelight/mysql/integration/Dates.sq | 13 ++ .../cash/sqldelight/mysql/integration/Dog.sq | 19 +++ .../sqldelight/mysql/integration/MySqlTest.kt | 128 ++++++++++++++++++ 7 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 sqldelight-gradle-plugin/src/test/integration-catalog/build.gradle create mode 100644 sqldelight-gradle-plugin/src/test/integration-catalog/settings.gradle create mode 100644 sqldelight-gradle-plugin/src/test/integration-catalog/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dates.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-catalog/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dog.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-catalog/src/test/kotlin/app/cash/sqldelight/mysql/integration/MySqlTest.kt diff --git a/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt b/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt index c8288e3eb56..0ea5af6bffa 100644 --- a/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt +++ b/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt @@ -33,6 +33,15 @@ class DialectIntegrationTests { val result = runner.build() Truth.assertThat(result.output).contains("BUILD SUCCESSFUL") } + + @Test fun `dialect accepts version catalog dependency`() { + val runner = GradleRunner.create() + .withCommonConfiguration(File("src/test/integration-catalog")) + .withArguments("clean", "check", "--stacktrace") + + val result = runner.build() + Truth.assertThat(result.output).contains("BUILD SUCCESSFUL") + } } private fun GradleRunner.withCommonConfiguration(projectRoot: File): GradleRunner { diff --git a/sqldelight-gradle-plugin/src/main/kotlin/app/cash/sqldelight/gradle/SqlDelightDatabase.kt b/sqldelight-gradle-plugin/src/main/kotlin/app/cash/sqldelight/gradle/SqlDelightDatabase.kt index 913b924c1b4..0c234c45420 100644 --- a/sqldelight-gradle-plugin/src/main/kotlin/app/cash/sqldelight/gradle/SqlDelightDatabase.kt +++ b/sqldelight-gradle-plugin/src/main/kotlin/app/cash/sqldelight/gradle/SqlDelightDatabase.kt @@ -41,7 +41,7 @@ class SqlDelightDatabase( fun dialect(dialect: Any) { if (addedDialect) throw IllegalStateException("Can only set a single dialect.") - configuration.dependencies.add(project.dependencies.create(dialect)) + project.dependencies.add(configuration.name, dialect) addedDialect = true } diff --git a/sqldelight-gradle-plugin/src/test/integration-catalog/build.gradle b/sqldelight-gradle-plugin/src/test/integration-catalog/build.gradle new file mode 100644 index 00000000000..beddafaaba3 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-catalog/build.gradle @@ -0,0 +1,27 @@ +buildscript { + apply from: "${projectDir.absolutePath}/../buildscript.gradle" +} + +apply plugin: 'org.jetbrains.kotlin.jvm' +apply plugin: 'app.cash.sqldelight' + +sqldelight { + MyDatabase { + packageName = "app.cash.sqldelight.mysql.integration" + dialect(libs.sqldelight.mysql) + } +} + +repositories { + maven { + url "file://${projectDir.absolutePath}/../../../../build/localMaven" + } + mavenCentral() +} + +dependencies { + implementation deps.mysqlJdbc + implementation "org.testcontainers:mysql:1.16.2" + implementation "app.cash.sqldelight:jdbc-driver:${app.cash.sqldelight.VersionKt.VERSION}" + implementation deps.truth +} diff --git a/sqldelight-gradle-plugin/src/test/integration-catalog/settings.gradle b/sqldelight-gradle-plugin/src/test/integration-catalog/settings.gradle new file mode 100644 index 00000000000..2505a970688 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-catalog/settings.gradle @@ -0,0 +1,17 @@ +apply from: "../settings.gradle" + +rootProject.name = 'sqldelight-mysql-integration' + +dependencyResolutionManagement { + versionCatalogs { + libs { + library('sqldelight-mysql', 'app.cash.sqldelight', 'mysql-dialect').withoutVersion() + } + } +} + +includeBuild("../../../../") { + dependencySubstitution { + substitute module("app.cash.sqldelight:mysql-dialect") using project(":dialects:mysql") + } +} diff --git a/sqldelight-gradle-plugin/src/test/integration-catalog/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dates.sq b/sqldelight-gradle-plugin/src/test/integration-catalog/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dates.sq new file mode 100644 index 00000000000..50eb8e06d5b --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-catalog/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dates.sq @@ -0,0 +1,13 @@ +CREATE TABLE dates( + time TIME NOT NULL, + date DATE NOT NULL, + datetime DATETIME NOT NULL, + timestamp TIMESTAMP NOT NULL, + year YEAR NOT NULL +); + +insertDate { +INSERT INTO dates VALUES (?, ?, ?, ?, ?); + +SELECT * FROM dates LIMIT 1; +} diff --git a/sqldelight-gradle-plugin/src/test/integration-catalog/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-catalog/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dog.sq new file mode 100644 index 00000000000..977e509000b --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-catalog/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dog.sq @@ -0,0 +1,19 @@ +CREATE TABLE dog ( + name VARCHAR(8) NOT NULL, + breed TEXT NOT NULL, + is_good BOOLEAN NOT NULL DEFAULT 1 +); + +insertDog: +INSERT INTO dog +VALUES (?, ?, ?); + +selectDogs: +SELECT * +FROM dog; + +selectDogsByBreedAndNames: +SELECT * +FROM dog +WHERE breed = ? + AND name IN ?; diff --git a/sqldelight-gradle-plugin/src/test/integration-catalog/src/test/kotlin/app/cash/sqldelight/mysql/integration/MySqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-catalog/src/test/kotlin/app/cash/sqldelight/mysql/integration/MySqlTest.kt new file mode 100644 index 00000000000..f6963f4f1dd --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-catalog/src/test/kotlin/app/cash/sqldelight/mysql/integration/MySqlTest.kt @@ -0,0 +1,128 @@ +package app.cash.sqldelight.mysql.integration + +import app.cash.sqldelight.Query +import app.cash.sqldelight.TransacterImpl +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.jdbc.JdbcDriver +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import java.sql.Connection +import java.sql.DriverManager +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter + +class MySqlTest { + lateinit var connection: Connection + lateinit var dogQueries: DogQueries + lateinit var datesQueries: DatesQueries + lateinit var driver: JdbcDriver + + @Before + fun before() { + connection = DriverManager.getConnection("jdbc:tc:mysql:///myDb") + driver = object : JdbcDriver() { + override fun getConnection() = connection + override fun closeConnection(connection: Connection) = Unit + override fun addListener(listener: Query.Listener, queryKeys: Array) = Unit + override fun removeListener(listener: Query.Listener, queryKeys: Array) = Unit + override fun notifyListeners(queryKeys: Array) = Unit + } + val database = MyDatabase(driver) + + MyDatabase.Schema.create(driver) + dogQueries = database.dogQueries + datesQueries = database.datesQueries + } + + @After + fun after() { + connection.close() + } + + @Test fun simpleSelect() { + dogQueries.insertDog("Tilda", "Pomeranian", true) + assertThat(dogQueries.selectDogs().executeAsOne()) + .isEqualTo( + Dog( + name = "Tilda", + breed = "Pomeranian", + is_good = true, + ), + ) + } + + @Test + fun simpleSelectWithIn() { + dogQueries.insertDog("Tilda", "Pomeranian", true) + dogQueries.insertDog("Tucker", "Portuguese Water Dog", true) + dogQueries.insertDog("Cujo", "Pomeranian", false) + dogQueries.insertDog("Buddy", "Pomeranian", true) + assertThat( + dogQueries.selectDogsByBreedAndNames( + breed = "Pomeranian", + name = listOf("Tilda", "Buddy"), + ).executeAsList(), + ) + .containsExactly( + Dog( + name = "Tilda", + breed = "Pomeranian", + is_good = true, + ), + Dog( + name = "Buddy", + breed = "Pomeranian", + is_good = true, + ), + ) + } + + @Test + fun testDates() { + with( + datesQueries.insertDate( + date = LocalDate.of(2020, 1, 1), + time = LocalTime.of(21, 30, 59), + datetime = LocalDateTime.of(2020, 1, 1, 21, 30, 59), + timestamp = OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 0, ZoneOffset.ofHours(0)), + year = "2022", + ).executeAsOne(), + ) { + assertThat(date).isEqualTo(LocalDate.of(2020, 1, 1)) + assertThat(time).isEqualTo(LocalTime.of(21, 30, 59)) + assertThat(datetime).isEqualTo(LocalDateTime.of(2020, 1, 1, 21, 30, 59)) + + assertThat(timestamp.format(DateTimeFormatter.ISO_LOCAL_DATE)) + .isEqualTo(OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 0, ZoneOffset.ofHours(0)).format(DateTimeFormatter.ISO_LOCAL_DATE)) + assertThat(year).isEqualTo("2022-01-01") + } + } + + @Test + fun transactionCrashRollsBack() { + val transacter = SqlDriverTransacter(driver) + + try { + transacter.transaction { + driver.execute(null, "CREATE TABLE throw_test(some Text)", 0, null) + afterRollback { driver.execute(null, "DROP TABLE throw_test", 0, null) } + throw ExpectedException() + } + Assert.fail() + } catch (_: ExpectedException) { + transacter.transaction { + driver.execute(null, "CREATE TABLE throw_test(some Text)", 0, null) + } + } + } + + private class ExpectedException : Exception() + private class SqlDriverTransacter(driver: SqlDriver) : TransacterImpl(driver) +}