package com.sequenceiq.it.verification;
import static org.testng.Assert.fail;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.Response;
public class Verification {
private static final Logger LOGGER = LoggerFactory.getLogger(Verification.class);
private String path;
private boolean regex;
private String httpMethod;
private Map<Call, Response> requestResponseMap;
private Integer atLeast;
private Integer exactTimes;
private List<Pattern> patternList = new ArrayList<>();
private Map<String, Integer> bodyContainsList = new HashMap<>();
public Verification(String path, String httpMethod, Map<Call, Response> requestResponseMap, boolean regex) {
this.path = path;
this.regex = regex;
this.httpMethod = httpMethod;
this.requestResponseMap = requestResponseMap;
}
public Verification atLeast(int times) {
this.atLeast = times;
return this;
}
public Verification exactTimes(int times) {
this.exactTimes = times;
return this;
}
public Verification bodyContains(String text) {
bodyContainsList.put(text, -1);
return this;
}
public Verification bodyContains(String text, int times) {
bodyContainsList.put(text, times);
return this;
}
public Verification bodyRegexp(String regexp) {
Pattern pattern = Pattern.compile(regexp);
patternList.add(pattern);
return this;
}
public void verify() {
logVerify();
int times = getTimesMatched();
checkAtLeast(times);
checkExactTimes(times);
}
private void logVerify() {
LOGGER.info("Verification call: " + path);
LOGGER.info("Body must contains: " + StringUtils.join(bodyContainsList, ","));
List<String> patternStringList = patternList.stream().map(Pattern::pattern).collect(Collectors.toList());
LOGGER.info("Body must match: " + StringUtils.join(patternStringList, ","));
}
private void checkExactTimes(int times) {
if (exactTimes != null) {
if (exactTimes != times) {
logRequests();
fail(path + " request didn't invoked exactly " + exactTimes + " times, invoked " + times + " times");
}
}
}
private void checkAtLeast(int times) {
if (atLeast != null) {
if (times < atLeast) {
logRequests();
fail(path + " request didn't invoked at least " + atLeast + " times, invoked " + times + " times");
}
}
}
private int getTimesMatched() {
int times = 0;
for (Call call : requestResponseMap.keySet()) {
boolean pathMatched = isPathMatched(call);
if (call.getMethod().equals(httpMethod) && pathMatched) {
int bodyContainsNumber = 0;
for (String bodyContains : bodyContainsList.keySet()) {
int count = StringUtils.countMatches(call.getPostBody(), bodyContains);
int required = bodyContainsList.get(bodyContains);
if ((required < 0 && count > 0) || (count == required)) {
bodyContainsNumber++;
}
}
int patternNumber = 0;
for (Pattern pattern : patternList) {
boolean patternMatch = pattern.matcher(call.getPostBody()).matches();
if (patternMatch) {
patternNumber++;
}
}
if (bodyContainsList.size() == bodyContainsNumber && patternList.size() == patternNumber) {
times++;
}
}
}
return times;
}
private boolean isPathMatched(Call call) {
boolean pathMatched;
if (regex) {
pathMatched = Pattern.matches(path, call.getUri());
} else {
pathMatched = call.getUri().equals(path);
}
return pathMatched;
}
private void logRequests() {
LOGGER.info("Request received: ");
requestResponseMap.keySet().stream().forEach(call -> LOGGER.info("Request: " + call.toString()));
}
}