| /** |
| * 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; |
| |
| import io.restassured.builder.RequestSpecBuilder; |
| import io.restassured.builder.ResponseSpecBuilder; |
| import io.restassured.http.ContentType; |
| import io.restassured.specification.RequestSpecification; |
| import io.restassured.specification.ResponseSpecification; |
| import java.security.SecureRandom; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.UUID; |
| import org.apache.fineract.batch.domain.BatchRequest; |
| import org.apache.fineract.batch.domain.BatchResponse; |
| import org.apache.fineract.batch.domain.Header; |
| import org.apache.fineract.infrastructure.core.exception.AbstractIdempotentCommandException; |
| import org.apache.fineract.integrationtests.common.BatchHelper; |
| import org.apache.fineract.integrationtests.common.ClientHelper; |
| import org.apache.fineract.integrationtests.common.CollateralManagementHelper; |
| import org.apache.fineract.integrationtests.common.GroupHelper; |
| import org.apache.fineract.integrationtests.common.Utils; |
| import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; |
| import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; |
| import org.junit.jupiter.api.Assertions; |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.Test; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Test class for testing the integration of Batch API with custom batch requests and various user defined workflow. |
| * Like in the case of mifos community-app |
| * |
| * @author Rishabh Shukla |
| */ |
| public class BatchRequestsIntegrationTest { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(BatchRequestsIntegrationTest.class); |
| private ResponseSpecification responseSpec; |
| private RequestSpecification requestSpec; |
| private static final SecureRandom secureRandom = new SecureRandom(); |
| |
| public BatchRequestsIntegrationTest() { |
| |
| } |
| |
| /** |
| * Sets up the essential settings for the TEST like contentType, expectedStatusCode. It uses the '@BeforeEach' |
| * annotation provided by jUnit. |
| */ |
| @BeforeEach |
| public void setup() { |
| |
| Utils.initializeRESTAssured(); |
| this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); |
| this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); |
| this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); |
| } |
| |
| @Test |
| /** |
| * Tests that a loan is successfully applied to client members of a group. Firstly, it'll create a few new clients |
| * and then will add those clients to the group. Then a few loans will be created and one of those loans will be |
| * chosen at random and similarily a few of the created clients will be chosen on random. Now, the selected loan |
| * will be applied to these clients through Batch - API ApplyLoanCommandStrategy. |
| */ |
| public void shouldReturnOkStatusForLoansAppliedToSelectedClients() { |
| |
| // Generate a random count of number of clients to be created |
| final Integer clientsCount = (int) Math.ceil(secureRandom.nextDouble() * 7) + 3; |
| final Integer[] clientIDs = new Integer[clientsCount]; |
| |
| // Create a new group and get its groupId |
| Integer groupID = GroupHelper.createGroup(this.requestSpec, this.responseSpec, true); |
| |
| // Create new clients and add those to this group |
| for (Integer i = 0; i < clientsCount; i++) { |
| clientIDs[i] = ClientHelper.createClient(this.requestSpec, this.responseSpec); |
| groupID = GroupHelper.associateClient(this.requestSpec, this.responseSpec, groupID.toString(), clientIDs[i].toString()); |
| LOG.info("client {} has been added to the group {}", clientIDs[i], groupID); |
| } |
| |
| // Generate a random count of number of new loan products to be created |
| final Integer loansCount = (int) Math.ceil(secureRandom.nextDouble() * 4) + 1; |
| final Integer[] loanProducts = new Integer[loansCount]; |
| |
| // Create new loan Products |
| for (Integer i = 0; i < loansCount; i++) { |
| final String loanProductJSON = new LoanProductTestBuilder() // |
| .withPrincipal(String.valueOf(10000.00 + Math.ceil(secureRandom.nextDouble() * 1000000.00))) // |
| .withNumberOfRepayments(String.valueOf(2 + (int) Math.ceil(secureRandom.nextDouble() * 36))) // |
| .withRepaymentAfterEvery(String.valueOf(1 + (int) Math.ceil(secureRandom.nextDouble() * 3))) // |
| .withRepaymentTypeAsMonth() // |
| .withinterestRatePerPeriod(String.valueOf(1 + (int) Math.ceil(secureRandom.nextDouble() * 4))) // |
| .withInterestRateFrequencyTypeAsMonths() // |
| .withAmortizationTypeAsEqualPrincipalPayment() // |
| .withInterestTypeAsDecliningBalance() // |
| .currencyDetails("0", "100").build(null); |
| |
| loanProducts[i] = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON); |
| } |
| |
| // Select anyone of the loan products at random |
| final Integer loanProductID = loanProducts[(int) Math.floor(secureRandom.nextDouble() * (loansCount - 1))]; |
| |
| final List<BatchRequest> batchRequests = new ArrayList<>(); |
| |
| // Select a few clients from created group at random |
| Integer selClientsCount = (int) Math.ceil(secureRandom.nextDouble() * clientsCount) + 2; |
| for (int i = 0; i < selClientsCount; i++) { |
| |
| final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec); |
| Assertions.assertNotNull(collateralId); |
| final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec, |
| String.valueOf(clientIDs[(int) Math.floor(secureRandom.nextDouble() * (clientsCount - 1))]), collateralId); |
| Assertions.assertNotNull(clientCollateralId); |
| |
| BatchRequest br = BatchHelper.applyLoanRequest((long) selClientsCount, null, loanProductID, clientCollateralId); |
| br.setBody(br.getBody().replace("$.clientId", |
| String.valueOf(clientIDs[(int) Math.floor(secureRandom.nextDouble() * (clientsCount - 1))]))); |
| batchRequests.add(br); |
| } |
| |
| // Send the request to Batch - API |
| final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests); |
| |
| final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec, |
| jsonifiedRequest); |
| |
| // Verify that each loan has been applied successfully |
| for (BatchResponse res : response) { |
| Assertions.assertEquals(200L, (long) res.getStatusCode(), "Verify Status Code 200"); |
| } |
| } |
| |
| @Test |
| public void shouldReturnOkStatusWithIdempotencySupport() { |
| |
| // Generate a random count of number of clients to be created |
| final Integer clientsCount = (int) Math.ceil(secureRandom.nextDouble() * 7) + 3; |
| final Integer[] clientIDs = new Integer[clientsCount]; |
| |
| // Create a new group and get its groupId |
| Integer groupID = GroupHelper.createGroup(this.requestSpec, this.responseSpec, true); |
| |
| // Create new clients and add those to this group |
| for (Integer i = 0; i < clientsCount; i++) { |
| clientIDs[i] = ClientHelper.createClient(this.requestSpec, this.responseSpec); |
| groupID = GroupHelper.associateClient(this.requestSpec, this.responseSpec, groupID.toString(), clientIDs[i].toString()); |
| LOG.info("client {} has been added to the group {}", clientIDs[i], groupID); |
| } |
| |
| // Generate a random count of number of new loan products to be created |
| final Integer loansCount = (int) Math.ceil(secureRandom.nextDouble() * 4) + 1; |
| final Integer[] loanProducts = new Integer[loansCount]; |
| |
| // Create new loan Products |
| LoanTransactionHelper helper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); |
| for (Integer i = 0; i < loansCount; i++) { |
| final String loanProductJSON = new LoanProductTestBuilder() // |
| .withPrincipal(String.valueOf(10000.00 + Math.ceil(secureRandom.nextDouble() * 1000000.00))) // |
| .withNumberOfRepayments(String.valueOf(2 + (int) Math.ceil(secureRandom.nextDouble() * 36))) // |
| .withRepaymentAfterEvery(String.valueOf(1 + (int) Math.ceil(secureRandom.nextDouble() * 3))) // |
| .withRepaymentTypeAsMonth() // |
| .withinterestRatePerPeriod(String.valueOf(1 + (int) Math.ceil(secureRandom.nextDouble() * 4))) // |
| .withInterestRateFrequencyTypeAsMonths() // |
| .withAmortizationTypeAsEqualPrincipalPayment() // |
| .withInterestTypeAsDecliningBalance() // |
| .currencyDetails("0", "100").build(null); |
| |
| loanProducts[i] = helper.getLoanProductId(loanProductJSON); |
| } |
| |
| // Select anyone of the loan products at random |
| final Integer loanProductID = loanProducts[(int) Math.floor(secureRandom.nextDouble() * (loansCount - 1))]; |
| |
| final List<BatchRequest> batchRequests = new ArrayList<>(); |
| |
| // Select a few clients from created group at random |
| Integer selClientsCount = (int) Math.ceil(secureRandom.nextDouble() * clientsCount) + 2; |
| for (int i = 0; i < selClientsCount; i++) { |
| |
| final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec); |
| Assertions.assertNotNull(collateralId); |
| final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec, |
| String.valueOf(clientIDs[(int) Math.floor(secureRandom.nextDouble() * (clientsCount - 1))]), collateralId); |
| Assertions.assertNotNull(clientCollateralId); |
| |
| BatchRequest br = BatchHelper.applyLoanRequest((long) selClientsCount, null, loanProductID, clientCollateralId); |
| br.setBody(br.getBody().replace("$.clientId", |
| String.valueOf(clientIDs[(int) Math.floor(secureRandom.nextDouble() * (clientsCount - 1))]))); |
| br.setHeaders(new HashSet<>()); |
| br.getHeaders().add(new Header("Idempotency-Key", UUID.randomUUID().toString())); |
| batchRequests.add(br); |
| } |
| |
| // Send the request to Batch - API |
| final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests); |
| |
| final List<BatchResponse> response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec, |
| jsonifiedRequest); |
| |
| // Verify that each loan has been applied successfully |
| for (BatchResponse res : response) { |
| Assertions.assertFalse( |
| res.getHeaders().stream() |
| .anyMatch(header -> header.getName().equals(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER)), |
| "First can not be cached!"); |
| Assertions.assertEquals(200L, (long) res.getStatusCode(), "Verify Status Code 200"); |
| } |
| |
| final List<BatchResponse> secondResponse = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, |
| this.responseSpec, jsonifiedRequest); |
| |
| // Verify that each loan has been applied successfully |
| for (BatchResponse res : secondResponse) { |
| Assertions.assertEquals("true", |
| res.getHeaders().stream() |
| .filter(header -> header.getName().equals(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER)) |
| .map(Header::getValue).findAny().get(), |
| "Not cached by idempotency key!"); |
| Assertions.assertEquals(200L, (long) res.getStatusCode(), "Verify Status Code 200"); |
| } |
| } |
| } |