
au.com.dius.pact.consumer.PactProviderRule Maven / Gradle / Ivy
package au.com.dius.pact.consumer;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.model.MockHttpsKeystoreProviderConfig;
import au.com.dius.pact.model.MockHttpsProviderConfig;
import au.com.dius.pact.model.MockProviderConfig;
import au.com.dius.pact.model.PactFragment;
import au.com.dius.pact.model.PactSpecVersion;
import org.apache.commons.lang3.StringUtils;
import org.junit.rules.ExternalResource;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.lang.reflect.Method;
import java.net.SocketException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* A junit rule that wraps every test annotated with {@link PactVerification}.
* Before each test, a mock server will be setup at given port/host that will provide mocked responses for the given
* provider. After each test, it will be teared down.
*
* If no host is given, it will default to localhost. If no port is given, it will default to a random port.
*
* @deprecated Use PactProviderRuleMk2 or PactHttpsProviderRuleMk2 instead
*/
@Deprecated
public class PactProviderRule extends ExternalResource {
private static final VerificationResult PACT_VERIFIED = PactVerified$.MODULE$;
private final String provider;
private final Object target;
private MockProviderConfig config;
private Map fragments;
/**
* Creates a mock provider by the given name
* @param provider Provider name to mock
* @param host Host to bind to. Defaults to localhost
* @param port Port to bind to. Defaults to a random port.
* @param pactVersion Pact specification version
* @param target Target test to apply this rule to.
*/
public PactProviderRule(String provider, String host, Integer port, PactSpecVersion pactVersion, Object target) {
this.provider = provider;
this.target = target;
if (host == null && port == null) {
config = MockProviderConfig.createDefault(pactVersion);
} else {
config = MockProviderConfig.httpConfig(host, port, pactVersion);
}
}
/**
* Creates a mock provider by the given name
* @param provider Provider name to mock
* @param host Host to bind to. Defaults to localhost
* @param port Port to bind to. Defaults to a random port.
* @param https Boolean flag to control starting HTTPS or HTTP mock server
* @param pactVersion Pact specification version
* @param target Target test to apply this rule to.
*/
public PactProviderRule(String provider, String host, Integer port, boolean https, PactSpecVersion pactVersion,
Object target) {
this(provider, host, port, pactVersion, target);
if (https) {
config = MockHttpsProviderConfig.httpsConfig(host, port, pactVersion);
}
}
/**
* Creates a mock provider by the given name
* @param provider Provider name to mock
* @param host Host to bind to. Defaults to localhost
* @param port Port to bind to. Defaults to a random port.
* @param https Boolean flag to control starting HTTPS or HTTP mock server
* @param keystore Path to keystore, example: /path/to/keystore.jks
* @param password Password for the keystore.
* @param pactVersion Pact specification version
* @param target Target test to apply this rule to.
*/
public PactProviderRule(String provider, String host, Integer port, boolean https, String keystore, String password, PactSpecVersion pactVersion,
Object target) {
this(provider, host, port, pactVersion, target);
if (https) {
config = MockHttpsKeystoreProviderConfig.httpsKeystoreConfig(host, port, keystore, password, pactVersion);
}
}
/**
* Creates a mock provider by the given name
* @param provider Provider name to mock
* @param host Host to bind to. Defaults to localhost
* @param port Port to bind to. Defaults to a random port.
* @param target Target test to apply this rule to.
*/
public PactProviderRule(String provider, String host, Integer port, Object target) {
this(provider, host, port, PactSpecVersion.V3, target);
}
/**
* Creates a mock provider by the given name. Binds to localhost and a random port.
* @param provider Provider name to mock
* @param target Target test to apply this rule to.
*/
public PactProviderRule(String provider, Object target) {
this(provider, null, null, PactSpecVersion.V3, target);
}
/**
* Creates a mock provider by the given name. Binds to localhost and a random port.
* @param provider Provider name to mock
* @param target Target test to apply this rule to.
*/
public PactProviderRule(String provider, PactSpecVersion pactSpecVersion, Object target) {
this(provider, null, null, pactSpecVersion, target);
}
public MockProviderConfig getConfig() {
return config;
}
@Override
public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
PactVerifications pactVerifications = description.getAnnotation(PactVerifications.class);
if (pactVerifications != null) {
evaluatePactVerifications(pactVerifications, base);
return;
}
PactVerification pactDef = description.getAnnotation(PactVerification.class);
// no pactVerification? execute the test normally
if (pactDef == null) {
base.evaluate();
return;
}
Map pacts = getPacts(pactDef.fragment());
Optional fragment;
if (pactDef.value().length == 1 && StringUtils.isEmpty(pactDef.value()[0])) {
fragment = pacts.values().stream().findFirst();
} else {
fragment = Arrays.asList(pactDef.value()).stream().map(pacts::get)
.filter(p -> p != null).findFirst();
}
if (!fragment.isPresent()) {
base.evaluate();
return;
}
VerificationResult result = runPactTest(base, fragment.get());
validateResult(result, pactDef);
}
};
}
private void evaluatePactVerifications(PactVerifications pactVerifications, Statement base) throws Throwable {
Optional possiblePactVerification = findPactVerification(pactVerifications);
if (!possiblePactVerification.isPresent()) {
base.evaluate();
return;
}
PactVerification pactVerification = possiblePactVerification.get();
Optional possiblePactMethod = findPactMethod(pactVerification);
if (!possiblePactMethod.isPresent()) {
throw new UnsupportedOperationException("Could not find method with @Pact for the provider " + provider);
}
Method method = possiblePactMethod.get();
Pact pact = method.getAnnotation(Pact.class);
PactDslWithProvider dslBuilder = ConsumerPactBuilder.consumer(pact.consumer()).hasPactWith(provider);
PactFragment pactFragment;
try {
pactFragment = (PactFragment) method.invoke(target, dslBuilder);
} catch (Exception e) {
throw new RuntimeException("Failed to invoke pact method", e);
}
VerificationResult result = runPactTest(base, pactFragment);
validateResult(result, pactVerification);
}
private Optional findPactVerification(PactVerifications pactVerifications) {
PactVerification[] pactVerificationValues = pactVerifications.value();
return Arrays.stream(pactVerificationValues).filter(p -> {
String[] providers = p.value();
if (providers.length != 1) {
throw new IllegalArgumentException(
"Each @PactVerification must specify one and only provider when using @PactVerifications");
}
String provider = providers[0];
return provider.equals(this.provider);
}).findFirst();
}
private Optional findPactMethod(PactVerification pactVerification) {
String pactFragment = pactVerification.fragment();
for (Method method : target.getClass().getMethods()) {
Pact pact = method.getAnnotation(Pact.class);
if (pact != null && pact.provider().equals(provider)
&& (pactFragment.isEmpty() || pactFragment.equals(method.getName()))) {
validatePactSignature(method);
return Optional.of(method);
}
}
return Optional.empty();
}
private void validatePactSignature(Method method) {
boolean hasValidPactSignature =
PactFragment.class.isAssignableFrom(method.getReturnType())
&& method.getParameterTypes().length == 1
&& method.getParameterTypes()[0].isAssignableFrom(PactDslWithProvider.class);
if (!hasValidPactSignature) {
throw new UnsupportedOperationException("Method " + method.getName() +
" does not conform required method signature 'public PactFragment xxx(PactDslWithProvider builder)'");
}
}
private VerificationResult runPactTest(final Statement base, PactFragment pactFragment) {
return pactFragment.runConsumer(config, new TestRun() {
@Override
public void run(MockProviderConfig config) throws Throwable {
base.evaluate();
}
});
}
private void validateResult(VerificationResult result, PactVerification pactVerification) throws Throwable {
if (!result.equals(PACT_VERIFIED)) {
if (result instanceof PactError) {
throw ((PactError)result).error();
}
if (result instanceof UserCodeFailed) {
throw ((UserCodeFailed)result).error();
}
if (result instanceof PactMismatch) {
PactMismatch mismatch = (PactMismatch) result;
throw new PactMismatchException(mismatch);
}
}
}
/**
* scan all methods for @Pact annotation and execute them, if not already initialized
* @param fragment
*/
protected Map getPacts(String fragment) {
if (fragments == null) {
fragments = new HashMap ();
for (Method m: target.getClass().getMethods()) {
if (conformsToSignature(m) && methodMatchesFragment(m, fragment)) {
Pact pact = m.getAnnotation(Pact.class);
if (StringUtils.isEmpty(pact.provider()) || provider.equals(pact.provider())) {
PactDslWithProvider dslBuilder = ConsumerPactBuilder.consumer(pact.consumer())
.hasPactWith(provider);
try {
fragments.put(provider, (PactFragment) m.invoke(target, dslBuilder));
} catch (Exception e) {
throw new RuntimeException("Failed to invoke pact method", e);
}
}
}
}
}
return fragments;
}
private boolean methodMatchesFragment(Method m, String fragment) {
return StringUtils.isEmpty(fragment) || m.getName().equals(fragment);
}
/**
* validates method signature as described at {@link Pact}
*/
private boolean conformsToSignature(Method m) {
Pact pact = m.getAnnotation(Pact.class);
boolean conforms =
pact != null
&& PactFragment.class.isAssignableFrom(m.getReturnType())
&& m.getParameterTypes().length == 1
&& m.getParameterTypes()[0].isAssignableFrom(PactDslWithProvider.class);
if (!conforms && pact != null) {
throw new UnsupportedOperationException("Method " + m.getName() +
" does not conform required method signature 'public PactFragment xxx(PactDslWithProvider builder)'");
}
return conforms;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy