Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.apache.fineract.client.services.ClientsAddressApi;
import org.apache.fineract.client.services.CodeValuesApi;
import org.apache.fineract.client.services.CodesApi;
import org.apache.fineract.client.services.CreditBureauConfigurationApi;
import org.apache.fineract.client.services.CurrencyApi;
import org.apache.fineract.client.services.DataTablesApi;
import org.apache.fineract.client.services.DefaultApi;
Expand Down Expand Up @@ -199,6 +200,7 @@ public final class FineractClient {
public final CentersApi centers;
public final ChargesApi charges;
public final ClientApi clients;
public final CreditBureauConfigurationApi creditBureauConfiguration;

public final ClientSearchV2Api clientSearchV2;
public final ClientChargesApi clientCharges;
Expand Down Expand Up @@ -333,6 +335,7 @@ private FineractClient(OkHttpClient okHttpClient, Retrofit retrofit) {
centers = retrofit.create(CentersApi.class);
charges = retrofit.create(ChargesApi.class);
clients = retrofit.create(ClientApi.class);
creditBureauConfiguration = retrofit.create(CreditBureauConfigurationApi.class);
clientSearchV2 = retrofit.create(ClientSearchV2Api.class);
clientCharges = retrofit.create(ClientChargesApi.class);
clientIdentifiers = retrofit.create(ClientIdentifierApi.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/**
* 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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
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.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.util.CallFailedRuntimeException;
import org.apache.fineract.integrationtests.common.CreditBureauConfigurationHelper;
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.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

@Slf4j
public class CreditBureauConfigurationValidationTest {

// Prerequisites - ThitsaWorks credit bureau is seeded in DB with ID 1
private static final Long VALID_CREDIT_BUREAU_ID = 1L;
private Long validOrganisationCreditBureauId;
private Long validLoanProductId;

@BeforeEach
public void setup() {
ensureOrganisationCreditBureauExists();
this.validLoanProductId = createTestLoanProduct();
}

@ParameterizedTest(name = "Create configuration missing {0} should return 400")
@CsvSource({ "configkey, value, description", "value, configkey, description", "description, configkey, value" })
void testCreateConfiguration_MissingMandatoryFields(String fieldToOmit, String field1, String field2) {
final Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put(field1, "testValue1");
jsonMap.put(field2, "testValue2");
final String jsonBody = new Gson().toJson(jsonMap);

CallFailedRuntimeException ex = assertThrows(CallFailedRuntimeException.class,
() -> CreditBureauConfigurationHelper.createCreditBureauConfigurationRaw(validOrganisationCreditBureauId, jsonBody));

assertEquals(400, ex.getResponse().code());
assertValidationErrorInMessage(ex.getMessage(), fieldToOmit);
}

@Test
void testCreateConfiguration_BlankConfigKey_ShouldFail400() {
final Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("configkey", "");
jsonMap.put("value", "testValue");
jsonMap.put("description", "testDescription");
final String jsonBody = new Gson().toJson(jsonMap);

CallFailedRuntimeException ex = assertThrows(CallFailedRuntimeException.class,
() -> CreditBureauConfigurationHelper.createCreditBureauConfigurationRaw(validOrganisationCreditBureauId, jsonBody));

assertEquals(400, ex.getResponse().code());
assertValidationErrorInMessage(ex.getMessage(), "configkey");
}

@Test
void testCreateConfiguration_ExceedingLength_ShouldFail400() {
final String longValue = "a".repeat(101);
final Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("configkey", longValue);
jsonMap.put("value", "testValue");
jsonMap.put("description", "testDescription");
final String jsonBody = new Gson().toJson(jsonMap);

CallFailedRuntimeException ex = assertThrows(CallFailedRuntimeException.class,
() -> CreditBureauConfigurationHelper.createCreditBureauConfigurationRaw(validOrganisationCreditBureauId, jsonBody));

assertEquals(400, ex.getResponse().code());
assertValidationErrorInMessage(ex.getMessage(), "configkey");
}

@Test
void testAddOrganisationCreditBureau_MissingAlias_ShouldFail400() {
final Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("isActive", true);
final String jsonBody = new Gson().toJson(jsonMap);

CallFailedRuntimeException ex = assertThrows(CallFailedRuntimeException.class,
() -> CreditBureauConfigurationHelper.addOrganisationCreditBureauRaw(VALID_CREDIT_BUREAU_ID, jsonBody));

assertEquals(400, ex.getResponse().code());
assertValidationErrorInMessage(ex.getMessage(), "alias");
}

@Test
void testAddOrganisationCreditBureau_BlankAlias_ShouldFail400() {
final Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("alias", "");
jsonMap.put("isActive", true);
final String jsonBody = new Gson().toJson(jsonMap);

CallFailedRuntimeException ex = assertThrows(CallFailedRuntimeException.class,
() -> CreditBureauConfigurationHelper.addOrganisationCreditBureauRaw(VALID_CREDIT_BUREAU_ID, jsonBody));

assertEquals(400, ex.getResponse().code());
assertValidationErrorInMessage(ex.getMessage(), "alias");
}

@Test
void testAddOrganisationCreditBureau_ExceedingAliasLength_ShouldFail400() {
final String longAlias = "a".repeat(101);
final Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("alias", longAlias);
jsonMap.put("isActive", true);
final String jsonBody = new Gson().toJson(jsonMap);

CallFailedRuntimeException ex = assertThrows(CallFailedRuntimeException.class,
() -> CreditBureauConfigurationHelper.addOrganisationCreditBureauRaw(VALID_CREDIT_BUREAU_ID, jsonBody));

assertEquals(400, ex.getResponse().code());
assertValidationErrorInMessage(ex.getMessage(), "alias");
}

@ParameterizedTest(name = "Create mapping missing {0} should return 400")
@CsvSource({ "isCreditcheckMandatory", "skipCreditcheckInFailure", "stalePeriod" })
void testCreateMapping_MissingMandatoryFields(String fieldToOmit) {
final Map<String, Object> jsonMap = buildMappingJsonOmitting(fieldToOmit);
final String jsonBody = new Gson().toJson(jsonMap);

CallFailedRuntimeException ex = assertThrows(CallFailedRuntimeException.class,
() -> CreditBureauConfigurationHelper.createLoanProductMappingRaw(validOrganisationCreditBureauId, jsonBody));

assertEquals(400, ex.getResponse().code());
assertValidationErrorInMessage(ex.getMessage(), fieldToOmit);
}

@Test
void testCreateMapping_MissingLoanProductId_ShouldFail400() {
final Map<String, Object> jsonMap = buildMappingJsonOmitting("loanProductId");
final String jsonBody = new Gson().toJson(jsonMap);

CallFailedRuntimeException ex = assertThrows(CallFailedRuntimeException.class,
() -> CreditBureauConfigurationHelper.createLoanProductMappingRaw(validOrganisationCreditBureauId, jsonBody));

assertEquals(400, ex.getResponse().code());
assertValidationErrorInMessage(ex.getMessage(), "loanProductId");
}

private void ensureOrganisationCreditBureauExists() {
String response = CreditBureauConfigurationHelper.addOrganisationCreditBureau(VALID_CREDIT_BUREAU_ID,
"Test Credit Bureau " + System.currentTimeMillis(), true);
JsonObject json = JsonParser.parseString(response).getAsJsonObject();
Integer resourceId = json.get("resourceId").getAsInt();
assertNotNull(resourceId, "Organisation credit bureau creation should return resourceId");
this.validOrganisationCreditBureauId = resourceId.longValue();
log.info("Created organisation credit bureau with ID: {}", validOrganisationCreditBureauId);
}

private Long createTestLoanProduct() {
Utils.initializeRESTAssured();
final RequestSpecification requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
final ResponseSpecification responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentAfterEvery("1")
.withRepaymentTypeAsMonth().withNumberOfRepayments("1").withInterestRateFrequencyTypeAsMonths()
.withinterestRatePerPeriod("0").withInterestTypeAsDecliningBalance().withAmortizationTypeAsEqualInstallments().build(null);
return (long) loanTransactionHelper.getLoanProductId(loanProductJSON);
}

private Map<String, Object> buildMappingJsonOmitting(String fieldToOmit) {
final Map<String, Object> jsonMap = new HashMap<>();
if (!"loanProductId".equals(fieldToOmit)) {
jsonMap.put("loanProductId", validLoanProductId);
}
if (!"isCreditcheckMandatory".equals(fieldToOmit)) {
jsonMap.put("isCreditcheckMandatory", true);
}
if (!"skipCreditcheckInFailure".equals(fieldToOmit)) {
jsonMap.put("skipCreditcheckInFailure", false);
}
if (!"stalePeriod".equals(fieldToOmit)) {
jsonMap.put("stalePeriod", 30);
}
jsonMap.put("isActive", true);
return jsonMap;
}

private void assertValidationErrorInMessage(String message, String expectedFieldInError) {
assertNotNull(message, "Exception message should not be null");
assertTrue(message.contains(expectedFieldInError),
String.format("Expected validation error for field '%s' in message: %s", expectedFieldInError, message));
log.info("Received expected validation error for field '{}'", expectedFieldInError);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.fineract.client.services.CreditBureauConfigurationApi;
import org.apache.fineract.client.util.Calls;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -214,4 +216,26 @@ public static String updateCreditBureauConfigurationAsJson(final String configKe
return new Gson().toJson(map);
}

private static CreditBureauConfigurationApi api() {
return FineractClientHelper.getFineractClient().creditBureauConfiguration;
}

public static String addOrganisationCreditBureau(Long creditBureauId, String alias, boolean isActive) {
final HashMap<String, Object> map = new HashMap<>();
map.put("alias", alias);
map.put("isActive", isActive);
return Calls.ok(api().addOrganisationCreditBureau(creditBureauId, new Gson().toJson(map)));
}

public static String createCreditBureauConfigurationRaw(Long creditBureauId, String jsonBody) {
return Calls.ok(api().createCreditBureauConfiguration(creditBureauId, jsonBody));
}

public static String addOrganisationCreditBureauRaw(Long creditBureauId, String jsonBody) {
return Calls.ok(api().addOrganisationCreditBureau(creditBureauId, jsonBody));
}

public static String createLoanProductMappingRaw(Long organisationCreditBureauId, String jsonBody) {
return Calls.ok(api().createCreditBureauLoanProductMapping(organisationCreditBureauId, jsonBody));
}
}