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
@@ -0,0 +1,246 @@
/**
* 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.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.google.gson.Gson;
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.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
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;

/**
* Integration tests for Credit Bureau Configuration API validation.
* <p>
* Tests verify that the API correctly rejects requests with missing or invalid mandatory fields, returning HTTP 400 Bad
* Request with appropriate validation error messages.
* </p>
*/
@Slf4j
public class CreditBureauConfigurationValidationTest {

private ResponseSpecification responseSpec200;
private ResponseSpecification responseSpec400;
private RequestSpecification requestSpec;
private LoanTransactionHelper loanTransactionHelper;

// 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() {
Utils.initializeRESTAssured();
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec200 = new ResponseSpecBuilder().expectStatusCode(200).build();
this.responseSpec400 = new ResponseSpecBuilder().expectStatusCode(400).build();
this.loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec200);

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);

Object response = CreditBureauConfigurationHelper.createCreditBureauConfigurationWithResponse(requestSpec, responseSpec400,
validOrganisationCreditBureauId, jsonBody);

assertValidationError(response, 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);

Object response = CreditBureauConfigurationHelper.createCreditBureauConfigurationWithResponse(requestSpec, responseSpec400,
validOrganisationCreditBureauId, jsonBody);

assertValidationError(response, "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);

Object response = CreditBureauConfigurationHelper.createCreditBureauConfigurationWithResponse(requestSpec, responseSpec400,
validOrganisationCreditBureauId, jsonBody);

assertValidationError(response, "configkey");
}

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

Object response = CreditBureauConfigurationHelper.addOrganisationCreditBureauWithResponse(requestSpec, responseSpec400,
VALID_CREDIT_BUREAU_ID, jsonBody);

assertValidationError(response, "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);

Object response = CreditBureauConfigurationHelper.addOrganisationCreditBureauWithResponse(requestSpec, responseSpec400,
VALID_CREDIT_BUREAU_ID, jsonBody);

assertValidationError(response, "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);

Object response = CreditBureauConfigurationHelper.addOrganisationCreditBureauWithResponse(requestSpec, responseSpec400,
VALID_CREDIT_BUREAU_ID, jsonBody);

assertValidationError(response, "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);

Object response = CreditBureauConfigurationHelper.createLoanProductMappingWithResponse(requestSpec, responseSpec400,
validOrganisationCreditBureauId, jsonBody);

assertValidationError(response, fieldToOmit);
}

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

Object response = CreditBureauConfigurationHelper.createLoanProductMappingWithResponse(requestSpec, responseSpec400,
validOrganisationCreditBureauId, jsonBody);

assertValidationError(response, "loanProductId");
}

private void ensureOrganisationCreditBureauExists() {
final Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("alias", "Test Credit Bureau " + System.currentTimeMillis());
jsonMap.put("isActive", true);
final String jsonBody = new Gson().toJson(jsonMap);
final String url = "/fineract-provider/api/v1/CreditBureauConfiguration/organisationCreditBureau/" + VALID_CREDIT_BUREAU_ID + "?"
+ Utils.TENANT_IDENTIFIER;
Integer resourceId = Utils.performServerPost(requestSpec, responseSpec200, url, jsonBody, "resourceId");
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() {
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;
}

@SuppressWarnings("unchecked")
private void assertValidationError(Object response, String expectedFieldInError) {
assertNotNull(response, "Response should not be null");
Map<String, Object> responseMap;
if (response instanceof String) {
responseMap = new Gson().fromJson((String) response, Map.class);
} else {
responseMap = (Map<String, Object>) response;
}

assertTrue(responseMap.containsKey("errors"), "Response should contain 'errors' key");

List<Map<String, Object>> errors = (List<Map<String, Object>>) responseMap.get("errors");
assertNotNull(errors, "Errors list should not be null");
assertTrue(!errors.isEmpty(), "Errors list should not be empty");

boolean foundExpectedError = errors.stream().anyMatch(error -> {
String parameterName = (String) error.get("parameterName");
String userMessageGlobalisationCode = (String) error.get("userMessageGlobalisationCode");
return (parameterName != null && parameterName.contains(expectedFieldInError))
|| (userMessageGlobalisationCode != null && userMessageGlobalisationCode.contains(expectedFieldInError));
});

assertTrue(foundExpectedError, String.format("Expected validation error for field '%s' but got: %s", expectedFieldInError, errors));
log.info("Received expected validation error for field '{}': {}", expectedFieldInError, errors);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,34 @@ public static String updateCreditBureauConfigurationAsJson(final String configKe
return new Gson().toJson(map);
}

// TODO: Rewrite to use fineract-client instead!
@Deprecated(forRemoval = true)
public static Object createCreditBureauConfigurationWithResponse(final RequestSpecification requestSpec,
final ResponseSpecification responseSpec, final Long creditBureauId, final String jsonBody) {
final String url = "/fineract-provider/api/v1/CreditBureauConfiguration/configuration/" + creditBureauId + "?"
+ Utils.TENANT_IDENTIFIER;
LOG.info("URL: {} - Body: {}", url, jsonBody);
return Utils.performServerPost(requestSpec, responseSpec, url, jsonBody, null);
}

// TODO: Rewrite to use fineract-client instead!
@Deprecated(forRemoval = true)
public static Object addOrganisationCreditBureauWithResponse(final RequestSpecification requestSpec,
final ResponseSpecification responseSpec, final Long creditBureauId, final String jsonBody) {
final String url = "/fineract-provider/api/v1/CreditBureauConfiguration/organisationCreditBureau/" + creditBureauId + "?"
+ Utils.TENANT_IDENTIFIER;
LOG.info("URL: {} - Body: {}", url, jsonBody);
return Utils.performServerPost(requestSpec, responseSpec, url, jsonBody, null);
}

// TODO: Rewrite to use fineract-client instead!
@Deprecated(forRemoval = true)
public static Object createLoanProductMappingWithResponse(final RequestSpecification requestSpec,
final ResponseSpecification responseSpec, final Long organisationCreditBureauId, final String jsonBody) {
final String url = "/fineract-provider/api/v1/CreditBureauConfiguration/mappings/" + organisationCreditBureauId + "?"
+ Utils.TENANT_IDENTIFIER;
LOG.info("URL: {} - Body: {}", url, jsonBody);
return Utils.performServerPost(requestSpec, responseSpec, url, jsonBody, null);
}

}
Loading