blob: 0c1f986c8a04b2999ae8b6baac11a2d1863105c1 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.integrationtests.common;
import static io.restassured.RestAssured.given;
import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.MONTHS;
import static java.time.temporal.ChronoUnit.WEEKS;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.fail;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.io.File;
import java.security.SecureRandom;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.http.conn.HttpHostConnectException;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Util for RestAssured tests. This class here in src/integrationTest is copy/pasted to src/test; please keep them in
* sync.
*/
@SuppressWarnings("unchecked")
public final class Utils {
public static final String TENANT_PARAM_NAME = "tenantIdentifier";
public static final String DEFAULT_TENANT = "default";
public static final String TENANT_IDENTIFIER = TENANT_PARAM_NAME + '=' + DEFAULT_TENANT;
private static final String LOGIN_URL = "/fineract-provider/api/v1/authentication?" + TENANT_IDENTIFIER;
public static final String TENANT_TIME_ZONE = "Asia/Kolkata";
public static final String DATE_FORMAT = "dd MMMM yyyy";
public static final String DATE_TIME_FORMAT = "dd MMMM yyyy HH:mm";
public static final DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder().appendPattern(DATE_FORMAT).toFormatter();
public static final DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder().appendPattern(DATE_TIME_FORMAT).toFormatter();
private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
private static final SecureRandom random = new SecureRandom();
private static final Gson gson = new Gson();
private static final String HEALTH_URL = "/fineract-provider/actuator/health";
private static final Random r = new Random();
private static final ConcurrentHashMap<String, Set<String>> uniqueRandomStringContainer = new ConcurrentHashMap<>();
public static final String SOURCE_SET_NUMBERS_AND_LETTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static final String SOURCE_SET_NUMBERS = "1234567890";
private Utils() {}
public static void initializeRESTAssured() {
RestAssured.baseURI = "https://localhost";
RestAssured.port = 8443;
RestAssured.keyStore("src/main/resources/keystore.jks", "openmf");
RestAssured.useRelaxedHTTPSValidation();
}
public static RequestSpecification initializeDefaultRequestSpecification() {
RequestSpecification requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
return requestSpec;
}
public static ResponseSpecification initializeDefaultResponseSpecification() {
return new ResponseSpecBuilder().expectStatusCode(200).build();
}
private static void awaitSpringBootActuatorHealthyUp() {
int attempt = 0;
final int max_attempts = 10;
Response response = null;
Exception lastException = null;
do {
try {
response = RestAssured.get(HEALTH_URL);
int healthHttpStatus = response.statusCode();
if (healthHttpStatus == 200) {
LOG.info("{} return HTTP 200, application is now ready for integration testing!", HEALTH_URL);
return;
} else {
LOG.info("{} returned HTTP {}, going to wait and retry (attempt {})", HEALTH_URL, healthHttpStatus, attempt++);
sleep(3);
}
} catch (Exception e) {
LOG.info("{} caused {}, going to wait and retry (attempt {})", HEALTH_URL, e.getMessage(), attempt++);
lastException = e;
sleep(3);
}
} while (attempt < max_attempts);
if (lastException != null) {
LOG.error("{} still not reachable, giving up", HEALTH_URL, lastException);
throw new AssertionError(HEALTH_URL + " not reachable", lastException);
} else {
LOG.error("{} still has not returned HTTP 200, giving up (last) body: {}", HEALTH_URL, response.prettyPrint());
fail(HEALTH_URL + " returned " + response.prettyPrint());
}
}
/**
* Wait until the given condition is true or the maxRun is reached.
*
* @param maxRun
* max number of times to run the condition
* @param seconds
* wait time between evaluation in seconds
* @param waitCondition
* condition to evaluate
*/
public static void conditionalSleepWithMaxWait(int maxRun, int seconds, Supplier<Boolean> waitCondition) {
do {
sleep(seconds);
maxRun--;
} while (maxRun > 0 && waitCondition.get());
}
private static void sleep(int seconds) {
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
LOG.warn("Unexpected InterruptedException", e);
throw new IllegalStateException("Unexpected InterruptedException", e);
}
}
public static String loginIntoServerAndGetBase64EncodedAuthenticationKey() {
return loginIntoServerAndGetBase64EncodedAuthenticationKey("mifos", "password");
}
public static String loginIntoServerAndGetBase64EncodedAuthenticationKey(String username, String password) {
awaitSpringBootActuatorHealthyUp();
try {
LOG.info("Logging in, for integration test...");
// system.out.println("-----------------------------------LOGIN-----------------------------------------");
String json = RestAssured.given().contentType(ContentType.JSON)
.body("{\"username\":\"" + username + "\", \"password\":\"" + password + "\"}").expect().log().ifError().when()
.post(LOGIN_URL).asString();
assertThat("Failed to login into fineract platform", StringUtils.isBlank(json), is(false));
String key = JsonPath.with(json).get("base64EncodedAuthenticationKey");
assertThat("Failed to obtain key: " + json, StringUtils.isBlank(key), is(false));
return key;
} catch (final Exception e) {
if (e instanceof HttpHostConnectException) {
final HttpHostConnectException hh = (HttpHostConnectException) e;
fail("Failed to connect to fineract platform:" + hh.getMessage());
}
throw new RuntimeException(e);
}
}
public static String performServerGet(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String url) {
return performServerGet(requestSpec, responseSpec, url, null);
}
public static Response performServerGetRaw(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String getURL, Function<RequestSpecification, RequestSpecification> requestMapper) {
return requestMapper.apply(given().spec(requestSpec)).expect().spec(responseSpec).log().ifError().when().get(getURL).andReturn();
}
public static <T> T performServerGet(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String getURL, final String jsonAttributeToGetBack) {
final String json = given().spec(requestSpec).expect().spec(responseSpec).log().ifError().when().get(getURL).andReturn().asString();
if (jsonAttributeToGetBack == null) {
return (T) json;
}
return (T) JsonPath.from(json).get(jsonAttributeToGetBack);
}
public static <T> T performServerPatch(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String getURL, final String jsonAttributeToGetBack) {
final String json = given().spec(requestSpec).expect().spec(responseSpec).log().ifError().when().patch(getURL).andReturn()
.asString();
if (jsonAttributeToGetBack == null) {
return (T) json;
}
return (T) JsonPath.from(json).get(jsonAttributeToGetBack);
}
public static List<String> performServerGetList(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String getURL, final String jsonAttributeToGetBack) {
final JsonPath jsonPath = given().spec(requestSpec).expect().spec(responseSpec).log().ifError().when().get(getURL).jsonPath();
List<String> items = jsonPath.getList(jsonAttributeToGetBack);
return items;
}
public static JsonElement performServerGetArray(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String getURL, final int position, final String jsonAttributeToGetBack) {
final JsonPath jsonPath = given().spec(requestSpec).expect().spec(responseSpec).log().ifError().when().get(getURL).jsonPath();
List<Map<String, Object>> items = jsonPath.getList("$");
return gson.fromJson(((ArrayList) items.get(position).get(jsonAttributeToGetBack)).toString(), JsonArray.class);
}
public static String performGetTextResponse(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String getURL) {
return given().spec(requestSpec).expect().spec(responseSpec).log().ifError().when().get(getURL).andReturn().asString();
}
public static byte[] performGetBinaryResponse(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String getURL) {
return given().spec(requestSpec).expect().spec(responseSpec).log().ifError().when().get(getURL).andReturn().asByteArray();
}
public static String performServerPost(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String postURL, final String jsonBodyToSend) {
return performServerPost(requestSpec, responseSpec, postURL, jsonBodyToSend, null);
}
public static <T> T performServerPost(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String postURL, final String jsonBodyToSend, final String jsonAttributeToGetBack) {
LOG.info("JSON {}", jsonBodyToSend);
RequestSpecification spec = given().spec(requestSpec);
if (StringUtils.isNotBlank(jsonBodyToSend)) {
spec = spec.body(jsonBodyToSend);
}
final String json = spec.expect().spec(responseSpec).log().ifError().when().post(postURL).andReturn().asString();
if (jsonAttributeToGetBack == null) {
return (T) json;
}
return (T) JsonPath.from(json).get(jsonAttributeToGetBack);
}
public static Response performServerPutRaw(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String putURL, Function<RequestSpecification, RequestSpecification> bodyMapper) {
return bodyMapper.apply(given().spec(requestSpec)).expect().spec(responseSpec).log().ifError().when().put(putURL).andReturn();
}
public static String performServerPut(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String putURL, final String jsonBodyToSend) {
return performServerPut(requestSpec, responseSpec, putURL, jsonBodyToSend, null);
}
public static <T> T performServerPut(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String putURL, final String jsonBodyToSend, final String jsonAttributeToGetBack) {
final String json = given().spec(requestSpec).body(jsonBodyToSend).expect().spec(responseSpec).log().ifError().when().put(putURL)
.andReturn().asString();
if (jsonAttributeToGetBack == null) {
return (T) json;
}
return (T) JsonPath.from(json).get(jsonAttributeToGetBack);
}
public static <T> T performServerDelete(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String deleteURL, final String jsonAttributeToGetBack) {
final String json = given().spec(requestSpec).expect().spec(responseSpec).log().ifError().when().delete(deleteURL).andReturn()
.asString();
if (jsonAttributeToGetBack == null) {
return (T) json;
}
return (T) JsonPath.from(json).get(jsonAttributeToGetBack);
}
public static <T> T performServerDelete(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String deleteURL, final String jsonBodyToSend, final String jsonAttributeToGetBack) {
final String json = given().spec(requestSpec).body(jsonBodyToSend).expect().spec(responseSpec).log().ifError().when()
.delete(deleteURL).andReturn().asString();
return (T) (jsonAttributeToGetBack == null ? json : JsonPath.from(json).get(jsonAttributeToGetBack));
}
public static String convertDateToURLFormat(final String dateToBeConvert) throws ParseException {
final SimpleDateFormat oldFormat = new SimpleDateFormat("dd MMMMMM yyyy", Locale.US);
final SimpleDateFormat newFormat = new SimpleDateFormat("yyyy-MM-dd");
String reformattedStr = "";
reformattedStr = newFormat.format(oldFormat.parse(dateToBeConvert));
return reformattedStr;
}
@SuppressFBWarnings(value = {
"DMI_RANDOM_USED_ONLY_ONCE" }, justification = "False positive for random object created and used only once")
public static String randomStringGenerator(final String prefix, final int len, final String sourceSetString) {
final int lengthOfSource = sourceSetString.length();
final StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
sb.append(sourceSetString.charAt(random.nextInt(lengthOfSource)));
}
return prefix + sb;
}
public static String randomStringGenerator(final String prefix, final int len) {
return randomStringGenerator(prefix, len, SOURCE_SET_NUMBERS_AND_LETTERS);
}
public static String uniqueRandomStringGenerator(final String prefix, final int lenOfRandomSuffix) {
return uniqueRandomGenerator(prefix, lenOfRandomSuffix, SOURCE_SET_NUMBERS_AND_LETTERS);
}
public static Integer uniqueRandomNumberGenerator(final int lenOfRandomSuffix) {
return Integer.parseInt(uniqueRandomGenerator("", lenOfRandomSuffix, SOURCE_SET_NUMBERS));
}
public static String uniqueRandomGenerator(final String prefix, final int lenOfRandomSuffix, String sourceSet) {
String response;
String key = prefix + lenOfRandomSuffix;
int i = 0;
do {
response = randomStringGenerator(prefix, lenOfRandomSuffix, sourceSet);
uniqueRandomStringContainer.putIfAbsent(key, new HashSet<>());
i++;
if (i == 10000) {
throw new IllegalStateException("Possible endless loop for: " + key);
}
} while (!uniqueRandomStringContainer.get(key).add(response));
return response;
}
@SuppressFBWarnings(value = {
"DMI_RANDOM_USED_ONLY_ONCE" }, justification = "False positive for random object created and used only once")
public static Integer randomNumberGenerator(final int expectedLength) {
String response = randomStringGenerator("", expectedLength, SOURCE_SET_NUMBERS);
return Integer.parseInt(response);
}
public static Float randomDecimalGenerator(final int expectedWholeLength, final int expectedFractionLength) {
final String source = SOURCE_SET_NUMBERS;
final int lengthOfSource = source.length();
StringBuilder stringBuilder = new StringBuilder(expectedWholeLength + expectedFractionLength + 1);
for (int i = 0; i < expectedWholeLength; i++) {
stringBuilder.append(source.charAt(random.nextInt(lengthOfSource)));
}
stringBuilder.append(".");
for (int i = 0; i < expectedFractionLength; i++) {
stringBuilder.append(source.charAt(random.nextInt(lengthOfSource)));
}
return Float.parseFloat(stringBuilder.toString());
}
public static String convertDateToURLFormat(final Calendar dateToBeConvert) {
DateFormat dateFormat = new SimpleDateFormat("dd MMMMMM yyyy");
dateFormat.setTimeZone(Utils.getTimeZoneOfTenant());
return dateFormat.format(dateToBeConvert.getTime());
}
public static String convertDateToURLFormat(final Calendar dateToBeConvert, final String dateGormat) {
DateFormat dateFormat = new SimpleDateFormat(dateGormat);
dateFormat.setTimeZone(Utils.getTimeZoneOfTenant());
return dateFormat.format(dateToBeConvert.getTime());
}
@NotNull
public static OffsetDateTime getAuditDateTimeToCompare() throws InterruptedException {
OffsetDateTime now = DateUtils.getAuditOffsetDateTime();
// Testing in minutes precision, but still need to take care around the end of the actual minute
if (now.getSecond() > 56) {
Thread.sleep(5000);
now = DateUtils.getAuditOffsetDateTime();
}
return now;
}
public static TimeZone getTimeZoneOfTenant() {
return TimeZone.getTimeZone(TENANT_TIME_ZONE);
}
public static ZoneId getZoneIdOfTenant() {
return ZoneId.of(TENANT_TIME_ZONE);
}
public static LocalDate getLocalDateOfTenant() {
return LocalDate.now(getZoneIdOfTenant());
}
public static LocalDateTime getLocalDateTimeOfTenant() {
return LocalDateTime.now(getZoneIdOfTenant());
}
public static Date convertJsonElementAsDate(JsonElement jsonElement) {
if (jsonElement.isJsonArray()) {
JsonArray jsonArray = jsonElement.getAsJsonArray();
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, jsonArray.get(0).getAsInt());
calendar.set(Calendar.MONTH, jsonArray.get(1).getAsInt() - 1);
calendar.set(Calendar.DATE, jsonArray.get(2).getAsInt());
// If the Array includes Time
if (jsonArray.size() > 3) {
calendar.set(Calendar.HOUR, jsonArray.get(3).getAsInt());
calendar.set(Calendar.MINUTE, jsonArray.get(4).getAsInt());
calendar.set(Calendar.SECOND, jsonArray.get(5).getAsInt());
}
return calendar.getTime();
}
return null;
}
public static String performServerTemplatePost(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String postURL, final String legalFormType, final File file, final String locale, final String dateFormat) {
final String importDocumentId = given().spec(requestSpec).queryParam("legalFormType", legalFormType).multiPart("file", file)
.formParam("locale", locale).formParam("dateFormat", dateFormat).expect().spec(responseSpec).log().ifError().when()
.post(postURL).andReturn().asString();
return importDocumentId;
}
public static String performServerOutputTemplateLocationGet(final RequestSpecification requestSpec,
final ResponseSpecification responseSpec, final String getURL, final String importDocumentId) {
final String templateLocation = given().spec(requestSpec).queryParam("importDocumentId", importDocumentId).expect()
.spec(responseSpec).log().ifError().when().get(getURL).andReturn().asString();
return templateLocation.substring(1, templateLocation.length() - 1);
}
public static String emptyJson() {
return "{}";
}
public static String randomDateGenerator(String dateFormat) {
DateTimeFormatter dateTimeFormatterBuilder = new DateTimeFormatterBuilder().parseCaseInsensitive().parseLenient()
.appendPattern(dateFormat).optionalStart().appendPattern(" HH:mm:ss").optionalEnd()
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0).parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0).toFormatter();
LocalDate localDate = LocalDate.of(getYear(), getMonth(), getDay());
return dateTimeFormatterBuilder.format(localDate);
}
public static String randomDateTimeGenerator(String dateFormat) {
DateTimeFormatter dateTimeFormatterBuilder = new DateTimeFormatterBuilder().parseCaseInsensitive().parseLenient()
.appendPattern(dateFormat).optionalStart().appendPattern(" HH:mm:ss").optionalEnd()
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0).parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0).toFormatter();
LocalDateTime localDate = LocalDateTime.of(getYear(), getMonth(), getDay(), getHour(), getMinute(), getSecond());
return dateTimeFormatterBuilder.format(localDate);
}
private static int getYear() {
return 1000 + r.nextInt(1001);
}
private static int getMonth() {
return 10 + r.nextInt(3);
}
private static int getDay() {
return 10 + r.nextInt(16);
}
private static int getHour() {
return 10 + r.nextInt(14);
}
private static int getMinute() {
return 10 + r.nextInt(50);
}
private static int getSecond() {
return 10 + r.nextInt(50);
}
public static String arrayDateToString(List intArray) {
String[] strArray = (String[]) intArray.stream().map(String::valueOf).toArray(String[]::new);
return String.join("-", strArray);
}
public static String arrayDateTimeToString(List<Integer> integerList) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 6; i++) {
if (i < 2) {
stringBuilder.append(integerList.get(i)).append("-");
} else if (i == 2) {
stringBuilder.append(integerList.get(i)).append(" ");
} else if (i == 3) {
stringBuilder.append(integerList.get(i));
} else {
stringBuilder.append(":").append(integerList.get(i));
}
}
return stringBuilder.toString();
}
public static String convertToJson(HashMap<String, Object> map) {
return new Gson().toJson(map);
}
public static String convertToJson(Object o) {
return gson.toJson(o);
}
public static LocalDate getDateAsLocalDate(String dateAsString) {
return LocalDate.parse(dateAsString, dateFormatter);
}
public static long getDifferenceInDays(final LocalDate localDateBefore, final LocalDate localDateAfter) {
return DAYS.between(localDateBefore, localDateAfter);
}
public static long getDifferenceInWeeks(final LocalDate localDateBefore, final LocalDate localDateAfter) {
return WEEKS.between(localDateBefore, localDateAfter);
}
public static long getDifferenceInMonths(final LocalDate localDateBefore, final LocalDate localDateAfter) {
return MONTHS.between(localDateBefore, localDateAfter);
}
}