Skip to content

Commit

Permalink
feat: Implemented scenarios for no provider state callback configured…
Browse files Browse the repository at this point in the history
… + request filters
  • Loading branch information
rholshausen committed Jun 15, 2023
1 parent d8c196e commit 0cd9ad0
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 3 deletions.
62 changes: 62 additions & 0 deletions compatibility-suite/src/test/groovy/steps/v1/HttpProvider.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@ import au.com.dius.pact.provider.ProviderInfo
import au.com.dius.pact.provider.ProviderVerifier
import au.com.dius.pact.provider.VerificationResult
import groovy.json.JsonSlurper
import io.cucumber.datatable.DataTable
import io.cucumber.java.After
import io.cucumber.java.Scenario
import io.cucumber.java.en.Given
import io.cucumber.java.en.Then
import io.cucumber.java.en.When
import org.apache.hc.core5.http.ClassicHttpRequest
import org.apache.hc.core5.http.io.entity.StringEntity

@SuppressWarnings('ThrowRuntimeException')
class HttpProvider {
Expand Down Expand Up @@ -117,6 +120,7 @@ class HttpProvider {
verifier = new ProviderVerifier()
verifier.projectHasProperty = { name -> verificationProperties.containsKey(name) }
verifier.projectGetProperty = { name -> verificationProperties[name] }
verifier.reporters = [ new StubVerificationReporter() ]
verificationResults = verifier.verifyProvider(providerInfo)
}

Expand Down Expand Up @@ -263,4 +267,62 @@ class HttpProvider {
void the_provider_state_callback_will_not_receive_a_teardown_call() {
assert providerStateParams.findAll { p -> p[1] == 'teardown' }.empty
}

@Then('a warning will be displayed that there was no provider state callback configured for provider state {string}')
void a_warning_will_be_displayed_that_there_was_no_provider_state_callback_configured(String state) {
assert verifier.reporters.first().events.find { it.state == state }
}

@Given('a request filter is configured to make the following changes:')
void a_request_filter_is_configured_to_make_the_following_changes(DataTable dataTable) {
providerInfo.requestFilter = { ClassicHttpRequest request ->
def entry = dataTable.entries().first()
if (entry['path']) {
request.path = entry['path']
}

if (entry['headers']) {
entry['headers'].split(',').collect {
it.trim()[1..-2].split(':', 2)
}.collectEntries {
Map.entry(it[0].trim(), it[1].trim())
}.each {
request.addHeader(it.key.toString(), it.value)
}
}

if (entry['body']) {
if (entry['body'].startsWith('JSON:')) {
request.addHeader('content-type', 'application/json')
def ct = new org.apache.hc.core5.http.ContentType('application/json', null)
request.entity = new StringEntity(entry['body'][5..-1], ct)
} else if (entry['body'].startsWith('XML:')) {
request.addHeader('content-type', 'application/xml')
def ct = new org.apache.hc.core5.http.ContentType('application/xml', null)
request.entity = new StringEntity(entry['body'][4..-1], ct)
} else {
String contentType = 'text/plain'
if (entry['body'].endsWith('.json')) {
contentType = 'application/json'
} else if (entry['body'].endsWith('.xml')) {
contentType = 'application/xml'
}
request.addHeader('content-type', contentType)
File contents = new File("pact-compatibility-suite/fixtures/${entry['body']}")
contents.withInputStream {
def ct = new org.apache.hc.core5.http.ContentType(contentType, null)
request.entity = new StringEntity(it.text, ct)
}
}
}
}
}

@Then('the request to the provider will contain the header {string}')
void the_request_to_the_provider_will_contain_the_header(String header) {
def h = header.split(':\\s+', 2)
assert mockProvider.matchedRequests.every {
it.second.headers.containsKey(h[0]) && it.second.headers[h[0]][0] == h[1]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package steps.v1

import au.com.dius.pact.core.model.Interaction
import au.com.dius.pact.core.model.Pact
import au.com.dius.pact.core.model.PactSource
import au.com.dius.pact.core.model.UrlPactSource
import au.com.dius.pact.provider.IConsumerInfo
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

@SuppressWarnings('GetterMethodCouldBeProperty')
class StubVerificationReporter extends BaseVerifierReporter {
List<Map<String, Object>> events = []

@Override
String getExt() { null }

@Override
File getReportDir() { null }

@Override
void setReportDir(File file) { }

@Override
File getReportFile() { null }

@Override
void setReportFile(File file) { }

@Override
IProviderVerifier getVerifier() { null }

@Override
void setVerifier(IProviderVerifier iProviderVerifier) { }

@Override
void initialise(IProviderInfo provider) { }

@Override
void finaliseReport() { }

@Override
void reportVerificationForConsumer(IConsumerInfo consumer, IProviderInfo provider, String tag) { }

@Override
void verifyConsumerFromUrl(UrlPactSource pactUrl, IConsumerInfo consumer) { }

@Override
void verifyConsumerFromFile(PactSource pactFile, IConsumerInfo consumer) { }

@Override
void pactLoadFailureForConsumer(IConsumerInfo consumer, String message) { }

@Override
void warnProviderHasNoConsumers(IProviderInfo provider) { }

@Override
void warnPactFileHasNoInteractions(Pact pact) { }

@Override
void interactionDescription(Interaction interaction) { }

@Override
void stateForInteraction(String state, IProviderInfo provider, IConsumerInfo consumer, boolean isSetup) { }

@Override
void warnStateChangeIgnored(String state, IProviderInfo provider, IConsumerInfo consumer) {
events << [state: state, provider: provider, consumer: consumer]
}

@Override
void stateChangeRequestFailedWithException(String state, boolean isSetup, Exception e, boolean printStackTrace) { }

@Override
void stateChangeRequestFailed(String state, IProviderInfo provider, boolean isSetup, String httpStatus) { }

@Override
void warnStateChangeIgnoredDueToInvalidUrl(String s, IProviderInfo p, boolean isSetup, Object stateChangeHandler) { }

@Override
void requestFailed(IProviderInfo p, Interaction i, String message, Exception e, boolean printStackTrace) { }

@Override
void returnsAResponseWhich() { }

@Override
void statusComparisonOk(int status) { }

@Override
void statusComparisonFailed(int status, Object comparison) { }

@Override
void includesHeaders() { }

@Override
void headerComparisonOk(String key, List<String> value) { }

@Override
void headerComparisonFailed(String key, List<String> value, Object comparison) { }

@Override
void bodyComparisonOk() { }

@Override
void bodyComparisonFailed(Object comparison) { }

@Override
void errorHasNoAnnotatedMethodsFoundForInteraction(Interaction interaction) { }

@Override
void verificationFailed(Interaction interaction, Exception e, boolean printStackTrace) { }

@Override
void generatesAMessageWhich() { }

@Override
void displayFailures(Map<String, ?> failures) { }

@Override
void displayFailures(List<VerificationResult.Failed> failures) { }

@Override
void includesMetadata() { }

@Override
void metadataComparisonOk() { }

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

@Override
void metadataComparisonFailed(String key, Object value, Object comparison) { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ abstract class AbstractBaseMockServer : MockServer {
abstract class BaseMockServer(val pact: BasePact, val config: MockProviderConfig) : AbstractBaseMockServer() {

val mismatchedRequests = ConcurrentHashMap<IRequest, MutableList<PactVerificationResult>>()
val matchedRequests = ConcurrentLinkedQueue<IRequest>()
val matchedRequests = ConcurrentLinkedQueue<Pair<IRequest, IRequest>>()
private val requestMatcher = RequestMatching(pact)

override fun waitForServer() {
Expand Down Expand Up @@ -184,10 +184,11 @@ abstract class BaseMockServer(val pact: BasePact, val config: MockProviderConfig
if (mismatchedRequests.isNotEmpty()) {
return PactVerificationResult.Mismatches(mismatchedRequests.values.flatten())
}
val receivedRequests = matchedRequests.map { it.first }
val expectedRequests = pact.interactions.asSequence()
.filter { it.isSynchronousRequestResponse() }
.map { it.asSynchronousRequestResponse()!!.request }
.filter { !matchedRequests.contains(it) }
.filter { !receivedRequests.contains(it) }
.toList()
if (expectedRequests.isNotEmpty()) {
return PactVerificationResult.ExpectedButNotReceived(expectedRequests)
Expand All @@ -199,7 +200,7 @@ abstract class BaseMockServer(val pact: BasePact, val config: MockProviderConfig
when (val matchResult = requestMatcher.matchInteraction(request)) {
is FullRequestMatch -> {
val interaction = matchResult.interaction
matchedRequests.add(interaction.request)
matchedRequests.add(interaction.request to request)
return DefaultResponseGenerator.generateResponse(interaction.response,
mutableMapOf(
"mockServer" to mapOf("href" to getUrl(), "port" to getPort()),
Expand Down

0 comments on commit 0cd9ad0

Please sign in to comment.