| /** |
| * 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 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 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); |
| final String json = given().spec(requestSpec).body(jsonBodyToSend).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); |
| } |
| } |