| /** |
| * 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.interoperation; |
| |
| import static org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper.ACCOUNT_TYPE_INDIVIDUAL; |
| import static org.apache.fineract.integrationtests.interoperation.InteropHelper.PARAM_ACCOUNT_BALANCE; |
| |
| import io.restassured.builder.RequestSpecBuilder; |
| import io.restassured.builder.ResponseSpecBuilder; |
| import io.restassured.http.ContentType; |
| import io.restassured.internal.common.path.ObjectConverter; |
| import io.restassured.path.json.JsonPath; |
| import io.restassured.specification.RequestSpecification; |
| import io.restassured.specification.ResponseSpecification; |
| import java.math.BigDecimal; |
| import java.math.MathContext; |
| import java.math.RoundingMode; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.UUID; |
| import org.apache.fineract.infrastructure.core.service.MathUtil; |
| import org.apache.fineract.integrationtests.common.ClientHelper; |
| import org.apache.fineract.integrationtests.common.Utils; |
| import org.apache.fineract.integrationtests.common.accounting.Account; |
| import org.apache.fineract.integrationtests.common.accounting.AccountHelper; |
| import org.apache.fineract.integrationtests.common.charges.ChargesHelper; |
| import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper; |
| import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper; |
| import org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker; |
| import org.apache.fineract.interoperation.domain.InteropActionState; |
| import org.apache.fineract.interoperation.domain.InteropIdentifierType; |
| import org.apache.fineract.interoperation.domain.InteropTransactionRole; |
| import org.apache.fineract.interoperation.util.InteropUtil; |
| import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; |
| import org.apache.fineract.portfolio.savings.SavingsApiConstants; |
| 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; |
| |
| public class InteropTest { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(InteropTest.class); |
| |
| private static final String MIN_INTEREST_CALCULATON_BALANCE = null; |
| private static final String MIN_REQUIRED_BALANCE = null; |
| private static final String MIN_OPENING_BALANCE = "100000.0"; |
| private static final boolean ENFORCE_MIN_REQUIRED_BALANCE = false; |
| private static final MathContext MATHCONTEXT = new MathContext(12, RoundingMode.HALF_EVEN); |
| |
| private RequestSpecification requestSpec; |
| private ResponseSpecification responseSpec; |
| private ResponseSpecification responseClientErrorSpec; |
| private ResponseSpecification responseNotFoundErrorSpec; |
| private ResponseSpecification responseForbiddenErrorSpec; |
| |
| private AccountHelper accountHelper; |
| private SavingsAccountHelper savingsAccountHelper; |
| private InteropHelper interopHelper; |
| |
| private Integer clientId; |
| private Integer savingsProductId; |
| private Integer savingsId; |
| private Integer chargeId; |
| private String requestCode; |
| private String quoteCode; |
| private String transferCode; |
| |
| @BeforeEach |
| public void setup() { |
| Utils.initializeRESTAssured(); |
| requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); |
| requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); |
| |
| responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); |
| responseClientErrorSpec = new ResponseSpecBuilder().expectStatusCode(400).build(); |
| responseForbiddenErrorSpec = new ResponseSpecBuilder().expectStatusCode(403).build(); |
| responseNotFoundErrorSpec = new ResponseSpecBuilder().expectStatusCode(404).build(); |
| |
| String savingsExternalId = UUID.randomUUID().toString(); |
| String transactionCode = UUID.randomUUID().toString(); |
| |
| accountHelper = new AccountHelper(this.requestSpec, this.responseSpec); |
| savingsAccountHelper = new SavingsAccountHelper(requestSpec, responseSpec); |
| interopHelper = new InteropHelper(requestSpec, responseSpec, savingsExternalId, transactionCode); |
| } |
| |
| @Test |
| public void testValidateAction() { |
| interopHelper.setResponseSpec(responseClientErrorSpec); |
| interopHelper.postTransferMissingAction(UUID.randomUUID().toString(), InteropTransactionRole.PAYER); |
| interopHelper.postTransfer(UUID.randomUUID().toString(), null, InteropTransactionRole.PAYER); |
| interopHelper.setResponseSpec(responseSpec); |
| } |
| |
| @Test |
| public void testInteroperation() { |
| createClient(); |
| createSavingsProduct(); |
| createCharge(); |
| openSavingsAccount(); |
| |
| testParties(); |
| testRequests(); |
| testQuotes(); |
| testTransfers(); |
| } |
| |
| private void createClient() { |
| clientId = ClientHelper.createClient(requestSpec, responseSpec); |
| Assertions.assertNotNull(clientId); |
| } |
| |
| private void createSavingsProduct() { |
| LOG.debug("------------------------------ Create Interoperable Saving Product ---------------------------------------"); |
| |
| Account[] accounts = { accountHelper.createAssetAccount(), accountHelper.createIncomeAccount(), |
| accountHelper.createExpenseAccount(), accountHelper.createLiabilityAccount() }; |
| |
| SavingsProductHelper savingsProductHelper = new SavingsProductHelper(); |
| final String savingsProductJSON = savingsProductHelper.withCurrencyCode(interopHelper.getCurrency()) |
| .withNominalAnnualInterestRate(BigDecimal.ZERO).withInterestCompoundingPeriodTypeAsDaily() |
| .withInterestPostingPeriodTypeAsMonthly().withInterestCalculationPeriodTypeAsDailyBalance() |
| .withMinBalanceForInterestCalculation(MIN_INTEREST_CALCULATON_BALANCE).withMinRequiredBalance(MIN_REQUIRED_BALANCE) |
| .withEnforceMinRequiredBalance(Boolean.toString(ENFORCE_MIN_REQUIRED_BALANCE)) |
| .withMinimumOpenningBalance(MIN_OPENING_BALANCE).withAccountingRuleAsCashBased(accounts).build(); |
| savingsProductId = SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec); |
| Assertions.assertNotNull(savingsProductId); |
| |
| LOG.debug("Sucessfully created Interoperable Saving Product (id: {})", savingsProductId); |
| } |
| |
| private void createCharge() { |
| chargeId = ChargesHelper.createCharges(requestSpec, responseSpec, ChargesHelper.getSavingsJSON(interopHelper.getFee().toString(), |
| interopHelper.getCurrency(), ChargeTimeType.WITHDRAWAL_FEE)); |
| Assertions.assertNotNull(chargeId); |
| } |
| |
| private void openSavingsAccount() { |
| LOG.debug("------------------------------ Create Interoperable Saving Account ---------------------------------------"); |
| savingsId = savingsAccountHelper.applyForSavingsApplicationWithExternalId(clientId, savingsProductId, ACCOUNT_TYPE_INDIVIDUAL, |
| interopHelper.getAccountExternalId(), true); |
| Assertions.assertNotNull(savingsId); |
| |
| HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(requestSpec, responseSpec, savingsId); |
| SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap); |
| |
| savingsStatusHashMap = savingsAccountHelper.approveSavings(savingsId); |
| SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap); |
| |
| savingsStatusHashMap = savingsAccountHelper.activateSavings(savingsId); |
| SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap); |
| |
| if (chargeId != null) { |
| savingsAccountHelper.addChargesForSavings(savingsId, chargeId, false, interopHelper.getFee()); |
| } |
| |
| LOG.debug("Sucessfully created Interoperable Saving Account (id: {})", savingsId); |
| } |
| |
| private void testParties() { |
| String idValue = UUID.randomUUID().toString(); |
| String accountId = interopHelper.postParty(InteropIdentifierType.MSISDN, idValue); |
| Assertions.assertEquals(interopHelper.getAccountExternalId(), accountId); |
| |
| interopHelper.setResponseSpec(responseForbiddenErrorSpec); |
| accountId = interopHelper.postParty(InteropIdentifierType.MSISDN, idValue); |
| Assertions.assertNull(accountId); |
| interopHelper.setResponseSpec(responseSpec); |
| |
| accountId = interopHelper.getParty(InteropIdentifierType.MSISDN, idValue); |
| Assertions.assertEquals(interopHelper.getAccountExternalId(), accountId); |
| |
| accountId = interopHelper.deleteParty(InteropIdentifierType.MSISDN, idValue); |
| Assertions.assertEquals(interopHelper.getAccountExternalId(), accountId); |
| |
| interopHelper.setResponseSpec(responseNotFoundErrorSpec); |
| accountId = interopHelper.getParty(InteropIdentifierType.MSISDN, idValue); |
| Assertions.assertNull(accountId); |
| interopHelper.setResponseSpec(responseSpec); |
| } |
| |
| private void testRequests() { |
| requestCode = UUID.randomUUID().toString(); |
| String response = interopHelper.postTransactionRequest(requestCode, InteropTransactionRole.PAYER); |
| JsonPath json = JsonPath.from(response); |
| Assertions.assertEquals(requestCode, json.getString(InteropUtil.PARAM_REQUEST_CODE)); |
| Assertions.assertEquals(InteropActionState.ACCEPTED.toString(), json.getString(InteropHelper.PARAM_ACTION_STATE)); |
| |
| interopHelper.setResponseSpec(responseClientErrorSpec); |
| interopHelper.postTransactionRequest(requestCode, InteropTransactionRole.PAYEE); |
| interopHelper.setResponseSpec(responseSpec); |
| } |
| |
| private void testQuotes() { |
| // payer |
| quoteCode = UUID.randomUUID().toString(); |
| String response = interopHelper.postQuote(quoteCode, InteropTransactionRole.PAYER); |
| JsonPath json = JsonPath.from(response); |
| Assertions.assertEquals(quoteCode, json.getString(InteropUtil.PARAM_QUOTE_CODE)); |
| Assertions.assertEquals(InteropActionState.ACCEPTED.toString(), json.getString(InteropHelper.PARAM_ACTION_STATE)); |
| |
| Map<Object, Object> fee = json.getMap(InteropUtil.PARAM_FSP_FEE); |
| Assertions.assertNotNull(fee); |
| BigDecimal feeAmount = ObjectConverter.convertObjectTo(fee.get(InteropUtil.PARAM_AMOUNT), BigDecimal.class); |
| Assertions.assertTrue(MathUtil.isEqualTo(interopHelper.getFee(), feeAmount), |
| "Quote fee expected: " + interopHelper.getFee() + ", actual: " + feeAmount); |
| Assertions.assertEquals(interopHelper.getCurrency(), fee.get(InteropUtil.PARAM_CURRENCY)); |
| |
| // payee |
| response = interopHelper.postQuote(quoteCode, InteropTransactionRole.PAYEE); |
| json = JsonPath.from(response); |
| Assertions.assertEquals(quoteCode, json.getString(InteropUtil.PARAM_QUOTE_CODE)); |
| Assertions.assertEquals(InteropActionState.ACCEPTED.toString(), json.getString(InteropHelper.PARAM_ACTION_STATE)); |
| |
| fee = json.getMap(InteropUtil.PARAM_FSP_FEE); |
| if (fee != null) { |
| feeAmount = ObjectConverter.convertObjectTo(fee.get(InteropUtil.PARAM_AMOUNT), BigDecimal.class); |
| Assertions.assertTrue(MathUtil.isZero(feeAmount), "PAYEE Quote fee expected: " + BigDecimal.ZERO + ", actual: " + feeAmount); |
| } |
| } |
| |
| private void testTransfers() { |
| String savings = (String) savingsAccountHelper.getSavingsAccountDetail(savingsId, null); |
| JsonPath savingsJson = JsonPath.from(savings); |
| BigDecimal onHold = ObjectConverter.convertObjectTo(savingsJson.get(SavingsApiConstants.savingsAmountOnHold), BigDecimal.class); |
| BigDecimal balance = ObjectConverter.convertObjectTo(savingsJson.get(PARAM_ACCOUNT_BALANCE), BigDecimal.class); |
| |
| transferCode = UUID.randomUUID().toString(); |
| String response = interopHelper.prepareTransfer(transferCode); |
| JsonPath json = JsonPath.from(response); |
| Assertions.assertEquals(transferCode, json.getString(InteropUtil.PARAM_TRANSFER_CODE)); |
| Assertions.assertEquals(InteropActionState.ACCEPTED.toString(), json.getString(InteropHelper.PARAM_ACTION_STATE)); |
| |
| // prepare |
| savings = (String) savingsAccountHelper.getSavingsAccountDetail(savingsId, null); |
| LOG.debug("Response Interoperable GET Saving: {}", savings); |
| savingsJson = JsonPath.from(savings); |
| BigDecimal onHold2 = ObjectConverter.convertObjectTo(savingsJson.get(SavingsApiConstants.savingsAmountOnHold), BigDecimal.class); |
| BigDecimal balance2 = ObjectConverter.convertObjectTo(savingsJson.get(PARAM_ACCOUNT_BALANCE), BigDecimal.class); |
| |
| BigDecimal transferAmount = interopHelper.getTransferAmount(); |
| BigDecimal expectedHold = MathUtil.add(onHold, transferAmount, MATHCONTEXT); |
| Assertions.assertTrue(MathUtil.isEqualTo(expectedHold, onHold2), |
| "On hold amount expected: " + expectedHold + ", actual: " + onHold2); |
| BigDecimal expectedBalance = MathUtil.subtract(balance, transferAmount, MATHCONTEXT); |
| Assertions.assertTrue(MathUtil.isEqualTo(expectedBalance, balance2), |
| "Balance amount expected: " + expectedBalance + ", actual: " + balance2); |
| |
| // payer |
| response = interopHelper.createTransfer(transferCode, InteropTransactionRole.PAYER); |
| json = JsonPath.from(response); |
| Assertions.assertEquals(transferCode, json.getString(InteropUtil.PARAM_TRANSFER_CODE)); |
| Assertions.assertEquals(InteropActionState.ACCEPTED.toString(), json.getString(InteropHelper.PARAM_ACTION_STATE)); |
| |
| savings = (String) savingsAccountHelper.getSavingsAccountDetail(savingsId, null); |
| LOG.debug("Response Interoperable GET Saving: {}", savings); |
| savingsJson = JsonPath.from(savings); |
| BigDecimal onHold3 = ObjectConverter.convertObjectTo(savingsJson.get(SavingsApiConstants.savingsAmountOnHold), BigDecimal.class); |
| BigDecimal balance3 = ObjectConverter.convertObjectTo(savingsJson.get(PARAM_ACCOUNT_BALANCE), BigDecimal.class); |
| Assertions.assertTrue(MathUtil.isEqualTo(onHold, onHold3), "On hold amount expected: " + onHold + ", actual: " + onHold3); |
| Assertions.assertTrue(MathUtil.isEqualTo(expectedBalance, balance3), |
| "Balance amount expected: " + expectedBalance + ", actual: " + balance3); |
| |
| // payee |
| response = interopHelper.createTransfer(transferCode, InteropTransactionRole.PAYEE); |
| json = JsonPath.from(response); |
| Assertions.assertEquals(transferCode, json.getString(InteropUtil.PARAM_TRANSFER_CODE)); |
| Assertions.assertEquals(InteropActionState.ACCEPTED.toString(), json.getString(InteropHelper.PARAM_ACTION_STATE)); |
| |
| savings = (String) savingsAccountHelper.getSavingsAccountDetail(savingsId, null); |
| LOG.debug("Response Interoperable GET Saving: {}", savings); |
| savingsJson = JsonPath.from(savings); |
| BigDecimal onHold4 = ObjectConverter.convertObjectTo(savingsJson.get(SavingsApiConstants.savingsAmountOnHold), BigDecimal.class); |
| BigDecimal balance4 = ObjectConverter.convertObjectTo(savingsJson.get(PARAM_ACCOUNT_BALANCE), BigDecimal.class); |
| expectedBalance = MathUtil.subtract(balance, interopHelper.getFee(), MATHCONTEXT); |
| Assertions.assertTrue(MathUtil.isEqualTo(onHold, onHold4), "On hold amount expected: " + onHold + ", actual: " + onHold4); |
| Assertions.assertTrue(MathUtil.isEqualTo(balance, balance4), |
| "Balance amount expected: " + expectedBalance + ", actual: " + balance4); |
| } |
| } |