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

se.sundsvall.dept44.test.AbstractAppTest Maven / Gradle / Ivy

The newest version!
package se.sundsvall.dept44.test;

import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor;
import static com.github.tomakehurst.wiremock.matching.UrlPattern.fromOneOf;
import static java.lang.Class.forName;
import static java.lang.String.format;
import static java.nio.file.Files.readString;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.concurrent.TimeUnit.SECONDS;
import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
import static net.javacrumbs.jsonunit.JsonAssert.setOptions;
import static org.apache.commons.lang3.ObjectUtils.allNotNull;
import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.APPLICATION_PROBLEM_JSON;
import static org.springframework.util.CollectionUtils.isEmpty;
import static org.springframework.util.ResourceUtils.getFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ResourceUtils;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilder;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.VerificationException;
import com.github.tomakehurst.wiremock.common.ClasspathFileSource;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.extension.Extension;
import com.github.tomakehurst.wiremock.standalone.JsonFileMappingsSource;
import com.github.tomakehurst.wiremock.verification.LoggedRequest;

import net.javacrumbs.jsonunit.JsonAssert;
import net.javacrumbs.jsonunit.core.Option;

public abstract class AbstractAppTest {

	private static final String FILES_DIR = "__files/";
	private static final String COMMON_MAPPING_DIR = "/common";
	private static final String MAPPING_DIRECTORY = "/mappings";
	private static final ObjectMapper JSON_MAPPER = JsonMapper.builder().findAndAddModules().build();
	private static final UriBuilder URI_BUILDER = new DefaultUriBuilderFactory().builder();
	private static final int DEFAULT_VERIFICATION_DELAY_IN_SECONDS = 5;
	private static final Class DEFAULT_RESPONSE_TYPE = String.class;
	private static final MediaType DEFAULT_CONTENT_TYPE = APPLICATION_JSON;

	private final Logger logger = LoggerFactory.getLogger(getClass().getName());

	private boolean expectedResponseBodyIsNull;
	private int maxVerificationDelayInSeconds = DEFAULT_VERIFICATION_DELAY_IN_SECONDS;
	private MultiValueMap multipartBody;
	private String requestBody;
	private String expectedResponseBody;
	private byte[] expectedResponseBinary;
	private Class expectedResponseType = DEFAULT_RESPONSE_TYPE;
	private String mappingPath;
	private String servicePath;
	private ResponseEntity response;
	private String responseBody;
	private HttpHeaders responseHeaders;
	private HttpMethod method;
	private MediaType contentType = DEFAULT_CONTENT_TYPE;
	private HttpStatus expectedResponseStatus;
	private HttpHeaders expectedResponseHeaders;
	private Map headerValues;
	private String testDirectoryPath;
	private String testCaseName;

	@Autowired
	protected TestRestTemplate restTemplate;

	@Autowired
	protected WireMockServer wiremock;

	public AbstractAppTest reset() {
		expectedResponseBodyIsNull = false;
		multipartBody = null;
		requestBody = null;
		expectedResponseBody = null;
		expectedResponseBinary = null;
		expectedResponseType = DEFAULT_RESPONSE_TYPE;
		mappingPath = null;
		servicePath = null;
		response = null;
		responseBody = null;
		responseHeaders = null;
		method = null;
		contentType = DEFAULT_CONTENT_TYPE;
		expectedResponseStatus = null;
		expectedResponseHeaders = null;
		headerValues = null;
		testDirectoryPath = null;
		testCaseName = null;

		return this;
	}

	public AbstractAppTest setupPaths() {
		// Fetch test case name.
		testCaseName = getTestMethodName();

		mappingPath = wiremock.getOptions().filesRoot().getPath();
		if (!mappingPath.endsWith("/")) {
			mappingPath += "/";
		}

		testDirectoryPath = "classpath:" + mappingPath + FILES_DIR + getTestMethodName() + FileSystems.getDefault().getSeparator();

		return this;
	}

	public AbstractAppTest setupCall() {
		reset();
		initializeJsonAssert();
		setupPaths();

		wiremock.loadMappingsUsing(new JsonFileMappingsSource(
			new ClasspathFileSource(mappingPath + FILES_DIR + COMMON_MAPPING_DIR + MAPPING_DIRECTORY)));
		if (nonNull(testCaseName)) {
			wiremock.loadMappingsUsing(new JsonFileMappingsSource(
				new ClasspathFileSource(mappingPath + FILES_DIR + testCaseName + MAPPING_DIRECTORY)));
		}

		return this;
	}

	public AbstractAppTest withExtensions(final Extension... extensions) {
		((WireMockConfiguration) wiremock.getOptions()).extensions(extensions);
		return this;
	}

	public AbstractAppTest withHttpMethod(final HttpMethod method) {
		this.method = method;
		return this;
	}

	public AbstractAppTest withContentType(final MediaType contentType) {
		this.contentType = contentType;
		return this;
	}

	public AbstractAppTest withExpectedResponseStatus(final HttpStatus status) {
		this.expectedResponseStatus = status;
		return this;
	}

	public AbstractAppTest withExpectedResponseType(final Class type) {
		this.expectedResponseType = type;
		return this;
	}

	public AbstractAppTest withHeader(final String key, final String value) {
		if (isNull(headerValues)) {
			headerValues = new HashMap<>();
		}
		headerValues.put(key, value);
		return this;
	}

	/**
	 * Set expected response header.
	 *
	 * @param  expectedHeaderKey   the expected header key.
	 * @param  expectedHeaderValue the list of expected header values, as regular expressions.
	 * @return                     AbstractAppTest
	 */
	public AbstractAppTest withExpectedResponseHeader(final String expectedHeaderKey, final List expectedHeaderValue) {
		if (isNull(expectedResponseHeaders)) {
			expectedResponseHeaders = new HttpHeaders();
		}
		expectedResponseHeaders.put(expectedHeaderKey, expectedHeaderValue);
		return this;
	}

	/**
	 * Method takes a JSON response string or a file name where the response can be
	 * read from.
	 *
	 * @param  expectedResponse raw json response string or filename where the response can be
	 *                          read from
	 * @return                  AbstractAppTest
	 */
	public AbstractAppTest withExpectedResponse(final String expectedResponse) {
		final var contentFromFile = fromTestFile(expectedResponse);
		if (nonNull(contentFromFile)) {
			expectedResponseBody = contentFromFile;
		} else {
			expectedResponseBody = expectedResponse;
		}

		return this;
	}

	/**
	 * Method takes a file name to a binary file.
	 *
	 * @param  expectedResponseFile the filename where the binary response can be read from.
	 * @return                      AbstractAppTest
	 * @throws IOException          if file can't be read.
	 */
	public AbstractAppTest withExpectedBinaryResponse(final String expectedResponseFile) throws IOException {
		final var file = ResourceUtils.getFile(testDirectoryPath + expectedResponseFile);
		expectedResponseBinary = Files.readAllBytes(file.toPath());
		expectedResponseType = byte[].class;

		return this;
	}

	public AbstractAppTest withExpectedResponseBodyIsNull() {
		this.expectedResponseBodyIsNull = true;
		return this;
	}

	public AbstractAppTest withServicePath(final String servicePath) {
		this.servicePath = servicePath;
		return this;
	}

	public AbstractAppTest withServicePath(final Function servicePathFunction) {
		servicePath = servicePathFunction.apply(URI_BUILDER).toString();
		return this;
	}

	/**
	 * Method adds options to be used when assertion of json is done, for example IGNORING_EXTRA_ARRAY_ITEMS.
	 * By default the test will compare arrays with option to ignore array order. If the need to use
	 * maximum strictness in JsonAssert - send in null or an empty list to just reset options to
	 * JsonAsserts default ones.
	 *
	 * @param  options list of options to use when doing the json assertion or null/empty list for resetting
	 *                 to JsonAssert defaults (strict comparison)
	 * @return         AbstractAppTest
	 */
	public AbstractAppTest withJsonAssertOptions(final List