From b1dc17ccad41960bb6936831d1e76a4db151a57b Mon Sep 17 00:00:00 2001 From: Shivd131 Date: Tue, 20 Jan 2026 13:53:50 +0530 Subject: [PATCH] FINERACT-2443: Add test cases for reports --- .../ReportExecutionIntegrationTest.java | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 integration-tests/src/test/java/org/apache/fineract/integrationtests/ReportExecutionIntegrationTest.java diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ReportExecutionIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ReportExecutionIntegrationTest.java new file mode 100644 index 00000000000..de052245807 --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ReportExecutionIntegrationTest.java @@ -0,0 +1,154 @@ +/** + * 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 java.io.IOException; +import java.math.BigDecimal; +import java.util.Map; +import java.util.UUID; +import okhttp3.MediaType; +import okhttp3.ResponseBody; +import org.apache.fineract.client.models.PostClientsRequest; +import org.apache.fineract.client.models.PostLoanProductsRequest; +import org.apache.fineract.client.models.PostLoanProductsResponse; +import org.apache.fineract.client.models.PostLoansLoanIdRequest; +import org.apache.fineract.client.models.PostLoansRequest; +import org.apache.fineract.client.models.PostLoansResponse; +import org.apache.fineract.client.models.RunReportsResponse; +import org.apache.fineract.integrationtests.client.IntegrationTest; +import org.apache.fineract.integrationtests.common.Utils; +import org.junit.jupiter.api.Test; +import retrofit2.Response; + +/** + * End-to-End Integration Test for Report Execution and Observability. Verifies that the Reporting engine correctly + * queries and outputs data for Clients and Loans. + */ +public class ReportExecutionIntegrationTest extends IntegrationTest { + + @Test + public void verifyClientListingReportAndMetrics() throws IOException { + String uniqueClientName = "ReportTestClient_" + UUID.randomUUID().toString().substring(0, 8); + Long clientId = createClient(uniqueClientName); + assertThat(clientId).isNotNull(); + + // Run the "Client Listing" Report (Table/JSON format) + Response response = okR( + fineractClient().reportsRun.runReportGetData("Client Listing", Map.of("R_officeId", "1"), false)); + + assertThat(response.code()).isEqualTo(200); + RunReportsResponse body = response.body(); + assertThat(body.getColumnHeaders()).isNotEmpty(); + // Verify our created client is in the output + assertThat(body.getData().toString()).contains(uniqueClientName); + + // Run the "Client Listing" Report (CSV format) + Response csvResponse = okR( + fineractClient().reportsRun.runReportGetFile("Client Listing", Map.of("R_officeId", "1", "exportCSV", "true"), false)); + + assertThat(csvResponse.body().contentType()).isEqualTo(MediaType.parse("text/csv")); + String csvContent = csvResponse.body().string(); + assertThat(csvContent).contains("Office/Branch"); + assertThat(csvContent).contains(uniqueClientName); + } + + @Test + public void verifyLoanListingReport() throws IOException { + // 1. Setup Data: Client -> Loan Product -> Loan Application -> Approval -> Disbursement + String uniqueClientName = "LoanReportClient_" + UUID.randomUUID().toString().substring(0, 8); + Long clientId = createClient(uniqueClientName); + + // Create a simple Loan Product + Long loanProductId = createLoanProduct(); + + // Apply for a Loan + Long loanId = applyForLoan(clientId, loanProductId); + + // Approve the Loan + approveLoan(loanId); + + // Disburse the Loan (CRITICAL: Required for "Active Loans" report) + disburseLoan(loanId, BigDecimal.valueOf(1000.0)); + + // 2. Execution: Run "Active Loans - Details" Report + Response response = okR( + fineractClient().reportsRun.runReportGetData("Active Loans - Details", Map.of("R_officeId", "1", "R_loanOfficerId", "-1", + "R_currencyId", "-1", "R_fundId", "-1", "R_loanProductId", "-1", "R_loanPurposeId", "-1"), false)); + + // 3. Verification + assertThat(response.code()).isEqualTo(200); + RunReportsResponse body = response.body(); + + // Verify the generic report structure works + assertThat(body.getColumnHeaders()).isNotEmpty(); + + // Verify our specific Loan ID and Client Name are present in the JSON data + String responseString = body.getData().toString(); + + // Assert that the list actually contains data now + assertThat(responseString).as("Report should contain the active loan ID").contains(String.valueOf(loanId)); + assertThat(responseString).as("Report should contain the client name").contains(uniqueClientName); + } + + // --- Helpers --- + + private Long createClient(String fullName) { + return ok(fineractClient().clients.create6(new PostClientsRequest().legalFormId(1L).officeId(1L).fullname(fullName) + .dateFormat(Utils.DATE_FORMAT).locale("en_US").active(true).activationDate("01 January 2023"))).getClientId(); + } + + private Long applyForLoan(Long clientId, Long loanProductId) { + PostLoansResponse response = ok(fineractClient().loans.calculateLoanScheduleOrSubmitLoanApplication( + new PostLoansRequest().dateFormat(Utils.DATE_FORMAT).locale("en_US").clientId(clientId).productId(loanProductId) + .principal(BigDecimal.valueOf(1000.0)).loanTermFrequency(12).loanTermFrequencyType(2) // Months + .numberOfRepayments(12).repaymentEvery(1).repaymentFrequencyType(2) // Months + .interestRatePerPeriod(BigDecimal.valueOf(1.5)).amortizationType(1) // Equal Installments + .interestType(1) // Flat + .interestCalculationPeriodType(1) // Same as repayment period + .transactionProcessingStrategyCode("mifos-standard-strategy").expectedDisbursementDate("01 January 2023") + .submittedOnDate("01 January 2023").loanType("individual"), + "application")); + return response.getLoanId(); + } + + private void approveLoan(Long loanId) { + ok(fineractClient().loans.stateTransitions(loanId, + new PostLoansLoanIdRequest().approvedOnDate("01 January 2023").dateFormat(Utils.DATE_FORMAT).locale("en_US"), "approve")); + } + + private void disburseLoan(Long loanId, BigDecimal amount) { + ok(fineractClient().loans.stateTransitions(loanId, new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023") + .dateFormat(Utils.DATE_FORMAT).locale("en_US").transactionAmount(amount), "disburse")); + } + + private Long createLoanProduct() { + PostLoanProductsResponse response = ok(fineractClient().loanProducts.createLoanProduct(new PostLoanProductsRequest() + .name(Utils.uniqueRandomStringGenerator("Report_Product_", 6)).shortName(Utils.uniqueRandomStringGenerator("", 4)) + .description("Loan Product for Report Tests").currencyCode("USD").digitsAfterDecimal(2).inMultiplesOf(1) + .installmentAmountInMultiplesOf(1).minPrincipal(100.0).principal(1000.0).maxPrincipal(10000.0).numberOfRepayments(12) + .repaymentEvery(1).repaymentFrequencyType(2L) // Months + .interestRatePerPeriod(1.5).interestRateFrequencyType(2) // Months + .amortizationType(1) // Equal installments + .interestType(1) // Flat + .interestCalculationPeriodType(1) // Same as repayment period + .transactionProcessingStrategyCode("mifos-standard-strategy").accountingRule(1) // None + .daysInYearType(1).daysInMonthType(1).isInterestRecalculationEnabled(false).dateFormat(Utils.DATE_FORMAT).locale("en_US"))); + return response.getResourceId(); + } +}