Skip to content

Commit

Permalink
feat(compatibility-suite): Implemented V3 features
Browse files Browse the repository at this point in the history
  • Loading branch information
rholshausen committed Jul 19, 2023
1 parent b628e96 commit 88c023f
Show file tree
Hide file tree
Showing 8 changed files with 375 additions and 30 deletions.
27 changes: 27 additions & 0 deletions compatibility-suite/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,30 @@ tasks.register('v2') {
}
}
}

tasks.register('v3') {
dependsOn assemble, testClasses
doLast {
def cucumberArgs = [
'--plugin', 'pretty',
'--plugin', 'html:build/cucumber-report-v3.html',
'--glue', 'steps.shared',
'--glue', 'steps.v3',
'pact-compatibility-suite/features/V3'
]
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
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import static io.ktor.http.HttpHeaderValueParserKt.parseHeaderValue
import static steps.shared.SharedSteps.configureBody

@SuppressWarnings(['ThrowRuntimeException', 'AbcMetric'])
class HttpProvider {
class SharedHttpProvider {
CompatibilitySuiteWorld world
BaseMockServer mockProvider
ProviderInfo providerInfo
Expand All @@ -54,7 +54,7 @@ class HttpProvider {
Map<String, String> verificationProperties = [:]
List providerStateParams = []

HttpProvider(CompatibilitySuiteWorld world) {
SharedHttpProvider(CompatibilitySuiteWorld world) {
this.world = world
}

Expand Down
60 changes: 58 additions & 2 deletions compatibility-suite/src/test/groovy/steps/v2/HttpConsumer.groovy
Original file line number Diff line number Diff line change
@@ -1,12 +1,68 @@
package steps.v2

import au.com.dius.pact.consumer.model.MockProviderConfig
import au.com.dius.pact.core.model.Consumer
import au.com.dius.pact.core.model.HeaderParser
import au.com.dius.pact.core.model.Provider
import au.com.dius.pact.core.model.RequestResponsePact
import io.cucumber.datatable.DataTable
import io.cucumber.java.en.When
import steps.shared.CompatibilitySuiteWorld
import steps.shared.MockServerData

import static au.com.dius.pact.consumer.MockHttpServerKt.mockServer
import static au.com.dius.pact.core.model.PactReaderKt.queryStringToMap
import static io.ktor.http.HttpHeaderValueParserKt.parseHeaderValue
import static steps.shared.SharedSteps.configureBody

class HttpConsumer {
CompatibilitySuiteWorld world
MockServerData mockServerData

HttpConsumer(CompatibilitySuiteWorld world) {
HttpConsumer(CompatibilitySuiteWorld world, MockServerData mockServerData) {
this.mockServerData = mockServerData
this.world = world
}
}

@When('the mock server is started with interaction {int} but with the following changes:')
void the_mock_server_is_started_with_interaction_but_with_the_following_changes(Integer num, DataTable dataTable) {
def interaction = world.interactions[num - 1]
def entry = dataTable.entries().first()
if (entry['method']) {
interaction.request.method = entry['method']
}

if (entry['path']) {
interaction.request.path = entry['path']
}

if (entry['query']) {
interaction.request.query = queryStringToMap(entry['query'])
}

if (entry['headers']) {
interaction.request.headers = entry['headers'].split(',').collect {
it.trim()[1..-2].split(':')
}.collect {
[it[0].trim(), parseHeaderValue(it[1].trim()).collect { HeaderParser.INSTANCE.hvToString(it) }]
}.inject([:]) { acc, e ->
if (acc.containsKey(e[0])) {
acc[e[0]] += e[1].flatten()
} else {
acc[e[0]] = e[1].flatten()
}
acc
}
}

if (entry['body']) {
configureBody(entry['body'], interaction.request)
}

mockServerData.pact = new RequestResponsePact(new Provider('p'),
new Consumer('v1-compatibility-suite-c'), [interaction])
mockServerData.config = new MockProviderConfig()
mockServerData.mockServer = mockServer(mockServerData.pact, mockServerData.config)
mockServerData.mockServer.start()
}
}
11 changes: 0 additions & 11 deletions compatibility-suite/src/test/groovy/steps/v2/HttpProvider.groovy

This file was deleted.

75 changes: 75 additions & 0 deletions compatibility-suite/src/test/groovy/steps/v3/HttpConsumer.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package steps.v3

import au.com.dius.pact.consumer.ConsumerPactBuilder
import au.com.dius.pact.core.model.PactSpecVersion
import au.com.dius.pact.core.model.RequestResponsePact
import au.com.dius.pact.core.support.Json
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

class HttpConsumer {
def builder
RequestResponsePact pact
String pactJsonStr
JsonValue.Object pactJson

@Given('an integration is being defined for a consumer test')
void an_integration_is_being_defined_for_a_consumer_test() {
builder = ConsumerPactBuilder
.consumer('V3 consumer')
.hasPactWith('V3 provider')
}

@Given('a provider state {string} is specified')
void a_provider_state_is_specified(String state) {
builder = builder.given(state)
}

@Given('a provider state {string} is specified with the following data:')
void a_provider_state_is_specified_with_the_following_data(String state, DataTable dataTable) {
def entry = dataTable.entries()
.first()
.collectEntries {
[it.key, JsonParser.parseString(it.value).unwrap()]
}
builder = builder.given(state, entry)
}


@When('the Pact file for the test is generated')
void the_pact_file_for_the_test_is_generated() {
pact = builder.uponReceiving('some request')
.path('/path')
.willRespondWith()
.toPact()
pactJsonStr = Json.INSTANCE.prettyPrint(pact.toMap(PactSpecVersion.V3))
pactJson = JsonParser.parseString(pactJsonStr).asObject()
}

@Then('the interaction in the Pact file will contain {int} provider state(s)')
void the_interaction_in_the_pact_file_will_contain_provider_states(Integer states) {
JsonValue.Object interaction = pactJson['interactions'].asArray().get(0).asObject()
JsonValue.Array providerStates = interaction['providerStates'].asArray()
assert providerStates.size() == states
}

@Then('the interaction in the Pact file will contain provider state {string}')
void the_interaction_in_the_pact_file_will_contain_provider_state(String state) {
JsonValue.Object interaction = pactJson['interactions'].asArray().get(0).asObject()
JsonValue.Array providerStates = interaction['providerStates'].asArray()
assert providerStates.values.find { it.get('name').toString() == state } != null
}

@Then("the provider state {string} in the Pact file will contain the following parameters:")
void the_provider_state_in_the_pact_file_will_contain_the_following_parameters(String state, DataTable dataTable) {
def entry = dataTable.entries().first()['parameters']
JsonValue.Object interaction = pactJson['interactions'].asArray().get(0).asObject()
JsonValue.Array providerStates = interaction['providerStates'].asArray()
def providerState = providerStates.values.find { it.get('name').toString() == state }
assert providerState.get('params').toString() == entry
}
}
112 changes: 112 additions & 0 deletions compatibility-suite/src/test/groovy/steps/v3/HttpMatching.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package steps.v3

import au.com.dius.pact.core.matchers.BodyMismatch
import au.com.dius.pact.core.matchers.HeaderMismatch
import au.com.dius.pact.core.matchers.RequestMatchResult
import au.com.dius.pact.core.model.HeaderParser
import au.com.dius.pact.core.model.Request
import au.com.dius.pact.core.model.matchingrules.MatchingRulesImpl
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 io.ktor.http.HttpHeaderValueParserKt.parseHeaderValue
import static steps.shared.SharedSteps.configureBody

class HttpMatching {
Request expectedRequest
List<Request> receivedRequests = []
List<RequestMatchResult> results = []

@Given('an expected request with a(n) {string} header of {string}')
void an_expected_request_with_a_header_of(String header, String value) {
expectedRequest = new Request()
expectedRequest.headers[header] = parseHeaderValue(value).collect { HeaderParser.INSTANCE.hvToString(it) }
}

@Given('a request is received with a(n) {string} header of {string}')
void a_request_is_received_with_a_header_of(String header, String value) {
receivedRequests << new Request()
receivedRequests[0].headers[header] = parseHeaderValue(value).collect { HeaderParser.INSTANCE.hvToString(it) }
}

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

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

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

@Given('the following requests are received:')
void the_following_requests_are_received(DataTable dataTable) {
for (entry in dataTable.entries()) {
def request = new Request()
if (entry['body']) {
configureBody(entry['body'], request)
}
receivedRequests << request
}
}

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

@When('the requests are compared to the expected one')
void the_requests_are_compared_to_the_expected_one() {
results.addAll(receivedRequests.collect {requestMismatches(expectedRequest, it) })
}

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

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

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

0 comments on commit 88c023f

Please sign in to comment.