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

org.wiremock.spring.internal.WireMockContextCustomizerFactory Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show newest version
package org.wiremock.spring.internal;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.TestContextAnnotationUtils;
import org.wiremock.spring.ConfigureWireMock;
import org.wiremock.spring.EnableWireMock;

/**
 * Creates {@link WireMockContextCustomizer} for test classes annotated with {@link EnableWireMock}.
 *
 * @author Maciej Walkowiak
 */
public class WireMockContextCustomizerFactory implements ContextCustomizerFactory {
  static final ConfigureWireMock DEFAULT_CONFIGURE_WIREMOCK =
      DefaultConfigureWireMock.class.getAnnotation(ConfigureWireMock.class);

  @ConfigureWireMock(name = "wiremock")
  private static class DefaultConfigureWireMock {}

  static ConfigureWireMock[] getConfigureWireMocksOrDefault(
      final ConfigureWireMock... configureWireMock) {
    if (configureWireMock == null || configureWireMock.length == 0) {
      return new ConfigureWireMock[] {WireMockContextCustomizerFactory.DEFAULT_CONFIGURE_WIREMOCK};
    }
    return configureWireMock;
  }

  @Override
  public ContextCustomizer createContextCustomizer(
      final Class testClass, final List configAttributes) {
    // scan class and all enclosing classes if the test class is @Nested
    final ConfigureWiremockHolder holder = new ConfigureWiremockHolder();
    this.parseDefinitions(testClass, holder);

    if (holder.isEmpty()) {
      return null;
    } else {
      return new WireMockContextCustomizer(holder.asArray());
    }
  }

  private void parseDefinitions(final Class testClass, final ConfigureWiremockHolder parser) {
    parser.parse(testClass);
    if (TestContextAnnotationUtils.searchEnclosingClass(testClass)) {
      this.parseDefinitions(testClass.getEnclosingClass(), parser);
    }
  }

  private static class ConfigureWiremockHolder {
    private final List annotations = new ArrayList<>();

    void add(final ConfigureWireMock... annotations) {
      this.annotations.addAll(Arrays.asList(annotations));
      this.sanityCheckDuplicateNames(this.annotations);
      this.sanityCheckHttpOrHttpsMustBeEnabled(this.annotations);
      this.sanityCheckHttpAndHttpsMustUseDifferentPorts(this.annotations);
      this.sanityCheckUniquePorts(this.annotations);
    }

    void parse(final Class clazz) {
      final EnableWireMock annotation = AnnotationUtils.findAnnotation(clazz, EnableWireMock.class);
      if (annotation != null) {
        this.add(getConfigureWireMocksOrDefault(annotation.value()));
      }
    }

    private void sanityCheckDuplicateNames(final List check) {
      final List names = check.stream().map(it -> it.name()).toList();
      final Set dublicateNames =
          names.stream()
              .filter(it -> Collections.frequency(names, it) > 1)
              .collect(Collectors.toSet());
      if (!dublicateNames.isEmpty()) {
        throw new IllegalStateException(
            "Names of mocks must be unique, found duplicates of: "
                + dublicateNames.stream().sorted().collect(Collectors.joining(",")));
      }
    }

    private void sanityCheckHttpOrHttpsMustBeEnabled(final List check) {
      for (final ConfigureWireMock configureWireMock : check) {
        if (configureWireMock.port() == -1 && configureWireMock.httpsPort() == -1) {
          throw new IllegalStateException(
              "ConfigureWireMock "
                  + configureWireMock.name()
                  + " has both HTTP and HTTPS disabled. It is an invalid configuration.");
        }
      }
    }

    private void sanityCheckUniquePorts(final List check) {
      final List ports =
          check.stream().map(it -> List.of(it.port(), it.httpsPort())).toList().stream()
              .collect(ArrayList::new, List::addAll, List::addAll);
      final Set dublicatePors =
          ports.stream()
              .filter(it -> it > 0)
              .filter(it -> Collections.frequency(ports, it) > 1)
              .collect(Collectors.toSet());
      if (!dublicatePors.isEmpty()) {
        throw new IllegalStateException(
            "Some statically configured ports are being used mor than once: "
                + dublicatePors.stream()
                    .sorted()
                    .map(it -> it.toString())
                    .collect(Collectors.joining(",")));
      }
    }

    private void sanityCheckHttpAndHttpsMustUseDifferentPorts(final List check) {
      for (final ConfigureWireMock configureWireMock : check) {
        if (configureWireMock.port() > 0
            && configureWireMock.port() == configureWireMock.httpsPort()) {
          throw new IllegalStateException(
              "ConfigureWireMock "
                  + configureWireMock.name()
                  + " uses same port "
                  + configureWireMock.port()
                  + " for HTTP and HTTPS.");
        }
      }
    }

    boolean isEmpty() {
      return this.annotations.isEmpty();
    }

    ConfigureWireMock[] asArray() {
      return this.annotations.toArray(new ConfigureWireMock[] {});
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy