diff --git a/build.sbt b/build.sbt index 9fd8a4c12..b89b9e329 100644 --- a/build.sbt +++ b/build.sbt @@ -26,7 +26,8 @@ addCommandAlias("check", "all scalafmtSbtCheck scalafmtCheck test:scalafmtCheck" val zioVersion = "2.0.0-RC6" val zioSchemaVersion = "0.1.9" val testcontainersVersion = "1.17.2" -val testcontainersScalaVersion = "0.40.7" +val testcontainersScalaVersion = "0.40.8" +val logbackVersion = "1.2.11" lazy val root = project .in(file(".")) @@ -143,7 +144,8 @@ lazy val mysql = project "org.testcontainers" % "jdbc" % testcontainersVersion % Test, "org.testcontainers" % "mysql" % testcontainersVersion % Test, "mysql" % "mysql-connector-java" % "8.0.29" % Test, - "com.dimafeng" %% "testcontainers-scala-mysql" % testcontainersScalaVersion % Test + "com.dimafeng" %% "testcontainers-scala-mysql" % testcontainersScalaVersion % Test, + "ch.qos.logback" % "logback-classic" % logbackVersion % Test ) ) .settings(testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")) @@ -161,7 +163,8 @@ lazy val oracle = project "org.testcontainers" % "oracle-xe" % testcontainersVersion % Test, "org.testcontainers" % "jdbc" % testcontainersVersion % Test, "com.oracle.database.jdbc" % "ojdbc8" % "21.5.0.0" % Test, - "com.dimafeng" %% "testcontainers-scala-oracle-xe" % testcontainersScalaVersion % Test + "com.dimafeng" %% "testcontainers-scala-oracle-xe" % testcontainersScalaVersion % Test, + "ch.qos.logback" % "logback-classic" % logbackVersion % Test ) ) .settings(testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")) @@ -179,7 +182,8 @@ lazy val postgres = project "org.testcontainers" % "postgresql" % testcontainersVersion % Test, "org.testcontainers" % "jdbc" % testcontainersVersion % Test, "org.postgresql" % "postgresql" % "42.3.6" % Compile, - "com.dimafeng" %% "testcontainers-scala-postgresql" % testcontainersScalaVersion % Test + "com.dimafeng" %% "testcontainers-scala-postgresql" % testcontainersScalaVersion % Test, + "ch.qos.logback" % "logback-classic" % logbackVersion % Test ) ) .settings(testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")) @@ -197,7 +201,8 @@ lazy val sqlserver = project "org.testcontainers" % "mssqlserver" % testcontainersVersion % Test, "org.testcontainers" % "jdbc" % testcontainersVersion % Test, "com.microsoft.sqlserver" % "mssql-jdbc" % "9.4.0.jre8" % Test, - "com.dimafeng" %% "testcontainers-scala-mssqlserver" % testcontainersScalaVersion % Test + "com.dimafeng" %% "testcontainers-scala-mssqlserver" % testcontainersScalaVersion % Test, + "ch.qos.logback" % "logback-classic" % logbackVersion % Test ) ) .settings(testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")) diff --git a/jdbc/src/test/scala/zio/sql/JdbcRunnableSpec.scala b/jdbc/src/test/scala/zio/sql/JdbcRunnableSpec.scala index 59116b1c4..86ccfb653 100644 --- a/jdbc/src/test/scala/zio/sql/JdbcRunnableSpec.scala +++ b/jdbc/src/test/scala/zio/sql/JdbcRunnableSpec.scala @@ -1,19 +1,68 @@ package zio.sql +import com.dimafeng.testcontainers.JdbcDatabaseContainer import zio.test.TestEnvironment -import zio.ZLayer +import zio.{ Scope, ZIO, ZLayer } import zio.test.ZIOSpecDefault +import com.dimafeng.testcontainers.SingleContainer +import java.util.Properties +import zio.test.Spec +/** + * Base trait for integration-style tests running on Testcontainers. + * Extending classes are expected to provide the container implementation + * this test suite will work on by implementing {@link getContainer}. + * + * Test suite should be implemented in {@link specLayered} and + * particular tests can depend on {@link SQLDriver} in the environment. + */ trait JdbcRunnableSpec extends ZIOSpecDefault with Jdbc { type JdbcEnvironment = TestEnvironment with SqlDriver - val poolConfigLayer: ZLayer[Any, Throwable, ConnectionPoolConfig] + def specLayered: Spec[JdbcEnvironment, Object] - final lazy val jdbcLayer: ZLayer[Any, Any, SqlDriver] = + protected def getContainer: SingleContainer[_] with JdbcDatabaseContainer + + protected val autoCommit = false + + override def spec: Spec[TestEnvironment, Any] = + specLayered.provideCustomShared(jdbcLayer) + + private[this] def connProperties(user: String, password: String): Properties = { + val props = new Properties + props.setProperty("user", user) + props.setProperty("password", password) + props + } + + private[this] val poolConfigLayer: ZLayer[Any, Throwable, ConnectionPoolConfig] = + ZLayer.scoped { + testContainer + .map(a => + ConnectionPoolConfig( + url = a.jdbcUrl, + properties = connProperties(a.username, a.password), + autoCommit = autoCommit + ) + ) + } + + private[this] final lazy val jdbcLayer: ZLayer[Any, Any, SqlDriver] = ZLayer.make[SqlDriver]( poolConfigLayer.orDie, ConnectionPool.live.orDie, SqlDriver.live ) + + private[this] def testContainer: ZIO[Scope, Throwable, SingleContainer[_] with JdbcDatabaseContainer] = + ZIO.acquireRelease { + ZIO.attemptBlocking { + val c = getContainer + c.start() + c + } + } { container => + ZIO.attemptBlocking(container.stop()).orDie + } } diff --git a/mysql/src/test/resources/logback.xml b/mysql/src/test/resources/logback.xml new file mode 100644 index 000000000..1378a823a --- /dev/null +++ b/mysql/src/test/resources/logback.xml @@ -0,0 +1,14 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + diff --git a/mysql/src/test/scala/zio/sql/mysql/MysqlRunnableSpec.scala b/mysql/src/test/scala/zio/sql/mysql/MysqlRunnableSpec.scala index 3878172b5..1a97a1a2e 100644 --- a/mysql/src/test/scala/zio/sql/mysql/MysqlRunnableSpec.scala +++ b/mysql/src/test/scala/zio/sql/mysql/MysqlRunnableSpec.scala @@ -1,29 +1,17 @@ package zio.sql.mysql -import java.util.Properties -import zio.sql.{ ConnectionPoolConfig, JdbcRunnableSpec } -import zio.test._ -import zio.ZLayer +import com.dimafeng.testcontainers.{ JdbcDatabaseContainer, MySQLContainer, SingleContainer } +import org.testcontainers.utility.DockerImageName +import zio.sql.JdbcRunnableSpec trait MysqlRunnableSpec extends JdbcRunnableSpec with MysqlJdbcModule { - private def connProperties(user: String, password: String): Properties = { - val props = new Properties - props.setProperty("user", user) - props.setProperty("password", password) - props - } - - val poolConfigLayer: ZLayer[Any, Throwable, ConnectionPoolConfig] = - ZLayer.scoped { - TestContainer - .mysql() - .map(a => ConnectionPoolConfig(a.jdbcUrl, connProperties(a.username, a.password))) + override protected def getContainer: SingleContainer[_] with JdbcDatabaseContainer = + new MySQLContainer( + mysqlImageVersion = Option("mysql").map(DockerImageName.parse) + ).configure { a => + a.withInitScript("shop_schema.sql") + () } - override def spec: Spec[TestEnvironment, Any] = - specLayered.provideCustomLayerShared(jdbcLayer) - - def specLayered: Spec[JdbcEnvironment, Object] - } diff --git a/mysql/src/test/scala/zio/sql/mysql/TestContainer.scala b/mysql/src/test/scala/zio/sql/mysql/TestContainer.scala deleted file mode 100644 index 67d778806..000000000 --- a/mysql/src/test/scala/zio/sql/mysql/TestContainer.scala +++ /dev/null @@ -1,22 +0,0 @@ -package zio.sql.mysql - -import com.dimafeng.testcontainers.MySQLContainer -import org.testcontainers.utility.DockerImageName -import zio._ - -object TestContainer { - - def mysql(imageName: String = "mysql"): ZIO[Scope, Throwable, MySQLContainer] = - ZIO.acquireRelease { - ZIO.attemptBlocking { - val c = new MySQLContainer( - mysqlImageVersion = Option(imageName).map(DockerImageName.parse) - ).configure { a => - a.withInitScript("shop_schema.sql") - () - } - c.start() - c - } - }(container => ZIO.attemptBlocking(container.stop()).orDie) -} diff --git a/oracle/src/test/resources/logback.xml b/oracle/src/test/resources/logback.xml new file mode 100644 index 000000000..1378a823a --- /dev/null +++ b/oracle/src/test/resources/logback.xml @@ -0,0 +1,14 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + diff --git a/oracle/src/test/scala/zio/sql/oracle/OracleRunnableSpec.scala b/oracle/src/test/scala/zio/sql/oracle/OracleRunnableSpec.scala index a964bf708..fcf4a2c2f 100644 --- a/oracle/src/test/scala/zio/sql/oracle/OracleRunnableSpec.scala +++ b/oracle/src/test/scala/zio/sql/oracle/OracleRunnableSpec.scala @@ -1,34 +1,17 @@ package zio.sql.oracle -import zio.ZLayer -import zio.sql.{ ConnectionPoolConfig, JdbcRunnableSpec } -import zio.test.{ Spec, TestEnvironment } - -import java.util.Properties +import com.dimafeng.testcontainers.{ JdbcDatabaseContainer, OracleContainer, SingleContainer } +import org.testcontainers.utility.DockerImageName +import zio.sql.JdbcRunnableSpec trait OracleRunnableSpec extends JdbcRunnableSpec with OracleJdbcModule { - private def connProperties(user: String, password: String): Properties = { - val props = new Properties - props.setProperty("user", user) - props.setProperty("password", password) - props - } - - val poolConfigLayer = ZLayer.scoped { - TestContainer - .oracle() - .map(container => - ConnectionPoolConfig( - url = container.jdbcUrl, - properties = connProperties(container.username, container.password) - ) - ) - } - - override def spec: Spec[TestEnvironment, Any] = - specLayered.provideCustomShared(jdbcLayer) - - def specLayered: Spec[JdbcEnvironment, Object] + override protected def getContainer: SingleContainer[_] with JdbcDatabaseContainer = + new OracleContainer( + dockerImageName = DockerImageName.parse("gvenzl/oracle-xe") + ).configure { container => + container.withInitScript("shop_schema.sql") + () + } } diff --git a/oracle/src/test/scala/zio/sql/oracle/TestContainer.scala b/oracle/src/test/scala/zio/sql/oracle/TestContainer.scala deleted file mode 100644 index 74577d433..000000000 --- a/oracle/src/test/scala/zio/sql/oracle/TestContainer.scala +++ /dev/null @@ -1,22 +0,0 @@ -package zio.sql.oracle - -import com.dimafeng.testcontainers.OracleContainer -import org.testcontainers.utility.DockerImageName -import zio.{ Scope, ZIO } - -object TestContainer { - - def oracle(imageName: String = "gvenzl/oracle-xe"): ZIO[Scope, Throwable, OracleContainer] = - ZIO.acquireRelease { - ZIO.attemptBlocking { - val c = new OracleContainer( - dockerImageName = DockerImageName.parse(imageName) - ).configure { container => - container.withInitScript("shop_schema.sql") - () - } - c.start() - c - } - }(container => ZIO.attemptBlocking(container.stop()).orDie) -} diff --git a/postgres/src/test/resources/logback.xml b/postgres/src/test/resources/logback.xml new file mode 100644 index 000000000..1378a823a --- /dev/null +++ b/postgres/src/test/resources/logback.xml @@ -0,0 +1,14 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + diff --git a/postgres/src/test/scala/zio/sql/postgresql/PostgresRunnableSpec.scala b/postgres/src/test/scala/zio/sql/postgresql/PostgresRunnableSpec.scala index f4a26efae..b5b4f7e83 100644 --- a/postgres/src/test/scala/zio/sql/postgresql/PostgresRunnableSpec.scala +++ b/postgres/src/test/scala/zio/sql/postgresql/PostgresRunnableSpec.scala @@ -1,36 +1,17 @@ package zio.sql.postgresql -import zio._ -import zio.test._ -import java.util.Properties -import zio.sql.{ ConnectionPoolConfig, JdbcRunnableSpec } +import com.dimafeng.testcontainers.{ JdbcDatabaseContainer, PostgreSQLContainer, SingleContainer } +import org.testcontainers.utility.DockerImageName +import zio.sql.JdbcRunnableSpec trait PostgresRunnableSpec extends JdbcRunnableSpec with PostgresJdbcModule { - def autoCommit: Boolean = true - - private def connProperties(user: String, password: String): Properties = { - val props = new Properties - props.setProperty("user", user) - props.setProperty("password", password) - props - } - - val poolConfigLayer = ZLayer.scoped { - TestContainer - .postgres() - .map(a => - ConnectionPoolConfig( - url = a.jdbcUrl, - properties = connProperties(a.username, a.password), - autoCommit = autoCommit - ) - ) - } - - override def spec: Spec[TestEnvironment, Any] = - specLayered.provideCustomShared(jdbcLayer) - - def specLayered: Spec[JdbcEnvironment, Object] + override protected def getContainer: SingleContainer[_] with JdbcDatabaseContainer = + new PostgreSQLContainer( + dockerImageNameOverride = Option("postgres:alpine").map(DockerImageName.parse) + ).configure { a => + a.withInitScript("db_schema.sql") + () + } } diff --git a/postgres/src/test/scala/zio/sql/postgresql/TestContainer.scala b/postgres/src/test/scala/zio/sql/postgresql/TestContainer.scala deleted file mode 100644 index 42704c031..000000000 --- a/postgres/src/test/scala/zio/sql/postgresql/TestContainer.scala +++ /dev/null @@ -1,25 +0,0 @@ -package zio.sql.postgresql - -import com.dimafeng.testcontainers.PostgreSQLContainer -import org.testcontainers.utility.DockerImageName -import zio._ - -object TestContainer { - - def postgres(imageName: String = "postgres:alpine"): ZIO[Scope, Throwable, PostgreSQLContainer] = - ZIO.acquireRelease { - ZIO.attemptBlocking { - val c = new PostgreSQLContainer( - dockerImageNameOverride = Option(imageName).map(DockerImageName.parse) - ).configure { a => - a.withInitScript("db_schema.sql") - () - } - c.start() - c - } - } { container => - ZIO.attemptBlocking(container.stop()).orDie - } - -} diff --git a/project/BuildHelper.scala b/project/BuildHelper.scala index 321d4883e..991416b57 100644 --- a/project/BuildHelper.scala +++ b/project/BuildHelper.scala @@ -162,6 +162,7 @@ object BuildHelper { compilerPlugin(("com.github.ghik" % "silencer-plugin" % SilencerVersion).cross(CrossVersion.full)) ) }, + resolvers += Resolver.sonatypeRepo("snapshots"), Test / parallelExecution := true, incOptions ~= (_.withLogRecompileOnMacro(false)), autoAPIMappings := true, diff --git a/sqlserver/src/test/resources/logback.xml b/sqlserver/src/test/resources/logback.xml new file mode 100644 index 000000000..1378a823a --- /dev/null +++ b/sqlserver/src/test/resources/logback.xml @@ -0,0 +1,14 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + diff --git a/sqlserver/src/test/scala/zio/sql/sqlserver/SqlServerRunnableSpec.scala b/sqlserver/src/test/scala/zio/sql/sqlserver/SqlServerRunnableSpec.scala index d415d6487..a28092daa 100644 --- a/sqlserver/src/test/scala/zio/sql/sqlserver/SqlServerRunnableSpec.scala +++ b/sqlserver/src/test/scala/zio/sql/sqlserver/SqlServerRunnableSpec.scala @@ -1,34 +1,19 @@ package zio.sql.sqlserver -import zio._ -import zio.test._ -import java.util.Properties -import zio.sql.{ ConnectionPoolConfig, JdbcRunnableSpec } +import com.dimafeng.testcontainers.{ JdbcDatabaseContainer, MSSQLServerContainer, SingleContainer } +import org.testcontainers.utility.DockerImageName +import zio.sql.JdbcRunnableSpec trait SqlServerRunnableSpec extends JdbcRunnableSpec with SqlServerJdbcModule { - def autoCommit: Boolean = true + override protected def getContainer: SingleContainer[_] with JdbcDatabaseContainer = + new MSSQLServerContainer( + dockerImageName = DockerImageName + .parse("mcr.microsoft.com/azure-sql-edge:latest") + .asCompatibleSubstituteFor("mcr.microsoft.com/mssql/server") + ).configure { a => + a.withInitScript("db_schema.sql") + () + } - private def connProperties(user: String, password: String): Properties = { - val props = new Properties - props.setProperty("user", user) - props.setProperty("password", password) - props - } - - val poolConfigLayer = ZLayer.scoped { - TestContainer.sqlServer - .map(a => - ConnectionPoolConfig( - url = a.jdbcUrl, - properties = connProperties(a.username, a.password), - autoCommit = autoCommit - ) - ) - } - - override def spec: Spec[TestEnvironment, Any] = - specLayered.provideCustomLayerShared(jdbcLayer) - - def specLayered: Spec[JdbcEnvironment, Object] } diff --git a/sqlserver/src/test/scala/zio/sql/sqlserver/TestContainer.scala b/sqlserver/src/test/scala/zio/sql/sqlserver/TestContainer.scala deleted file mode 100644 index 8834755dd..000000000 --- a/sqlserver/src/test/scala/zio/sql/sqlserver/TestContainer.scala +++ /dev/null @@ -1,30 +0,0 @@ -package zio.sql.sqlserver - -import com.dimafeng.testcontainers.MSSQLServerContainer -import org.testcontainers.utility.DockerImageName -import zio._ - -object TestContainer { - - /** - * We are using Azure sql edge because MS Sql Server image won't run on ARM. - */ - val sqlServer: ZIO[Scope, Throwable, MSSQLServerContainer] = - ZIO.acquireRelease { - ZIO.attemptBlocking { - val c = new MSSQLServerContainer( - dockerImageName = DockerImageName - .parse("mcr.microsoft.com/azure-sql-edge:latest") - .asCompatibleSubstituteFor("mcr.microsoft.com/mssql/server") - ).configure { a => - a.withInitScript("db_schema.sql") - () - } - c.start() - c - } - } { container => - ZIO.attemptBlocking(container.stop()).orDie - } - -}