Skip to content

Commit

Permalink
Add support for response factory verification. Fixes #11
Browse files Browse the repository at this point in the history
  • Loading branch information
kflorence committed Aug 22, 2021
1 parent 85cf955 commit 678ac8e
Show file tree
Hide file tree
Showing 20 changed files with 241 additions and 152 deletions.
7 changes: 4 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ inThisBuild(
publish / skip := true // don't publish the root project

val commonSettings = Seq(
resolvers += Resolver.url("typesafe", url("https://repo.typesafe.com/typesafe/ivy-releases/"))(
Resolver.ivyStylePatterns
resolvers ++= Seq(
Resolver.mavenLocal,
Resolver.url("typesafe", url("https://repo.typesafe.com/typesafe/ivy-releases/"))(Resolver.ivyStylePatterns)
)
)

Expand All @@ -56,7 +57,7 @@ def moduleName(base: String, axis: Seq[VirtualAxis]): String = {
}

val moduleBase =
Def.setting((Compile / scalaSource).value.getParentFile().getParentFile().getParentFile())
Def.setting((Compile / scalaSource).value.getParentFile.getParentFile.getParentFile)

lazy val shared =
(projectMatrix in file("shared"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ trait PactVerifier extends Assertions with PactVerifyResources {
override private[pact4s] def failure(message: String)(implicit fileName: FileName, file: File, line: Line): Nothing =
fail(message)(new Location(file.value, line.value))
}

trait MessagePactVerifier extends PactVerifier {
def messages: ResponseFactory
override def responseFactory: Option[ResponseFactory] = Some(messages)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ package pact4s.munit

import munit.CatsEffectSuite
import pact4s.{MockProviderServer, ProviderInfoBuilder, PublishVerificationResults}
import pact4s.VerificationSettings.AnnotatedMethodVerificationSettings
import pact4s.messages.MessagesProvider

class MessagePactVerifierBrokerMUnitSuite extends CatsEffectSuite with PactVerifier {
class MessagePactVerifierBrokerMUnitSuite extends CatsEffectSuite with MessagePactVerifier {
val mock = new MockProviderServer(2348)

override val provider: ProviderInfoBuilder = mock.brokerProviderInfo(
providerName = "Pact4sMessageProvider",
verificationSettings = Some(AnnotatedMethodVerificationSettings(packagesToScan = List("pact4s.messages")))
)
def messages: ResponseFactory = MessagesProvider.messages
override val provider: ProviderInfoBuilder = mock.brokerProviderInfo(providerName = "Pact4sMessageProvider")

verifyPacts(
publishVerificationResults = Some(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package pact4s.munit

import munit.CatsEffectSuite
import pact4s.VerificationSettings.AnnotatedMethodVerificationSettings
import pact4s.{MockProviderServer, ProviderInfoBuilder}
import pact4s.messages.MessagesProvider

class MessagePactVerifierMUnitSuite extends CatsEffectSuite with PactVerifier {
class MessagePactVerifierMUnitSuite extends CatsEffectSuite with MessagePactVerifier {
val mock = new MockProviderServer(2347)

def messages: ResponseFactory = MessagesProvider.messages
override val provider: ProviderInfoBuilder = mock.fileSourceProviderInfo(
consumerName = "Pact4sMessageConsumer",
providerName = "Pact4sMessageProvider",
fileName = "./scripts/Pact4sMessageConsumer-Pact4sMessageProvider.json",
verificationSettings = Some(AnnotatedMethodVerificationSettings(packagesToScan = List("pact4s.messages")))
fileName = "./scripts/Pact4sMessageConsumer-Pact4sMessageProvider.json"
)

test("Verify pacts for provider `Pact4sProvider`") {
Expand Down
4 changes: 2 additions & 2 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import sbt._

object Dependencies {
val pactJvmJava11 = "4.2.9"
val pactJvmJava8 = "4.1.24"
val pactJvmJava11 = "4.2.10"
val pactJvmJava8 = "4.1.25"
val http4s = "1.0.0-M24"
val _circe = "0.14.1"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,4 @@ trait MessagePactForger extends MessagePactForgerResources with SuiteMixin { sel
type Effect[_] = Either[Throwable, _]

def beforeWritePacts(): Either[Throwable, Unit] = Right(())

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ trait PactVerifier extends Assertions with PactVerifyResources {
override private[pact4s] def failure(message: String)(implicit fileName: FileName, file: File, line: Line): Nothing =
fail(message)(Position(fileName.value, file.value, line.value))
}

trait MessagePactVerifier extends PactVerifier {
def messages: ResponseFactory
override def responseFactory: Option[ResponseFactory] = Some(messages)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@
package pact4s.scalatest

import au.com.dius.pact.consumer.BaseMockServer
import au.com.dius.pact.core.model.RequestResponseInteraction
import org.scalatest.{Args, CompositeStatus, Status, Suite, SuiteMixin}
import pact4s.RequestResponsePactForgerResources

import scala.jdk.CollectionConverters._

trait RequestResponsePactForger extends RequestResponsePactForgerResources with SuiteMixin { self: Suite =>

def interactions: List[RequestResponseInteraction] =
// This seems to be the only reliable way to access RequestResponseInteraction across JDK versions
pact.getInteractions.asScala.toList.collect { case interaction: RequestResponseInteraction => interaction }

def mockServer: BaseMockServer = allocatedServer.get

@volatile private var testFailed = false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package pact4s.scalatest

import au.com.dius.pact.consumer.PactTestExecutionContext
import au.com.dius.pact.core.model.messaging.MessagePact
import au.com.dius.pact.core.model.messaging.{Message, MessagePact}
import io.circe.Json
import io.circe.syntax.EncoderOps
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatest.Assertion
import pact4s.circe.implicits._

class MessagePactForgerScalaTestSuite extends AnyFlatSpec with Matchers with MessagePactForger {
Expand All @@ -27,26 +28,26 @@ class MessagePactForgerScalaTestSuite extends AnyFlatSpec with Matchers with Mes
.withContent(Json.arr(Json.obj("a" -> 1.asJson), Json.obj("b" -> true.asJson)))
.toMessagePact

it should "scalatest message pact test" in {
messages.head.as[Json].flatMap(_.hcursor.get[String]("hello")).getOrElse(fail()) shouldBe "harry"
messages.head.metadata.getOrElse("hi", fail()) shouldBe "there"
def verify(message: Message): Assertion = message.getDescription match {
case "A message to say hello" =>
message.as[Json].flatMap(_.hcursor.get[String]("hello")).getOrElse(fail()) shouldBe "harry"
message.metadata.getOrElse("hi", fail()) shouldBe "there"
case "A message to say goodbye" =>
message.as[Json].flatMap(_.hcursor.get[String]("goodbye")).getOrElse(fail()) shouldBe "harry"
case "A message with nested arrays in the body" =>
message.as[Json].flatMap(_.hcursor.get[List[Int]]("array")).getOrElse(fail()) shouldBe List(1, 2, 3)
case "A message with a json array as content" =>
val res = for {
json <- message.as[Json]
acur = json.hcursor.downArray
int <- acur.get[Int]("a")
bool <- acur.right.get[Boolean]("b")
} yield (int, bool)
res.getOrElse(fail()) shouldBe ((1, true))
case description => fail(s"Missing verification for message: '$description'.'")
}

it should "another scalatest message pact test" in {
messages(1).as[Json].flatMap(_.hcursor.get[String]("goodbye")).getOrElse(fail()) shouldBe "harry"
}

it should "send a message with a nested array in its body" in {
messages(2).as[Json].flatMap(_.hcursor.get[List[Int]]("array")).getOrElse(fail()) shouldBe List(1, 2, 3)
}

it should "send a message with a json array as its content" in {
val res = for {
json <- messages(3).as[Json]
acur = json.hcursor.downArray
int <- acur.get[Int]("a")
bool <- acur.right.get[Boolean]("b")
} yield (int, bool)
res.getOrElse(fail()) shouldBe ((1, true))
messages.foreach { message =>
it should s"forge: ${message.getDescription}" in verify(message)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ package pact4s.scalatest
import org.scalatest.BeforeAndAfterAll
import org.scalatest.flatspec.AnyFlatSpec
import pact4s.{MockProviderServer, ProviderInfoBuilder, PublishVerificationResults}
import pact4s.VerificationSettings.AnnotatedMethodVerificationSettings
import pact4s.messages.MessagesProvider

class MessagePactVerifierBrokerScalaTestSuite extends AnyFlatSpec with PactVerifier with BeforeAndAfterAll {
class MessagePactVerifierBrokerScalaTestSuite extends AnyFlatSpec with MessagePactVerifier with BeforeAndAfterAll {
lazy val mock = new MockProviderServer(3460)

def provider: ProviderInfoBuilder = mock.brokerProviderInfo(
providerName = "Pact4sMessageProvider",
verificationSettings = Some(AnnotatedMethodVerificationSettings(packagesToScan = List("pact4s.messages")))
)
def messages: ResponseFactory = MessagesProvider.messages
def provider: ProviderInfoBuilder = mock.brokerProviderInfo(providerName = "Pact4sMessageProvider")

it should "Verify pacts for provider `MessageProvider`" in {
verifyPacts(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package pact4s.scalatest

import org.scalatest.BeforeAndAfterAll
import pact4s.VerificationSettings.AnnotatedMethodVerificationSettings
import org.scalatest.flatspec.AnyFlatSpec
import pact4s.{MockProviderServer, ProviderInfoBuilder}
import pact4s.messages.MessagesProvider

class MessagePactVerifierScalaTestSuite extends AnyFlatSpec with PactVerifier with BeforeAndAfterAll {
class MessagePactVerifierScalaTestSuite extends AnyFlatSpec with MessagePactVerifier with BeforeAndAfterAll {
lazy val mock = new MockProviderServer(3458)

def messages: ResponseFactory = MessagesProvider.messages
def provider: ProviderInfoBuilder = mock.fileSourceProviderInfo(
consumerName = "Pact4sMessageConsumer",
providerName = "Pact4sMessageProvider",
fileName = "./scripts/Pact4sMessageConsumer-Pact4sMessageProvider.json",
verificationSettings = Some(AnnotatedMethodVerificationSettings(packagesToScan = List("pact4s.messages")))
fileName = "./scripts/Pact4sMessageConsumer-Pact4sMessageProvider.json"
)

it should "Verify pacts for provider `MessageProvider`" in {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package pact4s.scalatest

import au.com.dius.pact.consumer.{ConsumerPactBuilder, PactTestExecutionContext}
import au.com.dius.pact.core.model.RequestResponsePact
import au.com.dius.pact.core.model.{RequestResponseInteraction, RequestResponsePact}
import cats.effect.IO
import org.http4s.ember.client.EmberClientBuilder
import org.scalatest.flatspec.AnyFlatSpec
Expand All @@ -14,7 +14,9 @@ import org.http4s.{Header, Headers, MediaType, Method, Request, Uri}
import org.typelevel.ci.CIString
import pact4s.circe.implicits._
import org.http4s.circe._
import org.http4s.client.Client
import org.http4s.headers.`Content-Type`
import org.scalatest.Assertion

class RequestResponsePactForgerScalaTestSuite extends AnyFlatSpec with Matchers with RequestResponsePactForger {
override val pactTestExecutionContext: PactTestExecutionContext = new PactTestExecutionContext(
Expand Down Expand Up @@ -47,43 +49,45 @@ class RequestResponsePactForgerScalaTestSuite extends AnyFlatSpec with Matchers
.body(Json.obj("found" -> "bob".asJson))
.toPact()

val client = EmberClientBuilder.default[IO].build.allocated.unsafeRunSync()._1
val client: Client[IO] = EmberClientBuilder.default[IO].build.allocated.unsafeRunSync()._1

it should "scalatest pact test" in {
val request = Request[IO](
method = Method.POST,
uri = Uri.unsafeFromString(mockServer.getUrl + "/hello"),
headers = Headers(Header.Raw(CIString("other-header"), "howdy"))
)
.withEntity(Json.obj("name" -> "harry".asJson))
client
.run(request)
.use {
_.as[String].map(_ shouldBe "{\"hello\":\"harry\"}")
}
.unsafeRunSync()
def verify(interaction: RequestResponseInteraction): Assertion = interaction.getDescription match {
case "a request to say Hello" =>
val request = Request[IO](
method = Method.POST,
uri = Uri.unsafeFromString(mockServer.getUrl + "/hello"),
headers = Headers(Header.Raw(CIString("other-header"), "howdy"))
)
.withEntity(Json.obj("name" -> "harry".asJson))
client
.run(request)
.use {
_.as[String].map(_ shouldBe "{\"hello\":\"harry\"}")
}
.unsafeRunSync()
case "a request to say Goodbye" =>
val request = Request[IO](
uri = Uri.unsafeFromString(mockServer.getUrl + "/goodbye"),
headers = Headers(`Content-Type`(MediaType.application.json))
)
client
.run(request)
.use {
_.status.code.pure[IO].map(_ shouldBe 204)
}
.unsafeRunSync()
case "a request to find a friend" =>
val request = Request[IO](uri = Uri.unsafeFromString(mockServer.getUrl + "/anyone-there"))
client
.run(request)
.use {
_.as[String].map(_ shouldBe "{\"found\":\"bob\"}")
}
.unsafeRunSync()
case description => fail(s"Missing verification for message: '$description'.'")
}

it should "another scalatest pact test" in {
val request = Request[IO](
uri = Uri.unsafeFromString(mockServer.getUrl + "/goodbye"),
headers = Headers(`Content-Type`(MediaType.application.json))
)
client
.run(request)
.use {
_.status.code.pure[IO].map(_ shouldBe 204)
}
.unsafeRunSync()
}

it should "test with provider state" in {
val request = Request[IO](uri = Uri.unsafeFromString(mockServer.getUrl + "/anyone-there"))
client
.run(request)
.use {
_.as[String].map(_ shouldBe "{\"found\":\"bob\"}")
}
.unsafeRunSync()
interactions.foreach { interaction =>
it should s"forge: ${interaction.getDescription}" in verify(interaction)
}
}
44 changes: 0 additions & 44 deletions shared/src/main/scala/pact4s/MessageAndMetadataBuilder.scala

This file was deleted.

Loading

0 comments on commit 678ac8e

Please sign in to comment.