From e586656e43fed2134ecc74ae7d914aa2ecd9e61b Mon Sep 17 00:00:00 2001 From: Ronald Holshausen Date: Thu, 11 Jul 2024 16:55:57 +1000 Subject: [PATCH] feat(JUnit 4): Allow provider state generator to fall back to the provider state parameters --- .../pact/provider/junit/InteractionRunner.kt | 2 + .../junit/InteractionRunnerSpec.groovy | 59 +++++++++++++++++++ .../ProviderStateParametersInjectedTest.java | 47 +++++++++++++++ .../provider-state-parameter-injected.json | 49 +++++++++++++++ provider/junit5/README.md | 2 +- 5 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 provider/junit/src/test/java/au/com/dius/pact/provider/junit/ProviderStateParametersInjectedTest.java create mode 100644 provider/junit/src/test/resources/pacts/provider-state-parameter-injected.json diff --git a/provider/junit/src/main/kotlin/au/com/dius/pact/provider/junit/InteractionRunner.kt b/provider/junit/src/main/kotlin/au/com/dius/pact/provider/junit/InteractionRunner.kt index 2056387ac..016db1d01 100644 --- a/provider/junit/src/main/kotlin/au/com/dius/pact/provider/junit/InteractionRunner.kt +++ b/provider/junit/src/main/kotlin/au/com/dius/pact/provider/junit/InteractionRunner.kt @@ -302,6 +302,8 @@ open class InteractionRunner( return if (interaction.providerStates.isNotEmpty()) { var stateChange = statement for (state in interaction.providerStates.reversed()) { + testContext.putAll(state.params.filterValues { it != null } as Map) + val methods = findStateChangeMethod(state, testTarget.getStateHandlers()) if (methods.isEmpty()) { return if (ignoreMissingStateChangeMethod()) { diff --git a/provider/junit/src/test/groovy/au/com/dius/pact/provider/junit/InteractionRunnerSpec.groovy b/provider/junit/src/test/groovy/au/com/dius/pact/provider/junit/InteractionRunnerSpec.groovy index df2d9b3d1..8f18d5fcc 100644 --- a/provider/junit/src/test/groovy/au/com/dius/pact/provider/junit/InteractionRunnerSpec.groovy +++ b/provider/junit/src/test/groovy/au/com/dius/pact/provider/junit/InteractionRunnerSpec.groovy @@ -20,12 +20,14 @@ import au.com.dius.pact.provider.TestResultAccumulator import au.com.dius.pact.provider.VerificationReporter import au.com.dius.pact.provider.VerificationResult import au.com.dius.pact.provider.junit.target.HttpTarget +import au.com.dius.pact.provider.junitsupport.State import au.com.dius.pact.provider.junitsupport.target.Target import au.com.dius.pact.provider.junitsupport.target.TestTarget import junit.framework.AssertionFailedError import org.apache.commons.lang3.tuple.Pair import org.jetbrains.annotations.NotNull import org.junit.runner.notification.RunNotifier +import org.junit.runners.model.Statement import org.junit.runners.model.TestClass import spock.lang.Specification import spock.util.environment.RestoreSystemProperties @@ -459,4 +461,61 @@ class InteractionRunnerSpec extends Specification { 1 * notifier.fireTestIgnored({ it.displayName.startsWith('consumer - Upon Interaction 2 ') }) 0 * _ } + + @SuppressWarnings('PublicInstanceField') + static class StateChangeClazz { + @TestTarget + public final Target target = new MockTarget() + + @State('state 1') + Map state1() { + [a: 100, b: '200'] + } + } + + def 'withStateChanges copies any returned values to the test context'() { + given: + def interaction1 = new RequestResponseInteraction('Interaction 1', [ new ProviderState('state 1') ]) + def pact = new RequestResponsePact(new Provider(), new Consumer(), [ interaction1 ]) + def statement = [evaluate: { }] as Statement + def verifier = [:] as IProviderVerifier + def testTarget = [ + getStateHandlers: { [] }, + getVerifier: { verifier } + ] as Target + def stateChangeClazz = new TestClass(StateChangeClazz) + + def runner = new InteractionRunner(stateChangeClazz, pact, UnknownPactSource.INSTANCE) + + when: + def stateChangeStatement = runner.withStateChanges(interaction1, new StateChangeClazz(), statement, testTarget) + stateChangeStatement.evaluate() + + then: + runner.testContext == [a: 100, b: '200'] + } + + def 'withStateChanges falls back to provider state parameters'() { + given: + def interaction1 = new RequestResponseInteraction('Interaction 1', [ + new ProviderState('state 1', [b: 200, c: 'test']) + ]) + def pact = new RequestResponsePact(new Provider(), new Consumer(), [ interaction1 ]) + def statement = [evaluate: { }] as Statement + def verifier = [:] as IProviderVerifier + def testTarget = [ + getStateHandlers: { [] }, + getVerifier: { verifier } + ] as Target + def stateChangeClazz = new TestClass(StateChangeClazz) + + def runner = new InteractionRunner(stateChangeClazz, pact, UnknownPactSource.INSTANCE) + + when: + def stateChangeStatement = runner.withStateChanges(interaction1, new StateChangeClazz(), statement, testTarget) + stateChangeStatement.evaluate() + + then: + runner.testContext == [a: 100, b: '200', c: 'test'] + } } diff --git a/provider/junit/src/test/java/au/com/dius/pact/provider/junit/ProviderStateParametersInjectedTest.java b/provider/junit/src/test/java/au/com/dius/pact/provider/junit/ProviderStateParametersInjectedTest.java new file mode 100644 index 000000000..63a254933 --- /dev/null +++ b/provider/junit/src/test/java/au/com/dius/pact/provider/junit/ProviderStateParametersInjectedTest.java @@ -0,0 +1,47 @@ +package au.com.dius.pact.provider.junit; + +import au.com.dius.pact.provider.junit.target.HttpTarget; +import au.com.dius.pact.provider.junitsupport.Provider; +import au.com.dius.pact.provider.junitsupport.State; +import au.com.dius.pact.provider.junitsupport.loader.PactFolder; +import au.com.dius.pact.provider.junitsupport.target.Target; +import au.com.dius.pact.provider.junitsupport.target.TestTarget; +import com.github.restdriver.clientdriver.ClientDriverRule; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Map; + +import static com.github.restdriver.clientdriver.RestClientDriver.giveResponse; +import static com.github.restdriver.clientdriver.RestClientDriver.onRequestTo; + +@Provider("ProviderStateParametersInjected") +@PactFolder("pacts") +@RunWith(PactRunner.class) +public class ProviderStateParametersInjectedTest { + private static final Logger LOGGER = LoggerFactory.getLogger(ProviderStateParametersInjectedTest.class); + + @ClassRule + public static final ClientDriverRule embeddedService = new ClientDriverRule(9241); + + @TestTarget + public final Target target = new HttpTarget(9241); + + @Before + public void before() { + embeddedService.addExpectation( + onRequestTo("/api/hello/John"), + giveResponse("{\"name\": \"John\"}", "application/json") + ); + } + + @State("User exists") + public Map defaultState(Map params) { + LOGGER.debug("Provider state params = " + params); + return Collections.emptyMap(); + } +} diff --git a/provider/junit/src/test/resources/pacts/provider-state-parameter-injected.json b/provider/junit/src/test/resources/pacts/provider-state-parameter-injected.json new file mode 100644 index 000000000..9c61a4f17 --- /dev/null +++ b/provider/junit/src/test/resources/pacts/provider-state-parameter-injected.json @@ -0,0 +1,49 @@ +{ + "consumer": { + "name": "SomeConsumer" + }, + "interactions": [ + { + "description": "Hello John", + "providerStates": [ + { + "name": "User exists", + "params": { + "name": "John" + } + } + ], + "request": { + "generators": { + "path": { + "dataType": "STRING", + "expression": "/api/hello/${name}", + "type": "ProviderState" + } + }, + "method": "GET", + "path": "/api/hello/James" + }, + "response": { + "body": { + "name": "John" + }, + "headers": { + "Content-Type": "application/json" + }, + "status": 200 + } + } + ], + "metadata": { + "pact-jvm": { + "version": "4.6.7" + }, + "pactSpecification": { + "version": "3.0.0" + } + }, + "provider": { + "name": "ProviderStateParametersInjected" + } +} diff --git a/provider/junit5/README.md b/provider/junit5/README.md index 9c59fa606..f921c9da0 100644 --- a/provider/junit5/README.md +++ b/provider/junit5/README.md @@ -6,7 +6,7 @@ The library is available on maven central using: * group-id = `au.com.dius.pact.provider` * artifact-id = `junit5` -* version-id = `4.3.x` +* version-id = `4.6.x` ## Overview