diff --git a/build.sbt b/build.sbt index 9f56526b..af0adcf8 100644 --- a/build.sbt +++ b/build.sbt @@ -98,6 +98,7 @@ lazy val root = (project in file(".")) moduleGcloud, moduleRedpanda, moduleMinIO, + moduleWireMock, allOld ) .settings(noPublishSettings) @@ -507,3 +508,11 @@ lazy val moduleMinIO = (project in file("modules/minio")) name := "testcontainers-scala-minio", libraryDependencies ++= Dependencies.moduleMinIO.value ) + +lazy val moduleWireMock = (project in file("modules/wiremock")) + .dependsOn(core % "compile->compile;test->test;provided->provided", scalatest % "test->test") + .settings(commonSettings) + .settings( + name := "testcontainers-scala-wiremock", + libraryDependencies ++= Dependencies.moduleWireMock.value + ) diff --git a/docs/src/main/tut/setup.md b/docs/src/main/tut/setup.md index 8d007f62..43f82479 100644 --- a/docs/src/main/tut/setup.md +++ b/docs/src/main/tut/setup.md @@ -93,6 +93,7 @@ Here is the full list of the [currently available modules](https://github.com/te * `testcontainers-scala-gcloud` — module with the Bigtable, Firebase and PubSub emulator containers. * `testcontainers-scala-minio` — module with MinIO container. * `testcontainers-scala-redis` — module with Redis container. +* `testcontainers-scala-wiremock` - module with WireMock container. Most of the modules are just proxies to the testcontainers-java modules and behave exactly like java containers. You can find documentation about them in the [testcontainers-java docs pages](https://www.testcontainers.org/). diff --git a/modules/wiremock/src/main/scala/com/dimafeng/testcontainers/WireMockContainer.scala b/modules/wiremock/src/main/scala/com/dimafeng/testcontainers/WireMockContainer.scala new file mode 100644 index 00000000..fac5820b --- /dev/null +++ b/modules/wiremock/src/main/scala/com/dimafeng/testcontainers/WireMockContainer.scala @@ -0,0 +1,56 @@ +package com.dimafeng.testcontainers + +import org.wiremock.integrations.testcontainers.{ + WireMockContainer => JavaWireMockContainer +} +import org.testcontainers.utility.DockerImageName + +class WireMockContainer(underlying: JavaWireMockContainer) + extends SingleContainer[JavaWireMockContainer] { + override val container: JavaWireMockContainer = underlying + + def getHost: String = container.getHost + def getPort: Int = container.getPort + def getBaseUrl: String = container.getBaseUrl + def getUrl(path: String): String = container.getUrl(path) +} + +object WireMockContainer { + val defaultImage: String = "wiremock/wiremock" + val defaultTag: String = "latest" + val defaultDockerImageName: String = s"$defaultImage:$defaultTag" + + case class Def( + dockerImageName: DockerImageName = + DockerImageName.parse(defaultDockerImageName), + builderFs: Seq[JavaWireMockContainer => JavaWireMockContainer] = + Seq.empty[JavaWireMockContainer => JavaWireMockContainer] + ) extends ContainerDef { + override type Container = WireMockContainer + + def withMappingFromResource(resourceName: String): Def = { + copy(builderFs = + builderFs :+ ((underlying: JavaWireMockContainer) => + underlying.withMappingFromResource(resourceName) + ) + ) + } + + def withFileFromResource(resourceName: String): Def = { + copy(builderFs = + builderFs :+ ((underlying: JavaWireMockContainer) => + underlying.withFileFromResource(resourceName) + ) + ) + } + + override protected def createContainer(): WireMockContainer = { + val underlying = + builderFs.foldLeft(new JavaWireMockContainer(dockerImageName))( + (underlying, f) => f(underlying) + ) + + new WireMockContainer(underlying) + } + } +} diff --git a/modules/wiremock/src/test/resources/bar_body.json b/modules/wiremock/src/test/resources/bar_body.json new file mode 100644 index 00000000..18835eef --- /dev/null +++ b/modules/wiremock/src/test/resources/bar_body.json @@ -0,0 +1 @@ +{"result":"world"} diff --git a/modules/wiremock/src/test/resources/bar_mapping.json b/modules/wiremock/src/test/resources/bar_mapping.json new file mode 100644 index 00000000..db73d0ca --- /dev/null +++ b/modules/wiremock/src/test/resources/bar_mapping.json @@ -0,0 +1,13 @@ +{ + "request": { + "url": "/bar", + "method": "GET" + }, + "response": { + "status": 200, + "bodyFileName": "bar_body.json", + "headers": { + "Content-Type": "application/json" + } + } +} diff --git a/modules/wiremock/src/test/resources/foo_mapping.json b/modules/wiremock/src/test/resources/foo_mapping.json new file mode 100644 index 00000000..66402ae3 --- /dev/null +++ b/modules/wiremock/src/test/resources/foo_mapping.json @@ -0,0 +1,15 @@ +{ + "request": { + "url": "/foo", + "method": "GET" + }, + "response": { + "status": 200, + "jsonBody": { + "result": "hello" + }, + "headers": { + "Content-Type": "application/json" + } + } +} diff --git a/modules/wiremock/src/test/scala/com/dimafeng/testcontainers/WireMockSpec.scala b/modules/wiremock/src/test/scala/com/dimafeng/testcontainers/WireMockSpec.scala new file mode 100644 index 00000000..cc794330 --- /dev/null +++ b/modules/wiremock/src/test/scala/com/dimafeng/testcontainers/WireMockSpec.scala @@ -0,0 +1,41 @@ +package com.dimafeng.testcontainers + +import com.dimafeng.testcontainers.scalatest.TestContainersForAll +import org.scalatest.flatspec.AnyFlatSpec +import sttp.client3._ + +class WireMockSpec extends AnyFlatSpec with TestContainersForAll { + override type Containers = WireMockContainer + + override def startContainers(): WireMockContainer = { + WireMockContainer + .Def() + .withMappingFromResource("foo_mapping.json") + .withMappingFromResource("bar_mapping.json") + .withFileFromResource("bar_body.json") + .start() + } + + "WireMock container" should "start" in withContainers { container => + val backend = HttpURLConnectionBackend() + + val response = basicRequest + .get(uri"${container.getUrl("/__admin/health")}") + .send(backend) + + assert(response.code.code == 200) + } + + "WireMock container" should "support adding mappings and files" in withContainers { + container => + val backend = HttpURLConnectionBackend() + + val fooResponse = + basicRequest.get(uri"${container.getUrl("/foo")}").send(backend) + val barResponse = + basicRequest.get(uri"${container.getUrl("/bar")}").send(backend) + + assert(fooResponse.body == Right("{\"result\":\"hello\"}")) + assert(barResponse.body == Right("{\"result\":\"world\"}\n")) + } +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index baf9e143..f60248ef 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -38,6 +38,7 @@ object Dependencies { private val pubsubVersion = "1.116.4" private val redisTestcontainersVersion = "2.0.1" private val jedisVersion = "5.0.0" + private val wireMockTestcontainersVersion = "1.0-alpha-13" val allOld = Def.setting( PROVIDED( @@ -321,4 +322,12 @@ object Dependencies { "org.testcontainers" % "minio" % testcontainersVersion ) ) + + val moduleWireMock = Def.setting( + COMPILE( + "org.wiremock.integrations.testcontainers" % "wiremock-testcontainers-module" % wireMockTestcontainersVersion + ) ++ TEST( + "com.softwaremill.sttp.client3" %% "core" % sttpVersion + ) + ) }