Skip to content

Commit

Permalink
feat(compatibility-suite): Implement initial V4 features
Browse files Browse the repository at this point in the history
  • Loading branch information
rholshausen committed Aug 10, 2023
1 parent 1524395 commit c1964a1
Show file tree
Hide file tree
Showing 18 changed files with 569 additions and 13 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/compatability-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,20 @@ jobs:
name: cucumber-report
path: compatibility-suite/build/cucumber-report-v3.html
if: always()
v4:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 18
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 18
- name: Build with Gradle
run: ./gradlew --no-daemon :compatibility-suite:v4
- name: Archive cucumber results
uses: actions/upload-artifact@v3
with:
name: cucumber-report
path: compatibility-suite/build/cucumber-report-v4.html
if: always()
25 changes: 24 additions & 1 deletion compatibility-suite/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,29 @@ tasks.register('v3') {
}
}

tasks.register('v4') {
dependsOn assemble, testClasses
doLast {
def cucumberArgs = [
'--plugin', 'pretty',
'--plugin', 'html:build/cucumber-report-v4.html',
'--glue', 'steps.shared',
'--glue', 'steps.v4',
'pact-compatibility-suite/features/V4'
]
if (project.hasProperty('cucumber.filter.tags')) {
cucumberArgs.add(0, project.property('cucumber.filter.tags'))
cucumberArgs.add(0, '-t')
}
javaexec {
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = cucumberArgs
systemProperty 'pact_do_not_track', 'true'
}
}
}

tasks.register('all') {
dependsOn v1, v2, v3
dependsOn v1, v2, v3, v4
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import au.com.dius.pact.provider.IProviderInfo
import au.com.dius.pact.provider.IProviderVerifier
import au.com.dius.pact.provider.VerificationResult
import au.com.dius.pact.provider.reporters.BaseVerifierReporter
import au.com.dius.pact.provider.reporters.Event
import org.jetbrains.annotations.NotNull

@SuppressWarnings('GetterMethodCouldBeProperty')
class StubVerificationReporter extends BaseVerifierReporter {
Expand Down Expand Up @@ -132,4 +134,15 @@ class StubVerificationReporter extends BaseVerifierReporter {

@Override
void metadataComparisonFailed(String key, Object value, Object comparison) { }

@Override
void receive(@NotNull Event event) {
switch (event) {
case Event.DisplayInteractionComments:
events << [comments: event.comments]
break
default:
super.receive(event)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,16 @@ class VerificationSteps {
@Then('the verification will be successful')
void the_verification_will_be_successful() {
assert verificationData.verificationResults.inject(true) { acc, result ->
acc && result instanceof VerificationResult.Ok
acc && (result instanceof VerificationResult.Ok ||
(result instanceof VerificationResult.Failed && result.pending))
}
}

@Then('the verification will NOT be successful')
void the_verification_will_not_be_successful() {
assert verificationData.verificationResults.any { it instanceof VerificationResult.Failed }
assert verificationData.verificationResults.any {
it instanceof VerificationResult.Failed && !it.pending
}
}

@Then('the verification results will contain a {string} error')
Expand Down
52 changes: 52 additions & 0 deletions compatibility-suite/src/test/groovy/steps/v4/HttpConsumer.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package steps.v4

import au.com.dius.pact.consumer.dsl.HttpInteractionBuilder
import io.cucumber.java.After
import io.cucumber.java.Before
import io.cucumber.java.Scenario
import io.cucumber.java.en.Given

class HttpConsumer {
HttpInteractionBuilder httpBuilder
SharedV4PactData v4Data

HttpConsumer(SharedV4PactData v4Data) {
this.v4Data = v4Data
}

@Before
void before(Scenario scenario) {
v4Data.scenarioId = scenario.id
}

@After
void after(Scenario scenario) {
if (!scenario.failed) {
def dir = "build/compatibility-suite/v4/${v4Data.scenarioId}" as File
dir.deleteDir()
}
}

@Given('an HTTP interaction is being defined for a consumer test')
void an_http_interaction_is_being_defined_for_a_consumer_test() {
httpBuilder = new HttpInteractionBuilder('HTTP interaction', [], [])
v4Data.builderCallbacks << {
httpBuilder.build()
}
}

@Given('a key of {string} is specified for the HTTP interaction')
void a_key_of_is_specified(String key) {
httpBuilder.key(key)
}

@Given('the HTTP interaction is marked as pending')
void the_interaction_is_marked_as_pending() {
httpBuilder.pending(true)
}

@Given('a comment {string} is added to the HTTP interaction')
void a_comment_is_added(String value) {
httpBuilder.comment(value)
}
}
151 changes: 151 additions & 0 deletions compatibility-suite/src/test/groovy/steps/v4/HttpMatching.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package steps.v4

import au.com.dius.pact.core.matchers.BodyMismatch
import au.com.dius.pact.core.matchers.HeaderMismatch
import au.com.dius.pact.core.matchers.Mismatch
import au.com.dius.pact.core.matchers.RequestMatchResult
import au.com.dius.pact.core.model.HttpRequest
import au.com.dius.pact.core.model.HttpResponse
import au.com.dius.pact.core.support.json.JsonParser
import au.com.dius.pact.core.support.json.JsonValue
import io.cucumber.datatable.DataTable
import io.cucumber.java.en.Given
import io.cucumber.java.en.Then
import io.cucumber.java.en.When

import static au.com.dius.pact.core.matchers.RequestMatching.requestMismatches
import static au.com.dius.pact.core.matchers.ResponseMatching.responseMismatches
import static steps.shared.SharedSteps.configureBody
import static steps.shared.SharedSteps.determineContentType

class HttpMatching {
HttpRequest expectedRequest
List<HttpRequest> receivedRequests = []
HttpResponse expectedResponse
List<HttpResponse> receivedResponses = []
List<Mismatch> responseResults = []
List<RequestMatchResult> requestResults = []

@Given('an expected response configured with the following:')
void an_expected_response_configured_with_the_following(DataTable dataTable) {
expectedResponse = new HttpResponse()
def entry = dataTable.entries().first()

if (entry['status']) {
expectedResponse.status = entry['status'].toInteger()
}

if (entry['body']) {
def part = configureBody(entry['body'], determineContentType(entry['body'], expectedResponse.contentTypeHeader()))
expectedResponse.body = part.body
expectedResponse.headers.putAll(part.headers)
}

if (entry['matching rules']) {
JsonValue json
if (entry['matching rules'].startsWith('JSON:')) {
json = JsonParser.INSTANCE.parseString(entry['matching rules'][5..-1])
} else {
File contents = new File("pact-compatibility-suite/fixtures/${entry['matching rules']}")
contents.withInputStream {
json = JsonParser.INSTANCE.parseStream(it)
}
}
expectedResponse.matchingRules.fromV3Json(json)
}
}

@Given('a status {int} response is received')
void a_status_response_is_received(Integer status) {
receivedResponses << new HttpResponse(status)
}

@When('the response is compared to the expected one')
void the_response_is_compared_to_the_expected_one() {
responseResults.addAll(responseMismatches(expectedResponse, receivedResponses[0]))
}

@Then('the response comparison should be OK')
void the_response_comparison_should_be_ok() {
assert responseResults.empty
}

@Then('the response comparison should NOT be OK')
void the_response_comparison_should_not_be_ok() {
assert !responseResults.empty
}

@Then('the response mismatches will contain a {string} mismatch with error {string}')
void the_response_mismatches_will_contain_a_mismatch_with_error(String type, String error) {
assert responseResults.find {
it.type() == type && it.description() == error
}
}

@Given('an expected request configured with the following:')
void an_expected_request_configured_with_the_following(DataTable dataTable) {
expectedRequest = new HttpRequest()
def entry = dataTable.entries().first()
if (entry['body']) {
def part = configureBody(entry['body'], determineContentType(entry['body'], expectedRequest.contentTypeHeader()))
expectedRequest.body = part.body
expectedRequest.headers.putAll(part.headers)
}

if (entry['matching rules']) {
JsonValue json
if (entry['matching rules'].startsWith('JSON:')) {
json = JsonParser.INSTANCE.parseString(entry['matching rules'][5..-1])
} else {
File contents = new File("pact-compatibility-suite/fixtures/${entry['matching rules']}")
contents.withInputStream {
json = JsonParser.INSTANCE.parseStream(it)
}
}
expectedRequest.matchingRules.fromV3Json(json)
}
}

@Given('a request is received with the following:')
void a_request_is_received_with_the_following(DataTable dataTable) {
receivedRequests << new HttpRequest()
def entry = dataTable.entries().first()
if (entry['body']) {
def part = configureBody(entry['body'], determineContentType(entry['body'],
receivedRequests[0].contentTypeHeader()))
receivedRequests[0].body = part.body
receivedRequests[0].headers.putAll(part.headers)
}
}

@When('the request is compared to the expected one')
void the_request_is_compared_to_the_expected_one() {
requestResults << requestMismatches(expectedRequest, receivedRequests[0])
}

@Then('the comparison should be OK')
void the_comparison_should_be_ok() {
assert requestResults.every { it.mismatches.empty }
}

@Then('the comparison should NOT be OK')
void the_comparison_should_not_be_ok() {
assert requestResults.any { !it.mismatches.empty }
}

@Then('the mismatches will contain a mismatch with error {string} -> {string}')
@SuppressWarnings('SpaceAfterOpeningBrace')
void the_mismatches_will_contain_a_mismatch_with_error(String path, String error) {
assert requestResults.any {
it.mismatches.find {
def pathMatches = switch (it) {
case HeaderMismatch -> it.headerKey == path
case BodyMismatch -> it.path == path
default -> false
}
def desc = it.description()
desc.contains(error) && pathMatches
} != null
}
}
}
Loading

0 comments on commit c1964a1

Please sign in to comment.