diff --git a/server/test/com/xsn/explorer/data/common/DockerPostgresService.scala b/server/test/com/xsn/explorer/data/common/DockerPostgresService.scala new file mode 100644 index 0000000..db146ca --- /dev/null +++ b/server/test/com/xsn/explorer/data/common/DockerPostgresService.scala @@ -0,0 +1,64 @@ +package com.xsn.explorer.data.common + +import java.sql.DriverManager + +import com.whisk.docker.{DockerCommandExecutor, DockerContainer, DockerContainerState, DockerKit, DockerReadyChecker} + +import scala.concurrent.ExecutionContext + +trait DockerPostgresService extends DockerKit { + + import DockerPostgresService._ + + import scala.concurrent.duration._ + + val postgresContainer = DockerContainer(PostgresImage) + .withPorts((PostgresAdvertisedPort, Some(PostgresExposedPort))) + .withEnv(s"POSTGRES_USER=$PostgresUsername", s"POSTGRES_PASSWORD=$PostgresPassword") + .withReadyChecker( + new PostgresReadyChecker().looped(15, 1.second) + ) + + abstract override def dockerContainers: List[DockerContainer] = + postgresContainer :: super.dockerContainers +} + +object DockerPostgresService { + + val PostgresImage = "postgres:9.6" + val PostgresUsername = "postgres" + val PostgresPassword = "" + val DatabaseName = "xsn_blockchain" + + def PostgresAdvertisedPort = 5432 + def PostgresExposedPort = 44444 + + class PostgresReadyChecker extends DockerReadyChecker { + + override def apply( + container: DockerContainerState)( + implicit docker: DockerCommandExecutor, + ec: ExecutionContext) = { + + container + .getPorts() + .map { ports => + try { + Class.forName("org.postgresql.Driver") + val url = s"jdbc:postgresql://${docker.host}:$PostgresExposedPort/" + Option(DriverManager.getConnection(url, PostgresUsername, PostgresPassword)) + .foreach { conn => + // NOTE: For some reason the result is always false + conn.createStatement().execute(s"CREATE DATABASE $DatabaseName") + conn.close() + } + + true + } catch { + case _: Throwable => + false + } + } + } + } +} diff --git a/server/test/com/xsn/explorer/data/common/PostgresDataHandlerSpec.scala b/server/test/com/xsn/explorer/data/common/PostgresDataHandlerSpec.scala new file mode 100644 index 0000000..de4abf6 --- /dev/null +++ b/server/test/com/xsn/explorer/data/common/PostgresDataHandlerSpec.scala @@ -0,0 +1,60 @@ +package com.xsn.explorer.data.common + +import com.spotify.docker.client.DefaultDockerClient +import com.whisk.docker.DockerFactory +import com.whisk.docker.impl.spotify.SpotifyDockerFactory +import com.whisk.docker.scalatest.DockerTestKit +import org.scalatest.time.{Second, Seconds, Span} +import org.scalatest.{BeforeAndAfterAll, MustMatchers, WordSpec} +import play.api.db.evolutions.Evolutions +import play.api.db.{Database, Databases} + +/** + * Allow us to write integration tests depending in a postgres database. + * + * The database is launched in a docker instance using docker-it-scala library. + * + * When the database is started, play evolutions are automatically applied, the + * idea is to let you write tests like this: + * {{{ + * class UserPostgresDALSpec extends PostgresDALSpec { + * lazy val dal = new UserPostgresDAL(database) + * ... + * } + * }}} + */ +trait PostgresDataHandlerSpec + extends WordSpec + with MustMatchers + with DockerTestKit + with DockerPostgresService + with BeforeAndAfterAll { + + import DockerPostgresService._ + + implicit val pc = PatienceConfig(Span(20, Seconds), Span(1, Second)) + + override implicit val dockerFactory: DockerFactory = new SpotifyDockerFactory( + DefaultDockerClient.fromEnv().build()) + + override def beforeAll(): Unit = { + super.beforeAll() + val _ = isContainerReady(postgresContainer).futureValue mustEqual true + } + + def database: Database = { + val database = Databases( + driver = "org.postgresql.Driver", + url = s"jdbc:postgresql://localhost:$PostgresExposedPort/$DatabaseName", + name = "default", + config = Map( + "username" -> PostgresUsername, + "password" -> PostgresPassword + ) + ) + + Evolutions.applyEvolutions(database) + + database + } +}