From cbdbb749c5e8ceb634ea33d73240082b5ebe75d0 Mon Sep 17 00:00:00 2001 From: jbwheatley Date: Mon, 19 Oct 2020 19:25:03 +0100 Subject: [PATCH] Single import test module (#182) * new module added scalapact-scalatest-suite --- build.sbt | 16 +- example/consumer/pact.sbt | 1 + ...ala-pact-consumer_scala-pact-provider.json | 44 ++-- .../provider_pact-for-verification/build.sbt | 14 +- .../provider/VerifyContractsSpec.scala | 10 +- ...ala-pact-consumer_scala-pact-provider.json | 44 ++-- .../itv/scalapact/circe13/JsonInstances.scala | 9 + .../com/itv/scalapact/circe13/package.scala | 12 +- .../com/itv/scalapact/json/package.scala | 9 +- .../src/main/resources/log4j.properties | 12 + .../src/main/resources/logback.xml | 20 ++ .../com/itv/scalapact/PactForgerSuite.scala | 6 + .../com/itv/scalapact/PactVerifySuite.scala | 6 + .../itv/scalapact/PactForgerSuiteSpec.scala | 15 ++ .../itv/scalapact/PactVerifySuiteSpec.scala | 9 + .../scalapact/ScalaPactContractWriter.scala | 14 +- .../com/itv/scalapact/ScalaPactForger.scala | 214 +----------------- .../com/itv/scalapact/ScalaPactMock.scala | 4 +- .../com/itv/scalapact/ScalaPactVerify.scala | 4 +- .../model/ScalaPactDescription.scala | 66 ++++++ .../model/ScalaPactInteraction.scala | 79 +++++++ .../model/ScalaPactMatchingRules.scala | 29 +++ .../scalapact/model/ScalaPactOptions.scala | 10 + .../scalapact/model/ScalaPactRequest.scala | 14 ++ .../scalapact/model/ScalaPactResponse.scala | 10 + .../ScalaPactContractWriterSpec.scala | 3 +- 26 files changed, 378 insertions(+), 296 deletions(-) create mode 100644 scalapact-circe-0-13/src/main/scala/com/itv/scalapact/circe13/JsonInstances.scala create mode 100644 scalapact-scalatest-suite/src/main/resources/log4j.properties create mode 100644 scalapact-scalatest-suite/src/main/resources/logback.xml create mode 100644 scalapact-scalatest-suite/src/main/scala/com/itv/scalapact/PactForgerSuite.scala create mode 100644 scalapact-scalatest-suite/src/main/scala/com/itv/scalapact/PactVerifySuite.scala create mode 100644 scalapact-scalatest-suite/src/test/scala/com/itv/scalapact/PactForgerSuiteSpec.scala create mode 100644 scalapact-scalatest-suite/src/test/scala/com/itv/scalapact/PactVerifySuiteSpec.scala create mode 100644 scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactDescription.scala create mode 100644 scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactInteraction.scala create mode 100644 scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactMatchingRules.scala create mode 100644 scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactOptions.scala create mode 100644 scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactRequest.scala create mode 100644 scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactResponse.scala diff --git a/build.sbt b/build.sbt index e7f5c3b46..c5adb8ccc 100644 --- a/build.sbt +++ b/build.sbt @@ -410,6 +410,20 @@ lazy val framework = ) .dependsOn(core) +lazy val frameworkWithDeps = + (project in file("scalapact-scalatest-suite")) + .settings(commonSettings: _*) + .settings(publishSettings: _*) + .settings( + name := "scalapact-scalatest-suite", + mappings in (Compile, packageBin) ~= { + _.filterNot { case (_, fileName) => fileName == "logback.xml" || fileName == "log4j.properties" } + } + ) + .dependsOn(framework) + .dependsOn(circe13) + .dependsOn(http4s021) + lazy val standalone = (project in file("scalapact-standalone-stubber")) .settings(commonSettings: _*) @@ -480,7 +494,7 @@ lazy val scalaPactProject = .aggregate(shared, core, pluginShared, plugin, pluginNoDeps, framework, testShared) .aggregate(http4s016a, http4s017, http4s018, http4s020, http4s021) .aggregate(argonaut62, circe08, circe09, circe10, circe11, circe12, circe13) - .aggregate(standalone) + .aggregate(standalone, frameworkWithDeps) .aggregate(docs) .aggregate(pactSpec, testsWithDeps) diff --git a/example/consumer/pact.sbt b/example/consumer/pact.sbt index 2ca027700..b50d7cbd6 100644 --- a/example/consumer/pact.sbt +++ b/example/consumer/pact.sbt @@ -1,4 +1,5 @@ //For publishing to pact-broker test server (these credentials are public knowledge) +import scala.concurrent.duration._ import scala.concurrent.duration._ diff --git a/example/provider/delivered_pacts/scala-pact-consumer_scala-pact-provider.json b/example/provider/delivered_pacts/scala-pact-consumer_scala-pact-provider.json index e35741a21..0ff366479 100644 --- a/example/provider/delivered_pacts/scala-pact-consumer_scala-pact-provider.json +++ b/example/provider/delivered_pacts/scala-pact-consumer_scala-pact-provider.json @@ -6,28 +6,6 @@ "name" : "scala-pact-consumer" }, "interactions" : [ - { - "providerState" : "Results: Bob, Fred, Harry", - "description" : "Fetching results", - "request" : { - "method" : "GET", - "path" : "/results" - }, - "response" : { - "status" : 200, - "headers" : { - "Pact" : "modifiedRequest" - }, - "body" : { - "count" : 3, - "results" : [ - "Bob", - "Fred", - "Harry" - ] - } - } - }, { "description" : "Fetching least secure auth token ever", "request" : { @@ -59,6 +37,28 @@ } } } + }, + { + "providerState" : "Results: Bob, Fred, Harry", + "description" : "Fetching results", + "request" : { + "method" : "GET", + "path" : "/results" + }, + "response" : { + "status" : 200, + "headers" : { + "Pact" : "modifiedRequest" + }, + "body" : { + "count" : 3, + "results" : [ + "Bob", + "Fred", + "Harry" + ] + } + } } ], "metadata" : { diff --git a/example/provider_pact-for-verification/build.sbt b/example/provider_pact-for-verification/build.sbt index a575296c1..60206d148 100644 --- a/example/provider_pact-for-verification/build.sbt +++ b/example/provider_pact-for-verification/build.sbt @@ -14,14 +14,12 @@ libraryDependencies ++= { //A hack so we don't have to manually update the scala-pact version for the examples lazy val pactVersion = IO.read(pactVersionFile.value).split('"')(1) Seq( - "org.http4s" %% "http4s-blaze-server" % "0.21.7", - "org.http4s" %% "http4s-dsl" % "0.21.7", - "org.http4s" %% "http4s-circe" % "0.21.7", - "org.slf4j" % "slf4j-simple" % "1.6.4", - "org.scalatest" %% "scalatest" % "3.0.8" % "test", - "com.itv" %% "scalapact-circe-0-13" % pactVersion % "test", - "com.itv" %% "scalapact-http4s-0-21" % pactVersion % "test", - "com.itv" %% "scalapact-scalatest" % pactVersion % "test", + "org.http4s" %% "http4s-blaze-server" % "0.21.7", + "org.http4s" %% "http4s-dsl" % "0.21.7", + "org.http4s" %% "http4s-circe" % "0.21.7", + "org.slf4j" % "slf4j-simple" % "1.6.4", + "org.scalatest" %% "scalatest" % "3.0.8" % "test", + "com.itv" %% "scalapact-scalatest-suite" % pactVersion % "test", // Optional for auto-derivation of JSON codecs "io.circe" %% "circe-generic" % "0.13.0", // Optional for string interpolation to JSON model diff --git a/example/provider_pact-for-verification/src/test/scala/com/example/provider/VerifyContractsSpec.scala b/example/provider_pact-for-verification/src/test/scala/com/example/provider/VerifyContractsSpec.scala index 68d29dec9..1461447f1 100644 --- a/example/provider_pact-for-verification/src/test/scala/com/example/provider/VerifyContractsSpec.scala +++ b/example/provider_pact-for-verification/src/test/scala/com/example/provider/VerifyContractsSpec.scala @@ -1,16 +1,12 @@ package com.example.provider import org.scalatest.{BeforeAndAfterAll, FunSpec, Matchers} -import com.itv.scalapact.ScalaPactVerify._ +import com.itv.scalapact.PactVerifySuite import com.itv.scalapact.shared.{ConsumerVersionSelector, PactBrokerAuthorization, ProviderStateResult} import scala.concurrent.duration._ -class VerifyContractsSpec extends FunSpec with Matchers with BeforeAndAfterAll { - - import com.itv.scalapact.circe13._ - import com.itv.scalapact.http4s21._ - +class VerifyContractsSpec extends FunSpec with Matchers with BeforeAndAfterAll with PactVerifySuite { val serverAllocated = AlternateStartupApproach.serverResource(_ => List("Bob", "Fred", "Harry"), _ => "abcABC123").allocated.unsafeRunSync() @@ -37,7 +33,7 @@ class VerifyContractsSpec extends FunSpec with Matchers with BeforeAndAfterAll { None, //again, these are publicly known creds for a test pact-broker PactBrokerAuthorization(pactBrokerCredentials = ("dXfltyFMgNOFZAxr8io9wJ37iUpY42M", "O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1"), ""), - None + Some(5.seconds) ) ) .setupProviderState("given") { diff --git a/example/provider_tests/delivered_pacts/scala-pact-consumer_scala-pact-provider.json b/example/provider_tests/delivered_pacts/scala-pact-consumer_scala-pact-provider.json index e35741a21..0ff366479 100644 --- a/example/provider_tests/delivered_pacts/scala-pact-consumer_scala-pact-provider.json +++ b/example/provider_tests/delivered_pacts/scala-pact-consumer_scala-pact-provider.json @@ -6,28 +6,6 @@ "name" : "scala-pact-consumer" }, "interactions" : [ - { - "providerState" : "Results: Bob, Fred, Harry", - "description" : "Fetching results", - "request" : { - "method" : "GET", - "path" : "/results" - }, - "response" : { - "status" : 200, - "headers" : { - "Pact" : "modifiedRequest" - }, - "body" : { - "count" : 3, - "results" : [ - "Bob", - "Fred", - "Harry" - ] - } - } - }, { "description" : "Fetching least secure auth token ever", "request" : { @@ -59,6 +37,28 @@ } } } + }, + { + "providerState" : "Results: Bob, Fred, Harry", + "description" : "Fetching results", + "request" : { + "method" : "GET", + "path" : "/results" + }, + "response" : { + "status" : 200, + "headers" : { + "Pact" : "modifiedRequest" + }, + "body" : { + "count" : 3, + "results" : [ + "Bob", + "Fred", + "Harry" + ] + } + } } ], "metadata" : { diff --git a/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/circe13/JsonInstances.scala b/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/circe13/JsonInstances.scala new file mode 100644 index 000000000..d773c870d --- /dev/null +++ b/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/circe13/JsonInstances.scala @@ -0,0 +1,9 @@ +package com.itv.scalapact.circe13 + +import com.itv.scalapact.shared.typeclasses.{IPactReader, IPactWriter} + +trait JsonInstances { + implicit val pactReaderInstance: IPactReader = new PactReader + + implicit val pactWriterInstance: IPactWriter = new PactWriter +} diff --git a/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/circe13/package.scala b/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/circe13/package.scala index b584c373d..d6c1c7dbb 100644 --- a/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/circe13/package.scala +++ b/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/circe13/package.scala @@ -1,13 +1,3 @@ package com.itv.scalapact -import com.itv.scalapact.shared.typeclasses.{IPactReader, IPactWriter} - -package object circe13 { - - implicit val pactReaderInstance: IPactReader = - new PactReader - - implicit val pactWriterInstance: IPactWriter = - new PactWriter - -} +package object circe13 extends JsonInstances diff --git a/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/json/package.scala b/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/json/package.scala index 02421b9ed..7314200e8 100644 --- a/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/json/package.scala +++ b/scalapact-circe-0-13/src/main/scala/com/itv/scalapact/json/package.scala @@ -1,12 +1,7 @@ package com.itv.scalapact -import com.itv.scalapact.circe13.{PactReader, PactWriter} -import com.itv.scalapact.shared.typeclasses.{IPactReader, IPactWriter} - -package object json { - implicit val pactReaderInstance: IPactReader = new PactReader - - implicit val pactWriterInstance: IPactWriter = new PactWriter +import com.itv.scalapact.circe13.JsonInstances +package object json extends JsonInstances { val JsonConversionFunctions: circe13.JsonConversionFunctions.type = circe13.JsonConversionFunctions } diff --git a/scalapact-scalatest-suite/src/main/resources/log4j.properties b/scalapact-scalatest-suite/src/main/resources/log4j.properties new file mode 100644 index 000000000..8c2015717 --- /dev/null +++ b/scalapact-scalatest-suite/src/main/resources/log4j.properties @@ -0,0 +1,12 @@ +# Root logger option +log4j.rootLogger=INFO, stdout + +# Direct log messages to stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n + + +log4j.logger.com.github.tomakehurst=ERROR +log4j.logger.org.mortbay.log=ERROR \ No newline at end of file diff --git a/scalapact-scalatest-suite/src/main/resources/logback.xml b/scalapact-scalatest-suite/src/main/resources/logback.xml new file mode 100644 index 000000000..b492807bb --- /dev/null +++ b/scalapact-scalatest-suite/src/main/resources/logback.xml @@ -0,0 +1,20 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + \ No newline at end of file diff --git a/scalapact-scalatest-suite/src/main/scala/com/itv/scalapact/PactForgerSuite.scala b/scalapact-scalatest-suite/src/main/scala/com/itv/scalapact/PactForgerSuite.scala new file mode 100644 index 000000000..e833847d9 --- /dev/null +++ b/scalapact-scalatest-suite/src/main/scala/com/itv/scalapact/PactForgerSuite.scala @@ -0,0 +1,6 @@ +package com.itv.scalapact + +import com.itv.scalapact.circe13.JsonInstances +import com.itv.scalapact.http4s21.impl.HttpInstances + +trait PactForgerSuite extends ScalaPactForgerDsl with HttpInstances with JsonInstances diff --git a/scalapact-scalatest-suite/src/main/scala/com/itv/scalapact/PactVerifySuite.scala b/scalapact-scalatest-suite/src/main/scala/com/itv/scalapact/PactVerifySuite.scala new file mode 100644 index 000000000..621a61a3e --- /dev/null +++ b/scalapact-scalatest-suite/src/main/scala/com/itv/scalapact/PactVerifySuite.scala @@ -0,0 +1,6 @@ +package com.itv.scalapact + +import com.itv.scalapact.circe13.JsonInstances +import com.itv.scalapact.http4s21.impl.HttpInstances + +trait PactVerifySuite extends ScalaPactVerifyDsl with HttpInstances with JsonInstances diff --git a/scalapact-scalatest-suite/src/test/scala/com/itv/scalapact/PactForgerSuiteSpec.scala b/scalapact-scalatest-suite/src/test/scala/com/itv/scalapact/PactForgerSuiteSpec.scala new file mode 100644 index 000000000..256706947 --- /dev/null +++ b/scalapact-scalatest-suite/src/test/scala/com/itv/scalapact/PactForgerSuiteSpec.scala @@ -0,0 +1,15 @@ +package com.itv.scalapact + +//should compile +class PactForgerSuiteSpec extends PactForgerSuite { + val pact = + forgePact + .between("consumer") + .and("provider") + .addInteraction( + interaction + .description("") + .uponReceiving(GET, "") + .willRespondWith(200) + ).runConsumerTest(_ => ()) +} diff --git a/scalapact-scalatest-suite/src/test/scala/com/itv/scalapact/PactVerifySuiteSpec.scala b/scalapact-scalatest-suite/src/test/scala/com/itv/scalapact/PactVerifySuiteSpec.scala new file mode 100644 index 000000000..432f66dee --- /dev/null +++ b/scalapact-scalatest-suite/src/test/scala/com/itv/scalapact/PactVerifySuiteSpec.scala @@ -0,0 +1,9 @@ +package com.itv.scalapact + +//Should compile +class PactVerifySuiteSpec extends PactVerifySuite { + val verify = verifyPact + .withPactSource(loadFromLocal("")) + .noSetupRequired + .runVerificationAgainst(80) +} diff --git a/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactContractWriter.scala b/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactContractWriter.scala index 1832ba509..70a1db7d2 100644 --- a/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactContractWriter.scala +++ b/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactContractWriter.scala @@ -3,18 +3,12 @@ package com.itv.scalapact import java.io.{File, PrintWriter} import java.nio.charset.StandardCharsets -import com.itv.scalapact.ScalaPactForger.{ - ScalaPactDescriptionFinal, - ScalaPactInteractionFinal, - ScalaPactMatchingRule, - ScalaPactMatchingRuleArrayMinLength, - ScalaPactMatchingRuleRegex, - ScalaPactMatchingRuleType -} +import com.itv.scalapact.model.ScalaPactMatchingRule.{ScalaPactMatchingRuleArrayMinLength, ScalaPactMatchingRuleRegex, ScalaPactMatchingRuleType} +import com.itv.scalapact.model.{ScalaPactDescriptionFinal, ScalaPactInteractionFinal, ScalaPactMatchingRule} import com.itv.scalapact.shared._ import com.itv.scalapact.shared.typeclasses.IPactWriter -object ScalaPactContractWriter { +private[scalapact] object ScalaPactContractWriter { def writePactContracts(outputPath: String)(implicit pactWriter: IPactWriter): ScalaPactDescriptionFinal => Unit = pactDescription => { val dirFile = new File(outputPath) @@ -74,7 +68,7 @@ object ScalaPactContractWriter { BuildInfo.version ) - private[scalapact] def producePactFromDescription: ScalaPactDescriptionFinal => Pact = + def producePactFromDescription: ScalaPactDescriptionFinal => Pact = pactDescription => Pact( provider = PactActor(pactDescription.provider), diff --git a/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactForger.scala b/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactForger.scala index c19139cc6..6c6b6a3f2 100644 --- a/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactForger.scala +++ b/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactForger.scala @@ -1,73 +1,21 @@ package com.itv.scalapact -import com.itv.scalapact.shared.{HttpMethod, SslContextMap} +import com.itv.scalapact.model.ScalaPactMatchingRule.{ScalaPactMatchingRuleArrayMinLength, ScalaPactMatchingRuleRegex, ScalaPactMatchingRuleType} +import com.itv.scalapact.model.{ScalaPactDescription, ScalaPactInteraction, ScalaPactMatchingRules, ScalaPactOptions, ScalaPactRequest, ScalaPactResponse} +import com.itv.scalapact.shared.HttpMethod -import scala.util.Properties -import com.itv.scalapact.shared.Maps._ -import com.itv.scalapact.shared.typeclasses.{IPactReader, IPactStubber, IPactWriter, IScalaPactHttpClient, IScalaPactHttpClientBuilder} - -import scala.concurrent.duration._ - -object ScalaPactForger { +trait ScalaPactForgerDsl { implicit val options: ScalaPactOptions = ScalaPactOptions.DefaultOptions - object forgePact extends ForgePactElements { - protected val strict: Boolean = false - } - - object forgeStrictPact extends ForgePactElements { - protected val strict: Boolean = true - } - - sealed trait ForgePactElements { - protected val strict: Boolean + object forgePact extends ForgePactElements(strict = false) + object forgeStrictPact extends ForgePactElements(strict = true) + sealed class ForgePactElements(strict: Boolean) { def between(consumer: String): ScalaPartialPact = new ScalaPartialPact(consumer) class ScalaPartialPact(consumer: String) { - def and(provider: String): ScalaPactDescription = new ScalaPactDescription(consumer, provider, None, Nil) + def and(provider: String): ScalaPactDescription = new ScalaPactDescription(strict, consumer, provider, None, Nil) } - - class ScalaPactDescription(consumer: String, - provider: String, - sslContextName: Option[String], - interactions: List[ScalaPactInteraction]) { - - /** - * Adds interactions to the Pact. Interactions should be created using the helper object 'interaction' - * - * @param interaction [ScalaPactInteraction] definition - * @return [ScalaPactDescription] to allow the builder to continue - */ - def addInteraction(interaction: ScalaPactInteraction): ScalaPactDescription = - new ScalaPactDescription(consumer, provider, sslContextName, interactions :+ interaction) - - def addSslContextForServer(name: String): ScalaPactDescription = - new ScalaPactDescription(consumer, provider, Some(name), interactions) - - def runConsumerTest[F[_], A](test: ScalaPactMockConfig => A)(implicit options: ScalaPactOptions, - sslContextMap: SslContextMap, - pactReader: IPactReader, - pactWriter: IPactWriter, - httpClientBuilder: IScalaPactHttpClientBuilder[F], - pactStubber: IPactStubber): A = { - implicit val client: IScalaPactHttpClient[F] = - httpClientBuilder.build(2.seconds, sslContextName) - ScalaPactMock.runConsumerIntegrationTest(strict)( - finalise(options) - )(test) - } - - private def finalise(implicit options: ScalaPactOptions): ScalaPactDescriptionFinal = - ScalaPactDescriptionFinal( - consumer, - provider, - sslContextName, - interactions.map(i => i.finalise), - options - ) - } - } object interaction { @@ -75,134 +23,6 @@ object ScalaPactForger { new ScalaPactInteraction(message, None, None, ScalaPactRequest.default, ScalaPactResponse.default) } - class ScalaPactInteraction(description: String, - providerState: Option[String], - sslContextName: Option[String], - request: ScalaPactRequest, - response: ScalaPactResponse) { - def given(state: String): ScalaPactInteraction = - new ScalaPactInteraction(description, Option(state), sslContextName, request, response) - - def withSsl(sslContextName: String): ScalaPactInteraction = - new ScalaPactInteraction(description, providerState, Some(sslContextName), request, response) - - def uponReceiving(path: String): ScalaPactInteraction = uponReceiving(GET, path, None, Map.empty, None, ScalaPactMatchingRules.empty) - - def uponReceiving(method: HttpMethod, path: String): ScalaPactInteraction = - uponReceiving(method, path, None, Map.empty, None, ScalaPactMatchingRules.empty) - - def uponReceiving(method: HttpMethod, path: String, query: Option[String]): ScalaPactInteraction = - uponReceiving(method, path, query, Map.empty, None, ScalaPactMatchingRules.empty) - - def uponReceiving(method: HttpMethod, - path: String, - query: Option[String], - headers: Map[String, String]): ScalaPactInteraction = - uponReceiving(method, path, query, headers, None, ScalaPactMatchingRules.empty) - - def uponReceiving(method: HttpMethod, - path: String, - query: Option[String], - headers: Map[String, String], - body: String): ScalaPactInteraction = - uponReceiving(method, path, query, headers, Some(body), ScalaPactMatchingRules.empty) - - def uponReceiving(method: HttpMethod, - path: String, - query: Option[String], - headers: Map[String, String], - body: Option[String], - matchingRules: ScalaPactMatchingRules): ScalaPactInteraction = - new ScalaPactInteraction( - description, - providerState, - sslContextName, - ScalaPactRequest(method, path, query, headers, body, matchingRules.toOption), - response - ) - - def willRespondWith(status: Int): ScalaPactInteraction = willRespondWith(status, Map.empty, None, ScalaPactMatchingRules.empty) - - def willRespondWith(status: Int, body: String): ScalaPactInteraction = - willRespondWith(status, Map.empty, Option(body), ScalaPactMatchingRules.empty) - - def willRespondWith(status: Int, headers: Map[String, String], body: String): ScalaPactInteraction = - willRespondWith(status, headers, Option(body), ScalaPactMatchingRules.empty) - - def willRespondWith(status: Int, - headers: Map[String, String], - body: Option[String], - matchingRules: ScalaPactMatchingRules): ScalaPactInteraction = - new ScalaPactInteraction( - description, - providerState, - sslContextName, - request, - ScalaPactResponse(status, headers, body, matchingRules.toOption) - ) - - def finalise: ScalaPactInteractionFinal = - ScalaPactInteractionFinal(description, providerState, sslContextName, request, response) - } - - case class ScalaPactDescriptionFinal(consumer: String, - provider: String, - serverSslContextName: Option[String], - interactions: List[ScalaPactInteractionFinal], - options: ScalaPactOptions) { - def withHeaderForSsl: ScalaPactDescriptionFinal = - copy( - interactions = interactions.map( - i => - i.copy( - request = i.request - .copy(headers = i.request.headers addOpt (SslContextMap.sslContextHeaderName -> i.sslContextName)) - ) - ) - ) - } - - case class ScalaPactInteractionFinal(description: String, - providerState: Option[String], - sslContextName: Option[String], - request: ScalaPactRequest, - response: ScalaPactResponse) - - object ScalaPactRequest { - val default: ScalaPactRequest = ScalaPactRequest(GET, "/", None, Map.empty, None, None) - } - - case class ScalaPactRequest(method: HttpMethod, - path: String, - query: Option[String], - headers: Map[String, String], - body: Option[String], - matchingRules: Option[List[ScalaPactMatchingRule]]) - - sealed trait ScalaPactMatchingRule { - val key: String - } - - case class ScalaPactMatchingRuleRegex(key: String, regex: String) extends ScalaPactMatchingRule - - case class ScalaPactMatchingRuleType(key: String) extends ScalaPactMatchingRule - - case class ScalaPactMatchingRuleArrayMinLength(key: String, minimum: Int) extends ScalaPactMatchingRule - - case class ScalaPactMatchingRules(rules: List[ScalaPactMatchingRule]) { - def ~>(newRules: ScalaPactMatchingRules): ScalaPactMatchingRules = ScalaPactMatchingRules( - rules = rules ++ newRules.rules - ) - def toOption: Option[List[ScalaPactMatchingRule]] = rules match { - case Nil => None - case rs => Some(rs) - } - } - - object ScalaPactMatchingRules { - def empty: ScalaPactMatchingRules = ScalaPactMatchingRules(Nil) - } - object headerRegexRule { def apply(key: String, regex: String): ScalaPactMatchingRules = ScalaPactMatchingRules( rules = List(ScalaPactMatchingRuleRegex("$.headers." + key, regex)) @@ -227,22 +47,6 @@ object ScalaPactForger { ) } - object ScalaPactResponse { - val default: ScalaPactResponse = ScalaPactResponse(200, Map.empty, None, None) - } - - case class ScalaPactResponse(status: Int, - headers: Map[String, String], - body: Option[String], - matchingRules: Option[List[ScalaPactMatchingRule]]) - - object ScalaPactOptions { - val DefaultOptions: ScalaPactOptions = - ScalaPactOptions(writePactFiles = true, outputPath = Properties.envOrElse("pact.rootDir", "target/pacts")) - } - - case class ScalaPactOptions(writePactFiles: Boolean, outputPath: String) - val GET: HttpMethod = HttpMethod.GET val POST: HttpMethod = HttpMethod.POST val PUT: HttpMethod = HttpMethod.PUT @@ -253,3 +57,5 @@ object ScalaPactForger { val TRACE: HttpMethod = HttpMethod.TRACE val HEAD: HttpMethod = HttpMethod.HEAD } + +object ScalaPactForger extends ScalaPactForgerDsl diff --git a/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactMock.scala b/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactMock.scala index 9651ab059..59beb506f 100644 --- a/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactMock.scala +++ b/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactMock.scala @@ -1,11 +1,11 @@ package com.itv.scalapact -import com.itv.scalapact.ScalaPactForger._ +import com.itv.scalapact.model.ScalaPactDescriptionFinal import com.itv.scalapact.shared.{PactLogger, _} import com.itv.scalapact.shared.typeclasses.{IPactReader, IPactStubber, IPactWriter, IScalaPactHttpClient} import com.itv.scalapactcore.common.stubber.InteractionManager -object ScalaPactMock { +private[scalapact] object ScalaPactMock { private def configuredTestRunner[A]( pactDescription: ScalaPactDescriptionFinal diff --git a/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactVerify.scala b/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactVerify.scala index 8586a3c5a..a241be001 100644 --- a/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactVerify.scala +++ b/scalapact-scalatest/src/main/scala/com/itv/scalapact/ScalaPactVerify.scala @@ -13,7 +13,7 @@ import com.itv.scalapact.shared.ProviderStateResult.SetupProviderState import scala.util.Try -object ScalaPactVerify { +trait ScalaPactVerifyDsl { private val defaultClientTimeout: Duration = 2.seconds object verifyPact { @@ -389,3 +389,5 @@ object ScalaPactVerify { } } + +object ScalaPactVerify extends ScalaPactVerifyDsl diff --git a/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactDescription.scala b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactDescription.scala new file mode 100644 index 000000000..47b1a0bce --- /dev/null +++ b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactDescription.scala @@ -0,0 +1,66 @@ +package com.itv.scalapact.model + +import com.itv.scalapact.{ScalaPactMock, ScalaPactMockConfig} +import com.itv.scalapact.shared.SslContextMap +import com.itv.scalapact.shared.typeclasses.{IPactReader, IPactStubber, IPactWriter, IScalaPactHttpClient, IScalaPactHttpClientBuilder} +import com.itv.scalapact.shared.Maps._ + +import scala.concurrent.duration._ + +class ScalaPactDescription(strict: Boolean, + consumer: String, + provider: String, + sslContextName: Option[String], + interactions: List[ScalaPactInteraction]) { + + /** + * Adds interactions to the Pact. Interactions should be created using the helper object 'interaction' + * + * @param interaction [ScalaPactInteraction] definition + * @return [ScalaPactDescription] to allow the builder to continue + */ + def addInteraction(interaction: ScalaPactInteraction): ScalaPactDescription = + new ScalaPactDescription(strict, consumer, provider, sslContextName, interactions :+ interaction) + + def addSslContextForServer(name: String): ScalaPactDescription = + new ScalaPactDescription(strict, consumer, provider, Some(name), interactions) + + def runConsumerTest[F[_], A](test: ScalaPactMockConfig => A)(implicit options: ScalaPactOptions, + sslContextMap: SslContextMap, + pactReader: IPactReader, + pactWriter: IPactWriter, + httpClientBuilder: IScalaPactHttpClientBuilder[F], + pactStubber: IPactStubber): A = { + implicit val client: IScalaPactHttpClient[F] = + httpClientBuilder.build(2.seconds, sslContextName) + ScalaPactMock.runConsumerIntegrationTest(strict)( + finalise + )(test) + } + + private def finalise(implicit options: ScalaPactOptions): ScalaPactDescriptionFinal = + ScalaPactDescriptionFinal( + consumer, + provider, + sslContextName, + interactions.map(i => i.finalise), + options + ) +} + +final case class ScalaPactDescriptionFinal(consumer: String, + provider: String, + serverSslContextName: Option[String], + interactions: List[ScalaPactInteractionFinal], + options: ScalaPactOptions) { + def withHeaderForSsl: ScalaPactDescriptionFinal = + copy( + interactions = interactions.map( + i => + i.copy( + request = i.request + .copy(headers = i.request.headers addOpt (SslContextMap.sslContextHeaderName -> i.sslContextName)) + ) + ) + ) +} \ No newline at end of file diff --git a/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactInteraction.scala b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactInteraction.scala new file mode 100644 index 000000000..86b33f5fb --- /dev/null +++ b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactInteraction.scala @@ -0,0 +1,79 @@ +package com.itv.scalapact.model + +import com.itv.scalapact.shared._ + +class ScalaPactInteraction(description: String, + providerState: Option[String], + sslContextName: Option[String], + request: ScalaPactRequest, + response: ScalaPactResponse) { + def given(state: String): ScalaPactInteraction = + new ScalaPactInteraction(description, Option(state), sslContextName, request, response) + + def withSsl(sslContextName: String): ScalaPactInteraction = + new ScalaPactInteraction(description, providerState, Some(sslContextName), request, response) + + def uponReceiving(path: String): ScalaPactInteraction = uponReceiving(HttpMethod.GET, path, None, Map.empty, None, ScalaPactMatchingRules.empty) + + def uponReceiving(method: HttpMethod, path: String): ScalaPactInteraction = + uponReceiving(method, path, None, Map.empty, None, ScalaPactMatchingRules.empty) + + def uponReceiving(method: HttpMethod, path: String, query: Option[String]): ScalaPactInteraction = + uponReceiving(method, path, query, Map.empty, None, ScalaPactMatchingRules.empty) + + def uponReceiving(method: HttpMethod, + path: String, + query: Option[String], + headers: Map[String, String]): ScalaPactInteraction = + uponReceiving(method, path, query, headers, None, ScalaPactMatchingRules.empty) + + def uponReceiving(method: HttpMethod, + path: String, + query: Option[String], + headers: Map[String, String], + body: String): ScalaPactInteraction = + uponReceiving(method, path, query, headers, Some(body), ScalaPactMatchingRules.empty) + + def uponReceiving(method: HttpMethod, + path: String, + query: Option[String], + headers: Map[String, String], + body: Option[String], + matchingRules: ScalaPactMatchingRules): ScalaPactInteraction = + new ScalaPactInteraction( + description, + providerState, + sslContextName, + ScalaPactRequest(method, path, query, headers, body, matchingRules.toOption), + response + ) + + def willRespondWith(status: Int): ScalaPactInteraction = willRespondWith(status, Map.empty, None, ScalaPactMatchingRules.empty) + + def willRespondWith(status: Int, body: String): ScalaPactInteraction = + willRespondWith(status, Map.empty, Option(body), ScalaPactMatchingRules.empty) + + def willRespondWith(status: Int, headers: Map[String, String], body: String): ScalaPactInteraction = + willRespondWith(status, headers, Option(body), ScalaPactMatchingRules.empty) + + def willRespondWith(status: Int, + headers: Map[String, String], + body: Option[String], + matchingRules: ScalaPactMatchingRules): ScalaPactInteraction = + new ScalaPactInteraction( + description, + providerState, + sslContextName, + request, + ScalaPactResponse(status, headers, body, matchingRules.toOption) + ) + + private[scalapact] def finalise: ScalaPactInteractionFinal = + ScalaPactInteractionFinal(description, providerState, sslContextName, request, response) +} + +case class ScalaPactInteractionFinal(description: String, + providerState: Option[String], + sslContextName: Option[String], + request: ScalaPactRequest, + response: ScalaPactResponse) \ No newline at end of file diff --git a/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactMatchingRules.scala b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactMatchingRules.scala new file mode 100644 index 000000000..e35379d4f --- /dev/null +++ b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactMatchingRules.scala @@ -0,0 +1,29 @@ +package com.itv.scalapact.model + +sealed trait ScalaPactMatchingRule { + val key: String +} + +object ScalaPactMatchingRule { + + final case class ScalaPactMatchingRuleRegex(key: String, regex: String) extends ScalaPactMatchingRule + + final case class ScalaPactMatchingRuleType(key: String) extends ScalaPactMatchingRule + + final case class ScalaPactMatchingRuleArrayMinLength(key: String, minimum: Int) extends ScalaPactMatchingRule + +} + +final case class ScalaPactMatchingRules(rules: List[ScalaPactMatchingRule]) { + def ~>(newRules: ScalaPactMatchingRules): ScalaPactMatchingRules = ScalaPactMatchingRules( + rules = rules ++ newRules.rules + ) + def toOption: Option[List[ScalaPactMatchingRule]] = rules match { + case Nil => None + case rs => Some(rs) + } +} + +object ScalaPactMatchingRules { + def empty: ScalaPactMatchingRules = ScalaPactMatchingRules(Nil) +} diff --git a/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactOptions.scala b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactOptions.scala new file mode 100644 index 000000000..eed4b5028 --- /dev/null +++ b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactOptions.scala @@ -0,0 +1,10 @@ +package com.itv.scalapact.model + +import scala.util.Properties + +final case class ScalaPactOptions(writePactFiles: Boolean, outputPath: String) + +object ScalaPactOptions { + val DefaultOptions: ScalaPactOptions = + ScalaPactOptions(writePactFiles = true, outputPath = Properties.envOrElse("pact.rootDir", "target/pacts")) +} diff --git a/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactRequest.scala b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactRequest.scala new file mode 100644 index 000000000..fa021a64f --- /dev/null +++ b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactRequest.scala @@ -0,0 +1,14 @@ +package com.itv.scalapact.model + +import com.itv.scalapact.shared.HttpMethod + +case class ScalaPactRequest(method: HttpMethod, + path: String, + query: Option[String], + headers: Map[String, String], + body: Option[String], + matchingRules: Option[List[ScalaPactMatchingRule]]) + +object ScalaPactRequest { + val default: ScalaPactRequest = ScalaPactRequest(HttpMethod.GET, "/", None, Map.empty, None, None) +} diff --git a/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactResponse.scala b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactResponse.scala new file mode 100644 index 000000000..62b50cbfc --- /dev/null +++ b/scalapact-scalatest/src/main/scala/com/itv/scalapact/model/ScalaPactResponse.scala @@ -0,0 +1,10 @@ +package com.itv.scalapact.model + +object ScalaPactResponse { + val default: ScalaPactResponse = ScalaPactResponse(200, Map.empty, None, None) +} + +case class ScalaPactResponse(status: Int, + headers: Map[String, String], + body: Option[String], + matchingRules: Option[List[ScalaPactMatchingRule]]) diff --git a/scalapact-scalatest/src/test/scala/com/itv/scalapact/ScalaPactContractWriterSpec.scala b/scalapact-scalatest/src/test/scala/com/itv/scalapact/ScalaPactContractWriterSpec.scala index 5fd83954b..cc44ab2ac 100644 --- a/scalapact-scalatest/src/test/scala/com/itv/scalapact/ScalaPactContractWriterSpec.scala +++ b/scalapact-scalatest/src/test/scala/com/itv/scalapact/ScalaPactContractWriterSpec.scala @@ -1,6 +1,7 @@ package com.itv.scalapact -import com.itv.scalapact.ScalaPactForger.{ScalaPactDescriptionFinal, ScalaPactInteractionFinal, ScalaPactMatchingRuleArrayMinLength, ScalaPactOptions, ScalaPactRequest, ScalaPactResponse} +import com.itv.scalapact.model.ScalaPactMatchingRule.ScalaPactMatchingRuleArrayMinLength +import com.itv.scalapact.model.{ScalaPactDescriptionFinal, ScalaPactInteractionFinal, ScalaPactOptions, ScalaPactRequest, ScalaPactResponse} import com.itv.scalapact.shared.HttpMethod.GET import com.itv.scalapact.shared.MatchingRule import org.scalatest.{FunSpec, Matchers}