All Downloads are FREE. Search and download functionalities are using the official Maven repository.

au.com.dius.pact.provider.junit.loader.PactBrokerLoader Maven / Gradle / Ivy

Go to download

Pact provider ============= sub project of https://github.com/DiUS/pact-jvm The pact provider is responsible for verifying that an API provider adheres to a number of pacts authored by its clients This library provides the basic tools required to automate the process, and should be usable on its own in many instances. Framework and build tool specific bindings will be provided in separate libraries that build on top of this core functionality. ### Provider State Before each interaction is executed, the provider under test will have the opportunity to enter a state. Generally the state maps to a set of fixture data for mocking out services that the provider is a consumer of (they will have their own pacts) The pact framework will instruct the test server to enter that state by sending: POST "${config.stateChangeUrl.url}/setup" { "state" : "${interaction.stateName}" } ### An example of running provider verification with junit This example uses Groovy, JUnit 4 and Hamcrest matchers to run the provider verification. As the provider service is a DropWizard application, it uses the DropwizardAppRule to startup the service before running any test. **Warning:** It only grabs the first interaction from the pact file with the consumer, where there could be many. (This could possibly be solved with a parameterized test) ```groovy class ReadmeExamplePactJVMProviderJUnitTest { @ClassRule public static final TestRule startServiceRule = new DropwizardAppRule<DropwizardConfiguration>( TestDropwizardApplication, ResourceHelpers.resourceFilePath('dropwizard/test-config.yaml')) private static ProviderInfo serviceProvider private static Pact<RequestResponseInteraction> testConsumerPact private static ConsumerInfo consumer @BeforeClass static void setupProvider() { serviceProvider = new ProviderInfo('Dropwizard App') serviceProvider.setProtocol('http') serviceProvider.setHost('localhost') serviceProvider.setPort(8080) serviceProvider.setPath('/') consumer = new ConsumerInfo() consumer.setName('test_consumer') consumer.setPactSource(new UrlSource( ReadmeExamplePactJVMProviderJUnitTest.getResource('/pacts/zoo_app-animal_service.json').toString())) testConsumerPact = DefaultPactReader.INSTANCE.loadPact(consumer.getPactSource()) as Pact<RequestResponseInteraction> } @Test void runConsumerPacts() { // grab the first interaction from the pact with consumer Interaction interaction = testConsumerPact.interactions.get(0) // setup the verifier ProviderVerifier verifier = setupVerifier(interaction, serviceProvider, consumer) // setup any provider state // setup the client and interaction to fire against the provider ProviderClient client = new ProviderClient(serviceProvider, new HttpClientFactory()) Map<String, Object> failures = new HashMap<>() verifier.verifyResponseFromProvider(serviceProvider, interaction, interaction.getDescription(), failures, client) // normally assert all good, but in this example it will fail assertThat(failures, is(not(empty()))) verifier.displayFailures(failures) } private ProviderVerifier setupVerifier(Interaction interaction, ProviderInfo provider, ConsumerInfo consumer) { ProviderVerifier verifier = new ProviderVerifier() verifier.initialiseReporters(provider) verifier.reportVerificationForConsumer(consumer, provider, new UrlSource('http://example.example')) if (!interaction.getProviderStates().isEmpty()) { for (ProviderState providerState: interaction.getProviderStates()) { verifier.reportStateForInteraction(providerState.getName(), provider, consumer, true) } } verifier.reportInteractionDescription(interaction) return verifier } } ``` ### An example of running provider verification with spock This example uses groovy and spock to run the provider verification. Again the provider service is a DropWizard application, and is using the DropwizardAppRule to startup the service. This example runs all interactions using spocks Unroll feature ```groovy class ReadmeExamplePactJVMProviderSpockSpec extends Specification { @ClassRule @Shared TestRule startServiceRule = new DropwizardAppRule<DropwizardConfiguration>(TestDropwizardApplication, ResourceHelpers.resourceFilePath('dropwizard/test-config.yaml')) @Shared ProviderInfo serviceProvider ProviderVerifier verifier def setupSpec() { serviceProvider = new ProviderInfo('Dropwizard App') serviceProvider.protocol = 'http' serviceProvider.host = 'localhost' serviceProvider.port = 8080 serviceProvider.path = '/' serviceProvider.hasPactWith('zoo_app') { consumer -> consumer.pactSource = new FileSource(new File(ResourceHelpers.resourceFilePath('pacts/zoo_app-animal_service.json'))) } } def setup() { verifier = new ProviderVerifier() } def cleanup() { // cleanup provider state // ie. db.truncateAllTables() } def cleanupSpec() { // cleanup provider } @Unroll def "Provider Pact - With Consumer #consumer"() { expect: !verifyConsumerPact(consumer).empty where: consumer << serviceProvider.consumers } private Map verifyConsumerPact(ConsumerInfo consumer) { Map failures = [:] verifier.initialiseReporters(serviceProvider) verifier.runVerificationForConsumer(failures, serviceProvider, consumer) if (!failures.empty) { verifier.displayFailures(failures) } failures } } ```

There is a newer version: 4.0.10
Show newest version
package au.com.dius.pact.provider.junit.loader;

import au.com.dius.pact.core.model.*;
import au.com.dius.pact.core.model.PactSource;
import au.com.dius.pact.core.pactbroker.PactBrokerClient;
import au.com.dius.pact.provider.ConsumerInfo;
import au.com.dius.pact.core.support.expressions.SystemPropertyResolver;
import au.com.dius.pact.core.support.expressions.ValueResolver;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static au.com.dius.pact.core.support.expressions.ExpressionParser.parseExpression;
import static au.com.dius.pact.core.support.expressions.ExpressionParser.parseListExpression;
import static java.util.stream.Collectors.toList;

/**
 * Out-of-the-box implementation of {@link PactLoader} that downloads pacts from Pact broker
 */
public class PactBrokerLoader implements PactLoader {
  private static final Logger LOGGER = LoggerFactory.getLogger(PactBrokerLoader.class);
  private static final String LATEST = "latest";

  private final String pactBrokerHost;
  private final String pactBrokerPort;
  private final String pactBrokerScheme;
  private final List pactBrokerTags;
  private final List pactBrokerConsumers;
  private boolean failIfNoPactsFound = true;
  private PactBrokerAuth authentication;
  private PactBrokerSource pactSource;
  private Class valueResolverClass;
  private ValueResolver valueResolver;

  public PactBrokerLoader(final String pactBrokerHost, final String pactBrokerPort, final String pactBrokerScheme) {
    this(pactBrokerHost, pactBrokerPort, pactBrokerScheme, Collections.singletonList(LATEST), new ArrayList<>());
  }

  public PactBrokerLoader(final String pactBrokerHost, final String pactBrokerPort, final String pactBrokerScheme,
      final List tags, final List consumers) {
    this.pactBrokerHost = pactBrokerHost;
    this.pactBrokerPort = pactBrokerPort;
    this.pactBrokerScheme = pactBrokerScheme;
    this.pactBrokerTags = tags;
    this.pactBrokerConsumers = consumers;
    this.pactSource = new PactBrokerSource(this.pactBrokerHost, this.pactBrokerPort, this.pactBrokerScheme);
  }

  public PactBrokerLoader(final PactBroker pactBroker) {
    this(pactBroker.host(), pactBroker.port(), pactBroker.scheme(),
      Arrays.asList(pactBroker.tags()), Arrays.asList(pactBroker.consumers()));
    this.authentication = pactBroker.authentication();
    this.valueResolverClass = pactBroker.valueResolver();
  }

  public List load(final String providerName) throws IOException {
    List pacts = new ArrayList<>();
    ValueResolver resolver = setupValueResolver();
    if (pactBrokerTags == null || pactBrokerTags.isEmpty()) {
      pacts.addAll(loadPactsForProvider(providerName, null, resolver));
    } else {
      for (String tag : pactBrokerTags.stream().flatMap(tag -> parseListExpression(tag, resolver).stream()).collect(toList())) {
        try {
          pacts.addAll(loadPactsForProvider(providerName, tag, resolver));
        } catch (NoPactsFoundException e) {
          // Ignoring exception at this point, it will be handled at a higher level
        }
      }
    }
    return pacts;
  }

  private ValueResolver setupValueResolver() {
    ValueResolver resolver = new SystemPropertyResolver();
    if (valueResolver != null) {
      resolver = valueResolver;
    } else if (valueResolverClass != null) {
      try {
        resolver = valueResolverClass.newInstance();
      } catch (InstantiationException | IllegalAccessException e) {
        LOGGER.warn("Failed to instantiate the value resolver, using the default", e);
      }
    }
    return resolver;
  }

  @Override
  public PactSource getPactSource() {
    return pactSource;
  }

  @Override
  public void setValueResolver(ValueResolver valueResolver) {
    this.valueResolver = valueResolver;
  }

  private List loadPactsForProvider(final String providerName, final String tag, ValueResolver resolver) throws IOException {
    LOGGER.debug("Loading pacts from pact broker for provider " + providerName + " and tag " + tag);
    String scheme = parseExpression(pactBrokerScheme, resolver);
    String host = parseExpression(pactBrokerHost, resolver);
    String port = parseExpression(pactBrokerPort, resolver);

    if(StringUtils.isEmpty(host)){
      throw new IllegalArgumentException(String.format("Invalid pact broker host specified ('%s'). "
        + "Please provide a valid host or specify the system property 'pactbroker.host'.", pactBrokerHost));
    }

    if(StringUtils.isNotEmpty(port) && !port.matches("^[0-9]+")){
      throw new IllegalArgumentException(String.format("Invalid pact broker port specified ('%s'). "
        + "Please provide a valid port number or specify the system property 'pactbroker.port'.", pactBrokerPort));
    }

    URIBuilder uriBuilder = new URIBuilder().setScheme(scheme).setHost(host);
    if (StringUtils.isNotEmpty(port)) {
      uriBuilder.setPort(Integer.parseInt(port));
    }
    try {
      List consumers;
      PactBrokerClient pactBrokerClient = newPactBrokerClient(uriBuilder.build(), resolver);
      if (StringUtils.isEmpty(tag) || tag.equals(LATEST)) {
        consumers = pactBrokerClient.fetchConsumers(providerName).stream()
                        .map(ConsumerInfo::from).collect(toList());
      } else {
        consumers = pactBrokerClient.fetchConsumersWithTag(providerName, tag).stream()
                        .map(ConsumerInfo::from).collect(toList());
      }

      if (failIfNoPactsFound && consumers.isEmpty()) {
        throw new NoPactsFoundException("No consumer pacts were found for provider '" + providerName + "' and tag '" +
                                            tag + "'. (URL " + getUrlForProvider(providerName, tag, pactBrokerClient) + ")");
      }

      if (!pactBrokerConsumers.isEmpty()) {
        List consumerInclusions = pactBrokerConsumers
          .stream()
          .flatMap(consumer -> parseListExpression(consumer, resolver).stream())
          .collect(toList());
        consumers = consumers.stream()
                        .filter(c -> consumerInclusions.isEmpty() || consumerInclusions.contains(c.getName()))
                        .collect(toList());
      }

      return consumers.stream()
                 .map(consumer -> this.loadPact(consumer, pactBrokerClient.getOptions()))
                 .collect(toList());
    } catch (URISyntaxException e) {
      throw new IOException("Was not able load pacts from broker as the broker URL was invalid", e);
    }
  }

  private String getUrlForProvider(String providerName, String tag, PactBrokerClient pactBrokerClient) {
    try {
      return pactBrokerClient.getUrlForProvider(providerName, tag);
    } catch (Exception e) {
      LOGGER.debug("Failed to get provider URL from the pact broker", e);
      return "Unknown";
    }
  }

  Pact loadPact(ConsumerInfo consumer, Map options) {
    Pact pact = DefaultPactReader.INSTANCE.loadPact(consumer.getPactSource(), options);
    Map> pacts = this.pactSource.getPacts();
    Consumer pactConsumer = consumer.toPactConsumer();
    List pactList = pacts.getOrDefault(pactConsumer, new ArrayList<>());
    pactList.add(pact);
    pacts.put(pactConsumer, pactList);
    return pact;
  }

  PactBrokerClient newPactBrokerClient(URI url, ValueResolver resolver) {
    if (this.authentication == null || this.authentication.scheme().equalsIgnoreCase("none")) {
      LOGGER.debug("Authentication: None");
      return new PactBrokerClient(url.toString(), Collections.emptyMap());
    }

    String scheme = parseExpression(this.authentication.scheme(), resolver);
    if (StringUtils.isNotEmpty(scheme)) {
      // Legacy behavior (before support for bearer token was added):
      // If scheme was not explicitly set, basic was always used.
      // If it was explicitly set, the given value was used together with username and password
      String schemeToUse = scheme.equals("legacy") ? "basic": scheme;
      LOGGER.debug("Authentication: {}", schemeToUse);
      Map> options = Collections.singletonMap("authentication", Arrays.asList(schemeToUse,
              parseExpression(this.authentication.username(), resolver),
              parseExpression(this.authentication.password(), resolver)));
      return new PactBrokerClient(url.toString(), options);
    }

    // Check if username is set. If yes, use basic auth.
    String username = parseExpression(this.authentication.username(), resolver);
    if (StringUtils.isNotEmpty(username)) {
      LOGGER.debug("Authentication: Basic");
      Map> options = Collections.singletonMap("authentication", Arrays.asList("basic", username, parseExpression(this.authentication.password(), resolver)));
      return new PactBrokerClient(url.toString(), options);
    }

    // Check if token is set. If yes, use bearer auth.
    String token = parseExpression(this.authentication.token(), resolver);
    if (StringUtils.isNotEmpty(token)) {
      LOGGER.debug("Authentication: Bearer");
      Map> options = Collections.singletonMap("authentication", Arrays.asList("bearer", token));
      return new PactBrokerClient(url.toString(), options);
    }

    throw new IllegalArgumentException("Invalid pact authentication specified. Either username or token must be set.");
  }

  public boolean isFailIfNoPactsFound() {
    return failIfNoPactsFound;
  }

  public void setFailIfNoPactsFound(boolean failIfNoPactsFound) {
    this.failIfNoPactsFound = failIfNoPactsFound;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy