FINERACT-2418: added e2e test covering loan origination feature
This commit is contained in:
committed by
Adam Saghy
parent
588b831f8f
commit
87e328e9e7
+662
@@ -0,0 +1,662 @@
|
||||
/**
|
||||
* 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.test.stepdef.loan;
|
||||
|
||||
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
|
||||
import static org.apache.fineract.client.feign.util.FeignCalls.fail;
|
||||
import static org.apache.fineract.client.feign.util.FeignCalls.failVoid;
|
||||
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import io.cucumber.java.en.Then;
|
||||
import io.cucumber.java.en.When;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.avro.loan.v1.OriginatorDetailsV1;
|
||||
import org.apache.fineract.client.feign.FineractFeignClient;
|
||||
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
|
||||
import org.apache.fineract.client.models.DeleteLoanOriginatorsResponse;
|
||||
import org.apache.fineract.client.models.GetCodeValuesDataResponse;
|
||||
import org.apache.fineract.client.models.GetLoanOriginatorTemplateResponse;
|
||||
import org.apache.fineract.client.models.GetLoanOriginatorsResponse;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdOriginatorData;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.models.PostLoanOriginatorsRequest;
|
||||
import org.apache.fineract.client.models.PostLoanOriginatorsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdRequest;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.PostLoansOriginatorData;
|
||||
import org.apache.fineract.client.models.PostLoansRequest;
|
||||
import org.apache.fineract.client.models.PostLoansResponse;
|
||||
import org.apache.fineract.client.models.PutLoanOriginatorsRequest;
|
||||
import org.apache.fineract.client.models.PutLoanOriginatorsResponse;
|
||||
import org.apache.fineract.test.api.ApiProperties;
|
||||
import org.apache.fineract.test.factory.LoanRequestFactory;
|
||||
import org.apache.fineract.test.helper.ErrorMessageHelper;
|
||||
import org.apache.fineract.test.messaging.EventAssertion;
|
||||
import org.apache.fineract.test.messaging.event.loan.LoanApprovedEvent;
|
||||
import org.apache.fineract.test.messaging.store.EventStore;
|
||||
import org.apache.fineract.test.stepdef.AbstractStepDef;
|
||||
import org.apache.fineract.test.support.TestContextKey;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@Slf4j
|
||||
public class LoanOriginationStepDef extends AbstractStepDef {
|
||||
|
||||
private static final long NON_EXISTENT_ID = Long.MAX_VALUE;
|
||||
|
||||
@Autowired
|
||||
private FineractFeignClient fineractClient;
|
||||
|
||||
@Autowired
|
||||
private LoanRequestFactory loanRequestFactory;
|
||||
|
||||
@Autowired
|
||||
private EventAssertion eventAssertion;
|
||||
|
||||
@Autowired
|
||||
private EventStore eventStore;
|
||||
|
||||
@Autowired
|
||||
private ApiProperties apiProperties;
|
||||
|
||||
// --- Originator CRUD steps ---
|
||||
|
||||
@When("Admin creates a new loan originator with external ID and name {string}")
|
||||
public void createOriginatorWithName(String name) {
|
||||
createOriginatorAndStore(name, null, TestContextKey.ORIGINATOR_CREATE_RESPONSE, TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
}
|
||||
|
||||
@When("Admin creates a new loan originator with external ID, name {string} and status {string}")
|
||||
public void createOriginatorWithNameAndStatus(String name, String status) {
|
||||
createOriginatorAndStore(name, status, TestContextKey.ORIGINATOR_CREATE_RESPONSE, TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
}
|
||||
|
||||
@When("Admin creates a new loan originator with all fields and name {string}")
|
||||
public void createOriginatorWithAllFields(String name) {
|
||||
GetLoanOriginatorTemplateResponse template = ok(() -> fineractClient.loanOriginators().retrieveLoanOriginatorTemplate());
|
||||
|
||||
List<GetCodeValuesDataResponse> originatorTypes = template.getOriginatorTypeOptions();
|
||||
List<GetCodeValuesDataResponse> channelTypes = template.getChannelTypeOptions();
|
||||
assertThat(originatorTypes).as("Originator type options should not be empty").isNotNull().isNotEmpty();
|
||||
assertThat(channelTypes).as("Channel type options should not be empty").isNotNull().isNotEmpty();
|
||||
|
||||
GetCodeValuesDataResponse originatorType = originatorTypes.get(0);
|
||||
GetCodeValuesDataResponse channelType = channelTypes.get(0);
|
||||
|
||||
String externalId = UUID.randomUUID().toString();
|
||||
PostLoanOriginatorsRequest request = new PostLoanOriginatorsRequest().externalId(externalId).name(name)
|
||||
.originatorTypeId(originatorType.getId()).channelTypeId(channelType.getId());
|
||||
|
||||
PostLoanOriginatorsResponse response = ok(() -> fineractClient.loanOriginators().create11(request));
|
||||
|
||||
assertThat(response.getResourceId()).isNotNull();
|
||||
testContext().set(TestContextKey.ORIGINATOR_CREATE_RESPONSE, response);
|
||||
testContext().set(TestContextKey.ORIGINATOR_EXTERNAL_ID, externalId);
|
||||
testContext().set(TestContextKey.ORIGINATOR_TYPE_NAME, originatorType.getName());
|
||||
testContext().set(TestContextKey.ORIGINATOR_CHANNEL_TYPE_NAME, channelType.getName());
|
||||
log.info("Created originator with ID {}, externalId {}, originatorType {}, channelType {}", response.getResourceId(), externalId,
|
||||
originatorType.getName(), channelType.getName());
|
||||
}
|
||||
|
||||
@When("Admin creates a second loan originator with external ID and name {string}")
|
||||
public void createSecondOriginatorWithName(String name) {
|
||||
createOriginatorAndStore(name, null, TestContextKey.ORIGINATOR_SECOND_CREATE_RESPONSE,
|
||||
TestContextKey.ORIGINATOR_SECOND_EXTERNAL_ID);
|
||||
}
|
||||
|
||||
@Then("Loan originator is created successfully with status {string}")
|
||||
public void verifyOriginatorStatus(String expectedStatus) {
|
||||
PostLoanOriginatorsResponse createResponse = testContext().get(TestContextKey.ORIGINATOR_CREATE_RESPONSE);
|
||||
Long originatorId = createResponse.getResourceId();
|
||||
|
||||
GetLoanOriginatorsResponse originator = ok(() -> fineractClient.loanOriginators().retrieveOne18(originatorId));
|
||||
|
||||
assertThat(originator.getStatus()).isEqualTo(expectedStatus);
|
||||
assertThat(originator.getExternalId()).isEqualTo(testContext().get(TestContextKey.ORIGINATOR_EXTERNAL_ID));
|
||||
}
|
||||
|
||||
@Then("Loan originator is created successfully with all fields populated")
|
||||
public void verifyOriginatorAllFields() {
|
||||
PostLoanOriginatorsResponse createResponse = testContext().get(TestContextKey.ORIGINATOR_CREATE_RESPONSE);
|
||||
Long originatorId = createResponse.getResourceId();
|
||||
String expectedExternalId = testContext().get(TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
String expectedOriginatorTypeName = testContext().get(TestContextKey.ORIGINATOR_TYPE_NAME);
|
||||
String expectedChannelTypeName = testContext().get(TestContextKey.ORIGINATOR_CHANNEL_TYPE_NAME);
|
||||
|
||||
GetLoanOriginatorsResponse originator = ok(() -> fineractClient.loanOriginators().retrieveOne18(originatorId));
|
||||
|
||||
assertThat(originator.getId()).as("Originator ID").isNotNull();
|
||||
assertThat(originator.getExternalId()).as("Originator externalId").isEqualTo(expectedExternalId);
|
||||
assertThat(originator.getStatus()).as("Originator status").isEqualTo("ACTIVE");
|
||||
assertThat(originator.getOriginatorType()).as("Originator type").isNotNull();
|
||||
assertThat(originator.getOriginatorType().getName()).as("Originator type name").isEqualTo(expectedOriginatorTypeName);
|
||||
assertThat(originator.getChannelType()).as("Channel type").isNotNull();
|
||||
assertThat(originator.getChannelType().getName()).as("Channel type name").isEqualTo(expectedChannelTypeName);
|
||||
log.info("Verified originator {} with all fields: type={}, channel={}", originatorId, expectedOriginatorTypeName,
|
||||
expectedChannelTypeName);
|
||||
}
|
||||
|
||||
// --- Attach / Detach steps ---
|
||||
|
||||
@When("Admin attaches the originator to the loan")
|
||||
public void attachOriginatorToLoan() {
|
||||
attachOriginatorInternal(TestContextKey.ORIGINATOR_CREATE_RESPONSE);
|
||||
}
|
||||
|
||||
@When("Admin attaches the second originator to the loan")
|
||||
public void attachSecondOriginatorToLoan() {
|
||||
attachOriginatorInternal(TestContextKey.ORIGINATOR_SECOND_CREATE_RESPONSE);
|
||||
}
|
||||
|
||||
@When("Admin detaches the originator from the loan")
|
||||
public void detachOriginatorFromLoan() {
|
||||
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||
PostLoanOriginatorsResponse originatorResponse = testContext().get(TestContextKey.ORIGINATOR_CREATE_RESPONSE);
|
||||
long loanId = loanResponse.getLoanId();
|
||||
long originatorId = originatorResponse.getResourceId();
|
||||
|
||||
executeVoid(() -> fineractClient.loanOriginators().detachOriginatorFromLoan(loanId, originatorId));
|
||||
log.info("Detached originator {} from loan {}", originatorId, loanId);
|
||||
}
|
||||
|
||||
// --- Loan details verification steps ---
|
||||
|
||||
@Then("Loan details with association {string} has the originator attached")
|
||||
public void verifyOriginatorInLoanDetails(String association) {
|
||||
long loanId = getLoanId();
|
||||
String expectedExternalId = testContext().get(TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
|
||||
List<GetLoansLoanIdOriginatorData> originators = retrieveLoanOriginators(loanId, association);
|
||||
assertThat(originators).as("Originators should not be null or empty").isNotNull().isNotEmpty();
|
||||
|
||||
boolean found = originators.stream().anyMatch(o -> expectedExternalId.equals(o.getExternalId()));
|
||||
assertThat(found).as("Expected originator with externalId %s in loan details", expectedExternalId).isTrue();
|
||||
}
|
||||
|
||||
@Then("Loan details with association {string} has the originator with all fields attached")
|
||||
public void verifyOriginatorWithAllFieldsInLoanDetails(String association) {
|
||||
PostLoanOriginatorsResponse createResponse = testContext().get(TestContextKey.ORIGINATOR_CREATE_RESPONSE);
|
||||
long loanId = getLoanId();
|
||||
long originatorId = createResponse.getResourceId();
|
||||
String expectedExternalId = testContext().get(TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
String expectedOriginatorTypeName = testContext().get(TestContextKey.ORIGINATOR_TYPE_NAME);
|
||||
String expectedChannelTypeName = testContext().get(TestContextKey.ORIGINATOR_CHANNEL_TYPE_NAME);
|
||||
|
||||
List<GetLoansLoanIdOriginatorData> originators = retrieveLoanOriginators(loanId, association);
|
||||
assertThat(originators).as("Originators should not be null or empty").isNotNull().isNotEmpty();
|
||||
|
||||
GetLoansLoanIdOriginatorData originator = originators.stream().filter(o -> expectedExternalId.equals(o.getExternalId())).findFirst()
|
||||
.orElseThrow(() -> new AssertionError("Originator with externalId " + expectedExternalId + " not found in loan details"));
|
||||
|
||||
assertThat(originator.getId()).as("Originator ID in loan details").isNotNull();
|
||||
assertThat(originator.getName()).as("Originator name in loan details").isNotNull();
|
||||
assertThat(originator.getStatus()).as("Originator status in loan details").isEqualTo("ACTIVE");
|
||||
|
||||
// Verify type fields via direct originator GET (loan details serializes CodeValueData as nested objects
|
||||
// which don't map to the flat fields in the generated client model)
|
||||
GetLoanOriginatorsResponse originatorDetails = ok(() -> fineractClient.loanOriginators().retrieveOne18(originatorId));
|
||||
assertThat(originatorDetails.getOriginatorType()).as("Originator type").isNotNull();
|
||||
assertThat(originatorDetails.getOriginatorType().getName()).as("Originator type name").isEqualTo(expectedOriginatorTypeName);
|
||||
assertThat(originatorDetails.getChannelType()).as("Channel type").isNotNull();
|
||||
assertThat(originatorDetails.getChannelType().getName()).as("Channel type name").isEqualTo(expectedChannelTypeName);
|
||||
log.info("Verified originator with all fields in loan {} details", loanId);
|
||||
}
|
||||
|
||||
@Then("Loan details with association {string} has {int} originator(s) attached")
|
||||
public void verifyOriginatorCountInLoanDetails(String association, int expectedCount) {
|
||||
long loanId = getLoanId();
|
||||
|
||||
List<GetLoansLoanIdOriginatorData> originators = retrieveLoanOriginators(loanId, association);
|
||||
int actualCount = (originators == null) ? 0 : originators.size();
|
||||
assertThat(actualCount).as("Number of originators in loan details").isEqualTo(expectedCount);
|
||||
log.info("Verified loan {} has {} originators attached", loanId, actualCount);
|
||||
}
|
||||
|
||||
@Then("Loan details with association {string} has originator with name {string}")
|
||||
public void verifyOriginatorNameInLoanDetails(String association, String expectedName) {
|
||||
long loanId = getLoanId();
|
||||
|
||||
List<GetLoansLoanIdOriginatorData> originators = retrieveLoanOriginators(loanId, association);
|
||||
assertThat(originators).as("Originators should not be null or empty").isNotNull().isNotEmpty();
|
||||
|
||||
boolean found = originators.stream().anyMatch(o -> expectedName.equals(o.getName()));
|
||||
assertThat(found).as("Expected originator with name '%s' in loan details", expectedName).isTrue();
|
||||
log.info("Verified loan {} has originator with name '{}'", loanId, expectedName);
|
||||
}
|
||||
|
||||
@Then("Loan details with association {string} has the second originator attached")
|
||||
public void verifySecondOriginatorInLoanDetails(String association) {
|
||||
long loanId = getLoanId();
|
||||
String expectedExternalId = testContext().get(TestContextKey.ORIGINATOR_SECOND_EXTERNAL_ID);
|
||||
|
||||
List<GetLoansLoanIdOriginatorData> originators = retrieveLoanOriginators(loanId, association);
|
||||
assertThat(originators).as("Originators should not be null or empty").isNotNull().isNotEmpty();
|
||||
|
||||
boolean found = originators.stream().anyMatch(o -> expectedExternalId.equals(o.getExternalId()));
|
||||
assertThat(found).as("Expected second originator with externalId %s in loan details", expectedExternalId).isTrue();
|
||||
}
|
||||
|
||||
@Then("Loan details with association {string} has no originator attached")
|
||||
public void verifyNoOriginatorInLoanDetails(String association) {
|
||||
long loanId = getLoanId();
|
||||
|
||||
List<GetLoansLoanIdOriginatorData> originators = retrieveLoanOriginators(loanId, association);
|
||||
assertThat(originators).as("Originators list should be empty after detach").isNullOrEmpty();
|
||||
}
|
||||
|
||||
// --- Failure / validation steps ---
|
||||
|
||||
@Then("Attaching the originator to the loan should fail")
|
||||
public void attachOriginatorShouldFail() {
|
||||
long loanId = getLoanId();
|
||||
long originatorId = getOriginatorId();
|
||||
|
||||
failVoid(() -> fineractClient.loanOriginators().attachOriginatorToLoan(loanId, originatorId));
|
||||
log.info("Attach originator {} to loan {} failed as expected", originatorId, loanId);
|
||||
}
|
||||
|
||||
@Then("Attaching the originator to the loan should fail with status {int}")
|
||||
public void attachOriginatorShouldFailWithStatus(int expectedStatus) {
|
||||
long loanId = getLoanId();
|
||||
long originatorId = getOriginatorId();
|
||||
|
||||
CallFailedRuntimeException exception = failVoid(
|
||||
() -> fineractClient.loanOriginators().attachOriginatorToLoan(loanId, originatorId));
|
||||
assertExpectedStatus(exception, expectedStatus);
|
||||
log.info("Attach originator {} to loan {} failed with expected status {}", originatorId, loanId, expectedStatus);
|
||||
}
|
||||
|
||||
@Then("Detaching the originator from the loan should fail with status {int}")
|
||||
public void detachOriginatorShouldFailWithStatus(int expectedStatus) {
|
||||
long loanId = getLoanId();
|
||||
long originatorId = getOriginatorId();
|
||||
|
||||
CallFailedRuntimeException exception = failVoid(
|
||||
() -> fineractClient.loanOriginators().detachOriginatorFromLoan(loanId, originatorId));
|
||||
assertExpectedStatus(exception, expectedStatus);
|
||||
log.info("Detach originator {} from loan {} failed with expected status {}", originatorId, loanId, expectedStatus);
|
||||
}
|
||||
|
||||
@Then("Attaching the second originator to the loan should fail with status {int}")
|
||||
public void attachSecondOriginatorShouldFailWithStatus(int expectedStatus) {
|
||||
long loanId = getLoanId();
|
||||
PostLoanOriginatorsResponse originatorResponse = testContext().get(TestContextKey.ORIGINATOR_SECOND_CREATE_RESPONSE);
|
||||
long originatorId = originatorResponse.getResourceId();
|
||||
|
||||
CallFailedRuntimeException exception = failVoid(
|
||||
() -> fineractClient.loanOriginators().attachOriginatorToLoan(loanId, originatorId));
|
||||
assertExpectedStatus(exception, expectedStatus);
|
||||
log.info("Attach second originator {} to loan {} failed with expected status {}", originatorId, loanId, expectedStatus);
|
||||
}
|
||||
|
||||
@Then("Attaching non-existent originator to the loan should fail with status {int}")
|
||||
public void attachNonExistentOriginatorShouldFail(int expectedStatus) {
|
||||
long loanId = getLoanId();
|
||||
|
||||
CallFailedRuntimeException exception = failVoid(
|
||||
() -> fineractClient.loanOriginators().attachOriginatorToLoan(loanId, NON_EXISTENT_ID));
|
||||
assertExpectedStatus(exception, expectedStatus);
|
||||
log.info("Attach non-existent originator to loan {} failed with expected status {}", loanId, expectedStatus);
|
||||
}
|
||||
|
||||
@Then("Attaching the originator to non-existent loan should fail with status {int}")
|
||||
public void attachOriginatorToNonExistentLoanShouldFail(int expectedStatus) {
|
||||
long originatorId = getOriginatorId();
|
||||
|
||||
CallFailedRuntimeException exception = failVoid(
|
||||
() -> fineractClient.loanOriginators().attachOriginatorToLoan(NON_EXISTENT_ID, originatorId));
|
||||
assertExpectedStatus(exception, expectedStatus);
|
||||
log.info("Attach originator {} to non-existent loan failed with expected status {}", originatorId, expectedStatus);
|
||||
}
|
||||
|
||||
@Then("Creating a loan originator without name should fail with status {int}")
|
||||
public void createOriginatorWithoutNameShouldFail(int expectedStatus) {
|
||||
PostLoanOriginatorsRequest request = new PostLoanOriginatorsRequest().externalId(UUID.randomUUID().toString());
|
||||
|
||||
CallFailedRuntimeException exception = fail(() -> fineractClient.loanOriginators().create11(request));
|
||||
assertExpectedStatus(exception, expectedStatus);
|
||||
log.info("Create originator without name failed with expected status {}", expectedStatus);
|
||||
}
|
||||
|
||||
@Then("Creating a loan originator without name succeeds")
|
||||
public void createOriginatorWithoutNameSucceeds() {
|
||||
PostLoanOriginatorsRequest request = new PostLoanOriginatorsRequest().externalId(UUID.randomUUID().toString());
|
||||
|
||||
PostLoanOriginatorsResponse response = ok(() -> fineractClient.loanOriginators().create11(request));
|
||||
assertThat(response.getResourceId()).as("Originator created without name").isNotNull();
|
||||
log.info("Created originator without name, resourceId {}", response.getResourceId());
|
||||
}
|
||||
|
||||
// --- Permission steps ---
|
||||
|
||||
@Then("Created user without ATTACH_LOAN_ORIGINATOR permission fails to attach originator to the loan")
|
||||
public void userWithoutAttachPermissionFails() {
|
||||
long loanId = getLoanId();
|
||||
long originatorId = getOriginatorId();
|
||||
FineractFeignClient userClient = createClientForUser();
|
||||
|
||||
CallFailedRuntimeException exception = failVoid(() -> userClient.loanOriginators().attachOriginatorToLoan(loanId, originatorId));
|
||||
assertExpectedStatus(exception, 403);
|
||||
log.info("User without ATTACH_LOAN_ORIGINATOR permission failed to attach originator {} to loan {} as expected", originatorId,
|
||||
loanId);
|
||||
}
|
||||
|
||||
@Then("Created user without DETACH_LOAN_ORIGINATOR permission fails to detach originator from the loan")
|
||||
public void userWithoutDetachPermissionFails() {
|
||||
long loanId = getLoanId();
|
||||
long originatorId = getOriginatorId();
|
||||
FineractFeignClient userClient = createClientForUser();
|
||||
|
||||
CallFailedRuntimeException exception = failVoid(() -> userClient.loanOriginators().detachOriginatorFromLoan(loanId, originatorId));
|
||||
assertExpectedStatus(exception, 403);
|
||||
log.info("User without DETACH_LOAN_ORIGINATOR permission failed to detach originator {} from loan {} as expected", originatorId,
|
||||
loanId);
|
||||
}
|
||||
|
||||
@Then("Created user without CREATE_LOAN_ORIGINATOR permission fails to create an originator")
|
||||
public void userWithoutCreatePermissionFails() {
|
||||
FineractFeignClient userClient = createClientForUser();
|
||||
|
||||
PostLoanOriginatorsRequest request = new PostLoanOriginatorsRequest().externalId(UUID.randomUUID().toString())
|
||||
.name("Should Fail Originator");
|
||||
|
||||
CallFailedRuntimeException exception = fail(() -> userClient.loanOriginators().create11(request));
|
||||
assertExpectedStatus(exception, 403);
|
||||
log.info("User without CREATE_LOAN_ORIGINATOR permission failed to create originator as expected");
|
||||
}
|
||||
|
||||
@Then("Created user without UPDATE_LOAN_ORIGINATOR permission fails to update the originator")
|
||||
public void userWithoutUpdatePermissionFails() {
|
||||
long originatorId = getOriginatorId();
|
||||
FineractFeignClient userClient = createClientForUser();
|
||||
|
||||
PutLoanOriginatorsRequest updateRequest = new PutLoanOriginatorsRequest().name("Should Fail Update");
|
||||
|
||||
CallFailedRuntimeException exception = fail(() -> userClient.loanOriginators().update16(originatorId, updateRequest));
|
||||
assertExpectedStatus(exception, 403);
|
||||
log.info("User without UPDATE_LOAN_ORIGINATOR permission failed to update originator {} as expected", originatorId);
|
||||
}
|
||||
|
||||
@Then("Created user without DELETE_LOAN_ORIGINATOR permission fails to delete the originator")
|
||||
public void userWithoutDeletePermissionFails() {
|
||||
long originatorId = getOriginatorId();
|
||||
FineractFeignClient userClient = createClientForUser();
|
||||
|
||||
CallFailedRuntimeException exception = fail(() -> userClient.loanOriginators().delete14(originatorId));
|
||||
assertExpectedStatus(exception, 403);
|
||||
log.info("User without DELETE_LOAN_ORIGINATOR permission failed to delete originator {} as expected", originatorId);
|
||||
}
|
||||
|
||||
// --- Inline loan creation ---
|
||||
|
||||
@When("Admin creates a new Loan with originator inline submitted on date: {string}")
|
||||
public void createLoanWithOriginatorInline(String submitDate) {
|
||||
PostClientsResponse clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
|
||||
Long clientId = clientResponse.getClientId();
|
||||
|
||||
String originatorExternalId = UUID.randomUUID().toString();
|
||||
PostLoansOriginatorData originatorData = new PostLoansOriginatorData().externalId(originatorExternalId)
|
||||
.name("Inline Created Originator");
|
||||
|
||||
PostLoansRequest loansRequest = loanRequestFactory.defaultLoansRequest(clientId).submittedOnDate(submitDate)
|
||||
.expectedDisbursementDate(submitDate).addOriginatorsItem(originatorData);
|
||||
|
||||
PostLoansResponse response = ok(() -> fineractClient.loans().calculateLoanScheduleOrSubmitLoanApplication(loansRequest, Map.of()));
|
||||
|
||||
assertThat(response.getLoanId()).isNotNull();
|
||||
testContext().set(TestContextKey.LOAN_CREATE_RESPONSE, response);
|
||||
testContext().set(TestContextKey.ORIGINATOR_EXTERNAL_ID, originatorExternalId);
|
||||
log.info("Created loan {} with inline originator externalId {}", response.getLoanId(), originatorExternalId);
|
||||
}
|
||||
|
||||
// --- Approve without event check + event verification ---
|
||||
|
||||
@When("Admin approves the loan on {string} with {string} amount and expected disbursement date on {string}")
|
||||
public void approveLoanWithoutEventCheck(String approveDate, String approvedAmount, String expectedDisbursementDate) {
|
||||
long loanId = getLoanId();
|
||||
eventStore.reset();
|
||||
|
||||
PostLoansLoanIdRequest approveRequest = LoanRequestFactory.defaultLoanApproveRequest().approvedOnDate(approveDate)
|
||||
.approvedLoanAmount(new BigDecimal(approvedAmount)).expectedDisbursementDate(expectedDisbursementDate);
|
||||
|
||||
PostLoansLoanIdResponse loanApproveResponse = ok(
|
||||
() -> fineractClient.loans().stateTransitions(loanId, approveRequest, Map.of("command", "approve")));
|
||||
testContext().set(TestContextKey.LOAN_APPROVAL_RESPONSE, loanApproveResponse);
|
||||
log.info("Loan {} approved (event check skipped for separate verification)", loanId);
|
||||
}
|
||||
|
||||
@Then("LoanApprovedBusinessEvent is created with originator details")
|
||||
public void verifyOriginatorInApprovalEvent() {
|
||||
long loanId = getLoanId();
|
||||
String expectedExternalId = testContext().get(TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
|
||||
eventAssertion.assertEvent(LoanApprovedEvent.class, loanId).extractingData(loanAccountDataV1 -> {
|
||||
List<OriginatorDetailsV1> originators = loanAccountDataV1.getOriginators();
|
||||
assertThat(originators).as("Originators in LoanApprovedEvent should not be null or empty").isNotNull().isNotEmpty();
|
||||
assertThat(originators.get(0).getExternalId()).as("Originator externalId in LoanApprovedEvent").isEqualTo(expectedExternalId);
|
||||
assertThat(originators.get(0).getStatus()).as("Originator status in LoanApprovedEvent").isEqualTo("ACTIVE");
|
||||
return loanAccountDataV1.getId();
|
||||
}).isEqualTo(loanId);
|
||||
log.info("Verified originator {} in LoanApprovedEvent for loan {}", expectedExternalId, loanId);
|
||||
}
|
||||
|
||||
// --- Organization-level CRUD steps ---
|
||||
|
||||
@Then("Loan originator is retrieved successfully by external ID with all fields")
|
||||
public void retrieveOriginatorByExternalIdWithAllFields() {
|
||||
String expectedExternalId = testContext().get(TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
String expectedOriginatorTypeName = testContext().get(TestContextKey.ORIGINATOR_TYPE_NAME);
|
||||
String expectedChannelTypeName = testContext().get(TestContextKey.ORIGINATOR_CHANNEL_TYPE_NAME);
|
||||
|
||||
GetLoanOriginatorsResponse originator = ok(() -> fineractClient.loanOriginators().retrieveByExternalId(expectedExternalId));
|
||||
|
||||
assertThat(originator.getId()).as("Originator ID").isNotNull();
|
||||
assertThat(originator.getExternalId()).as("Originator externalId").isEqualTo(expectedExternalId);
|
||||
assertThat(originator.getName()).as("Originator name").isNotNull();
|
||||
assertThat(originator.getStatus()).as("Originator status").isEqualTo("ACTIVE");
|
||||
assertThat(originator.getOriginatorType()).as("Originator type").isNotNull();
|
||||
assertThat(originator.getOriginatorType().getName()).as("Originator type name").isEqualTo(expectedOriginatorTypeName);
|
||||
assertThat(originator.getChannelType()).as("Channel type").isNotNull();
|
||||
assertThat(originator.getChannelType().getName()).as("Channel type name").isEqualTo(expectedChannelTypeName);
|
||||
log.info("Retrieved originator by externalId {} with all fields", expectedExternalId);
|
||||
}
|
||||
|
||||
@Then("Loan originator list contains the created originator")
|
||||
public void verifyOriginatorInList() {
|
||||
long expectedId = getOriginatorId();
|
||||
|
||||
List<GetLoanOriginatorsResponse> allOriginators = ok(() -> fineractClient.loanOriginators().retrieveAll28());
|
||||
|
||||
assertThat(allOriginators).as("Originator list should not be null or empty").isNotNull().isNotEmpty();
|
||||
|
||||
boolean found = allOriginators.stream().anyMatch(o -> o.getId() != null && o.getId() == expectedId);
|
||||
assertThat(found).as("Expected originator with ID %d in list", expectedId).isTrue();
|
||||
log.info("Verified originator {} is present in the list of {} originators", expectedId, allOriginators.size());
|
||||
}
|
||||
|
||||
@Then("Loan originator template contains status options, originator type options and channel type options")
|
||||
public void verifyOriginatorTemplate() {
|
||||
GetLoanOriginatorTemplateResponse template = ok(() -> fineractClient.loanOriginators().retrieveLoanOriginatorTemplate());
|
||||
|
||||
assertThat(template.getStatusOptions()).as("Status options").isNotNull().isNotEmpty();
|
||||
assertThat(template.getOriginatorTypeOptions()).as("Originator type options").isNotNull().isNotEmpty();
|
||||
assertThat(template.getChannelTypeOptions()).as("Channel type options").isNotNull().isNotEmpty();
|
||||
assertThat(template.getExternalId()).as("Template generated externalId").isNotNull().isNotEmpty();
|
||||
|
||||
log.info("Verified template: {} status options, {} originator types, {} channel types", template.getStatusOptions().size(),
|
||||
template.getOriginatorTypeOptions().size(), template.getChannelTypeOptions().size());
|
||||
}
|
||||
|
||||
@When("Admin updates the originator name to {string} and status to {string}")
|
||||
public void updateOriginatorById(String newName, String newStatus) {
|
||||
long originatorId = getOriginatorId();
|
||||
|
||||
PutLoanOriginatorsRequest updateRequest = new PutLoanOriginatorsRequest().name(newName).status(newStatus);
|
||||
|
||||
PutLoanOriginatorsResponse updateResponse = ok(() -> fineractClient.loanOriginators().update16(originatorId, updateRequest));
|
||||
|
||||
assertThat(updateResponse.getResourceId()).as("Updated originator resource ID").isEqualTo(originatorId);
|
||||
log.info("Updated originator {} with name={}, status={}", originatorId, newName, newStatus);
|
||||
}
|
||||
|
||||
@When("Admin updates the originator by external ID with name {string}")
|
||||
public void updateOriginatorByExternalId(String newName) {
|
||||
String externalId = testContext().get(TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
|
||||
PutLoanOriginatorsRequest updateRequest = new PutLoanOriginatorsRequest().name(newName);
|
||||
|
||||
PutLoanOriginatorsResponse updateResponse = ok(
|
||||
() -> fineractClient.loanOriginators().updateByExternalId(externalId, updateRequest));
|
||||
|
||||
assertThat(updateResponse.getResourceId()).as("Updated originator resource ID").isNotNull();
|
||||
log.info("Updated originator by externalId {} with name={}", externalId, newName);
|
||||
}
|
||||
|
||||
@Then("Loan originator has name {string} and status {string}")
|
||||
public void verifyOriginatorNameAndStatus(String expectedName, String expectedStatus) {
|
||||
long originatorId = getOriginatorId();
|
||||
|
||||
GetLoanOriginatorsResponse originator = ok(() -> fineractClient.loanOriginators().retrieveOne18(originatorId));
|
||||
|
||||
assertThat(originator.getName()).as("Originator name").isEqualTo(expectedName);
|
||||
assertThat(originator.getStatus()).as("Originator status").isEqualTo(expectedStatus);
|
||||
log.info("Verified originator {} has name={}, status={}", originatorId, expectedName, expectedStatus);
|
||||
}
|
||||
|
||||
@Then("Loan originator retrieved by external ID has name {string}")
|
||||
public void verifyOriginatorByExternalIdHasName(String expectedName) {
|
||||
String externalId = testContext().get(TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
|
||||
GetLoanOriginatorsResponse originator = ok(() -> fineractClient.loanOriginators().retrieveByExternalId(externalId));
|
||||
|
||||
assertThat(originator.getName()).as("Originator name").isEqualTo(expectedName);
|
||||
log.info("Verified originator with externalId {} has name={}", externalId, expectedName);
|
||||
}
|
||||
|
||||
@When("Admin deletes the originator by ID")
|
||||
public void deleteOriginatorById() {
|
||||
long originatorId = getOriginatorId();
|
||||
|
||||
DeleteLoanOriginatorsResponse deleteResponse = ok(() -> fineractClient.loanOriginators().delete14(originatorId));
|
||||
|
||||
assertThat(deleteResponse.getResourceId()).as("Deleted originator resource ID").isEqualTo(originatorId);
|
||||
log.info("Deleted originator by ID {}", originatorId);
|
||||
}
|
||||
|
||||
@When("Admin deletes the originator by external ID")
|
||||
public void deleteOriginatorByExternalId() {
|
||||
String externalId = testContext().get(TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
|
||||
DeleteLoanOriginatorsResponse deleteResponse = ok(() -> fineractClient.loanOriginators().deleteByExternalId(externalId));
|
||||
|
||||
assertThat(deleteResponse.getResourceId()).as("Deleted originator resource ID").isNotNull();
|
||||
log.info("Deleted originator by externalId {}", externalId);
|
||||
}
|
||||
|
||||
@Then("Retrieving the deleted originator by ID should fail with status {int}")
|
||||
public void retrieveDeletedOriginatorByIdShouldFail(int expectedStatus) {
|
||||
long originatorId = getOriginatorId();
|
||||
|
||||
CallFailedRuntimeException exception = fail(() -> fineractClient.loanOriginators().retrieveOne18(originatorId));
|
||||
assertExpectedStatus(exception, expectedStatus);
|
||||
log.info("Retrieving deleted originator {} failed with expected status {}", originatorId, expectedStatus);
|
||||
}
|
||||
|
||||
@Then("Retrieving the deleted originator by external ID should fail with status {int}")
|
||||
public void retrieveDeletedOriginatorByExternalIdShouldFail(int expectedStatus) {
|
||||
String externalId = testContext().get(TestContextKey.ORIGINATOR_EXTERNAL_ID);
|
||||
|
||||
CallFailedRuntimeException exception = fail(() -> fineractClient.loanOriginators().retrieveByExternalId(externalId));
|
||||
assertExpectedStatus(exception, expectedStatus);
|
||||
log.info("Retrieving deleted originator by externalId {} failed with expected status {}", externalId, expectedStatus);
|
||||
}
|
||||
|
||||
@Then("Deleting the originator should fail with status {int}")
|
||||
public void deleteOriginatorShouldFailWithStatus(int expectedStatus) {
|
||||
long originatorId = getOriginatorId();
|
||||
|
||||
CallFailedRuntimeException exception = fail(() -> fineractClient.loanOriginators().delete14(originatorId));
|
||||
assertExpectedStatus(exception, expectedStatus);
|
||||
log.info("Deleting originator {} failed with expected status {}", originatorId, expectedStatus);
|
||||
}
|
||||
|
||||
// --- Helper methods ---
|
||||
|
||||
private long getLoanId() {
|
||||
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||
return loanResponse.getLoanId();
|
||||
}
|
||||
|
||||
private long getOriginatorId() {
|
||||
PostLoanOriginatorsResponse response = testContext().get(TestContextKey.ORIGINATOR_CREATE_RESPONSE);
|
||||
return response.getResourceId();
|
||||
}
|
||||
|
||||
private void createOriginatorAndStore(String name, String status, String responseKey, String externalIdKey) {
|
||||
String externalId = UUID.randomUUID().toString();
|
||||
PostLoanOriginatorsRequest request = new PostLoanOriginatorsRequest().externalId(externalId).name(name);
|
||||
if (status != null) {
|
||||
request.status(status);
|
||||
}
|
||||
|
||||
PostLoanOriginatorsResponse response = ok(() -> fineractClient.loanOriginators().create11(request));
|
||||
|
||||
assertThat(response.getResourceId()).isNotNull();
|
||||
testContext().set(responseKey, response);
|
||||
testContext().set(externalIdKey, externalId);
|
||||
log.info("Created originator with ID {} and externalId {}", response.getResourceId(), externalId);
|
||||
}
|
||||
|
||||
private void attachOriginatorInternal(String originatorContextKey) {
|
||||
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||
PostLoanOriginatorsResponse originatorResponse = testContext().get(originatorContextKey);
|
||||
long loanId = loanResponse.getLoanId();
|
||||
long originatorId = originatorResponse.getResourceId();
|
||||
|
||||
executeVoid(() -> fineractClient.loanOriginators().attachOriginatorToLoan(loanId, originatorId));
|
||||
log.info("Attached originator {} to loan {}", originatorId, loanId);
|
||||
}
|
||||
|
||||
private List<GetLoansLoanIdOriginatorData> retrieveLoanOriginators(long loanId, String association) {
|
||||
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId,
|
||||
Map.of("staffInSelectedOfficeOnly", false, "associations", association, "exclude", "", "fields", "")));
|
||||
return loanDetails.getOriginators();
|
||||
}
|
||||
|
||||
private void assertExpectedStatus(CallFailedRuntimeException exception, int expectedStatus) {
|
||||
assertThat(exception.getStatus()).as(ErrorMessageHelper.wrongErrorCode(exception.getStatus(), expectedStatus))
|
||||
.isEqualTo(expectedStatus);
|
||||
}
|
||||
|
||||
private FineractFeignClient createClientForUser() {
|
||||
String username = testContext().get(TestContextKey.CREATED_SIMPLE_USER_USERNAME);
|
||||
String password = testContext().get(TestContextKey.CREATED_SIMPLE_USER_PASSWORD);
|
||||
String apiBaseUrl = apiProperties.getBaseUrl() + "/fineract-provider/api/";
|
||||
|
||||
return FineractFeignClient.builder().baseUrl(apiBaseUrl).credentials(username, password).tenantId(apiProperties.getTenantId())
|
||||
.disableSslVerification(true).connectTimeout(60, TimeUnit.SECONDS)
|
||||
.readTimeout((int) apiProperties.getReadTimeout(), TimeUnit.SECONDS).build();
|
||||
}
|
||||
}
|
||||
+6
@@ -298,6 +298,12 @@ public abstract class TestContextKey {
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADVANCED_CUSTOM_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE_PRINCIPAL_FIRST = "loanProductCreateResponseLP2AdvancedPaymentHorizontalPrincipalFirst";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_MULTIDISBURSE_FULL_TERM_TRANCHE = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisburseFullTermTranche";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_360_30_USD = "loanProductCreateResponseLP2AdvancedPaymentHorizontal36030Usd";
|
||||
public static final String ORIGINATOR_CREATE_RESPONSE = "originatorCreateResponse";
|
||||
public static final String ORIGINATOR_EXTERNAL_ID = "originatorExternalId";
|
||||
public static final String ORIGINATOR_TYPE_NAME = "originatorTypeName";
|
||||
public static final String ORIGINATOR_CHANNEL_TYPE_NAME = "originatorChannelTypeName";
|
||||
public static final String ORIGINATOR_SECOND_CREATE_RESPONSE = "originatorSecondCreateResponse";
|
||||
public static final String ORIGINATOR_SECOND_EXTERNAL_ID = "originatorSecondExternalId";
|
||||
public static final String VERIFIED_LOAN_ACCRUALS = "VERIFIED_LOAN_ACCRUALS";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_DOWNPAYMENT = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisburseFullTermTrancheDownPayment";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_DOWNPAYMENT_AUTO = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisburseFullTermTrancheDownPaymentAuto";
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
@LoanOriginationFeature
|
||||
Feature: Loan Origination
|
||||
|
||||
@TestRailId:C4649
|
||||
Scenario: Verify loan originator registration, attachment to loan, and persistence through approval and disbursal
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Merchant Partner Alpha"
|
||||
Then Loan originator is created successfully with status "ACTIVE"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
And Admin successfully approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025"
|
||||
When Admin successfully disburse the loan on "1 January 2025" with "1000" EUR transaction amount
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
|
||||
@TestRailId:C4650
|
||||
Scenario: Verify loan originator inline attachment with auto-creation during loan application
|
||||
Given Global configuration "enable-originator-creation-during-loan-application" is enabled
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new Loan with originator inline submitted on date: "1 January 2025"
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
And Admin successfully approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025"
|
||||
When Admin successfully disburse the loan on "1 January 2025" with "1000" EUR transaction amount
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
Given Global configuration "enable-originator-creation-during-loan-application" is disabled
|
||||
|
||||
@TestRailId:C4651
|
||||
Scenario: Verify loan originator details in external business event after loan approval
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Event Test Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
When Admin approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025"
|
||||
Then LoanApprovedBusinessEvent is created with originator details
|
||||
|
||||
@TestRailId:C4652
|
||||
Scenario: Verify loan originator detachment from loan before approval
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Detach Test Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
When Admin detaches the originator from the loan
|
||||
Then Loan details with association "originators" has no originator attached
|
||||
|
||||
@TestRailId:C4653
|
||||
Scenario: Verify that inactive originator cannot be attached to loan
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID, name "Inactive Originator" and status "PENDING"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
Then Attaching the originator to the loan should fail
|
||||
|
||||
@TestRailId:C4654
|
||||
Scenario: Verify loan originator creation with all fields and persistence in loan details
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with all fields and name "Full Fields Originator"
|
||||
Then Loan originator is created successfully with all fields populated
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
Then Loan details with association "originators" has the originator with all fields attached
|
||||
|
||||
@TestRailId:C4655
|
||||
Scenario: Verify that originator cannot be attached to approved loan
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Post Approval Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
And Admin successfully approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025"
|
||||
Then Attaching the originator to the loan should fail with status 403
|
||||
|
||||
@TestRailId:C4656
|
||||
Scenario: Verify that originator cannot be detached from approved loan
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Pre Approval Detach Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
And Admin successfully approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025"
|
||||
Then Detaching the originator from the loan should fail with status 403
|
||||
|
||||
@TestRailId:C4657
|
||||
Scenario: Verify that same originator cannot be attached to loan twice
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Duplicate Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
Then Attaching the originator to the loan should fail with status 403
|
||||
|
||||
@TestRailId:C4658
|
||||
Scenario: Verify that non-attached originator cannot be detached from loan
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Not Attached Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
Then Detaching the originator from the loan should fail with status 404
|
||||
|
||||
@TestRailId:C4659
|
||||
Scenario: Verify that originator cannot be attached to non-existent loan
|
||||
When Admin creates a new loan originator with external ID and name "Orphan Originator"
|
||||
Then Attaching the originator to non-existent loan should fail with status 404
|
||||
|
||||
@TestRailId:C4660
|
||||
Scenario: Verify that non-existent originator cannot be attached to loan
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
Then Attaching non-existent originator to the loan should fail with status 404
|
||||
|
||||
@TestRailId:C4661
|
||||
Scenario: Verify loan originator creation without name succeeds with default handling
|
||||
Then Creating a loan originator without name succeeds
|
||||
|
||||
@TestRailId:C4662
|
||||
Scenario: Verify that user without ATTACH_LOAN_ORIGINATOR permission cannot attach originator
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Permission Test Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin creates new user with "ORIGINATOR_NO_ATTACH" username, "ORIGINATOR_NO_ATTACH_ROLE" role name and given permissions:
|
||||
| READ_LOAN |
|
||||
Then Created user without ATTACH_LOAN_ORIGINATOR permission fails to attach originator to the loan
|
||||
|
||||
@TestRailId:C4663
|
||||
Scenario: Verify that user without DETACH_LOAN_ORIGINATOR permission cannot detach originator
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Permission Detach Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
When Admin creates new user with "ORIGINATOR_NO_DETACH" username, "ORIGINATOR_NO_DETACH_ROLE" role name and given permissions:
|
||||
| READ_LOAN |
|
||||
Then Created user without DETACH_LOAN_ORIGINATOR permission fails to detach originator from the loan
|
||||
|
||||
@TestRailId:C4664
|
||||
Scenario: Verify loan originator persistence through full loan lifecycle with repayments
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with all fields and name "Lifecycle Originator"
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||
| LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2025 | 1000 | 12 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
When Admin attaches the originator to the loan
|
||||
Then Loan details with association "originators" has the originator with all fields attached
|
||||
And Admin successfully approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025"
|
||||
When Admin successfully disburse the loan on "1 January 2025" with "1000" EUR transaction amount
|
||||
Then Loan details with association "originators" has the originator with all fields attached
|
||||
When Admin sets the business date to "1 February 2025"
|
||||
And Customer makes "AUTOPAY" repayment on "1 February 2025" with 340 EUR transaction amount
|
||||
Then Loan details with association "originators" has the originator with all fields attached
|
||||
When Admin sets the business date to "1 March 2025"
|
||||
And Customer makes "AUTOPAY" repayment on "1 March 2025" with 340 EUR transaction amount
|
||||
Then Loan details with association "originators" has the originator with all fields attached
|
||||
|
||||
@TestRailId:C4665
|
||||
Scenario: Verify multiple originators on a loan with add, update, and detach operations
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "First Originator"
|
||||
When Admin creates a second loan originator with external ID and name "Second Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
When Admin attaches the second originator to the loan
|
||||
Then Loan details with association "originators" has 2 originators attached
|
||||
When Admin updates the originator name to "First Originator Updated" and status to "ACTIVE"
|
||||
Then Loan details with association "originators" has originator with name "First Originator Updated"
|
||||
When Admin detaches the originator from the loan
|
||||
Then Loan details with association "originators" has 1 originator attached
|
||||
And Loan details with association "originators" has the second originator attached
|
||||
|
||||
@TestRailId:C4666
|
||||
Scenario: Verify loan originator persistence through undo approval
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Undo Approval Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
And Admin successfully approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025"
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
Then Admin can successfully undone the loan approval
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
|
||||
@TestRailId:C4667
|
||||
Scenario: Verify loan originator persistence through undo disbursal
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Undo Disbursal Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
And Admin successfully approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025"
|
||||
When Admin successfully disburse the loan on "1 January 2025" with "1000" EUR transaction amount
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
When Admin successfully undo disbursal
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
|
||||
@TestRailId:C4668
|
||||
Scenario: Verify loan originator persistence through loan charge-off
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Charge Off Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
And Admin successfully approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025"
|
||||
When Admin successfully disburse the loan on "1 January 2025" with "1000" EUR transaction amount
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
And Admin does charge-off the loan on "1 January 2025"
|
||||
Then Loan details with association "originators" has the originator attached
|
||||
|
||||
@TestRailId:C4669
|
||||
Scenario: Verify that originator cannot be attached or detached from disbursed loan
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Disbursed Loan Originator"
|
||||
When Admin creates a second loan originator with external ID and name "Disbursed Loan Extra Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
And Admin successfully approves the loan on "1 January 2025" with "1000" amount and expected disbursement date on "1 January 2025"
|
||||
When Admin successfully disburse the loan on "1 January 2025" with "1000" EUR transaction amount
|
||||
Then Attaching the second originator to the loan should fail with status 403
|
||||
And Detaching the originator from the loan should fail with status 403
|
||||
|
||||
@TestRailId:C4670
|
||||
Scenario: Verify loan originator read operations with retrieve by external ID, list all, and template
|
||||
When Admin creates a new loan originator with all fields and name "CRUD Read Originator"
|
||||
Then Loan originator is retrieved successfully by external ID with all fields
|
||||
And Loan originator list contains the created originator
|
||||
And Loan originator template contains status options, originator type options and channel type options
|
||||
|
||||
@TestRailId:C4671
|
||||
Scenario: Verify loan originator update operations by ID and by external ID
|
||||
When Admin creates a new loan originator with all fields and name "CRUD Update Originator"
|
||||
Then Loan originator is created successfully with status "ACTIVE"
|
||||
When Admin updates the originator name to "Updated Name" and status to "INACTIVE"
|
||||
Then Loan originator has name "Updated Name" and status "INACTIVE"
|
||||
When Admin updates the originator by external ID with name "ExtId Updated Name"
|
||||
Then Loan originator retrieved by external ID has name "ExtId Updated Name"
|
||||
|
||||
@TestRailId:C4672
|
||||
Scenario: Verify loan originator delete operations by ID, by external ID, and deletion prevention when mapped to loan
|
||||
When Admin creates a new loan originator with external ID and name "Delete By ID Originator"
|
||||
When Admin deletes the originator by ID
|
||||
Then Retrieving the deleted originator by ID should fail with status 404
|
||||
When Admin creates a new loan originator with external ID and name "Delete By ExtId Originator"
|
||||
When Admin deletes the originator by external ID
|
||||
Then Retrieving the deleted originator by external ID should fail with status 404
|
||||
When Admin sets the business date to "1 January 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a new loan originator with external ID and name "Mapped Originator"
|
||||
When Admin creates a new default Loan with date: "1 January 2025"
|
||||
When Admin attaches the originator to the loan
|
||||
Then Deleting the originator should fail with status 403
|
||||
|
||||
@TestRailId:C4673
|
||||
Scenario: Verify loan originator CRUD permission checks with create, update, and delete denied without permissions
|
||||
When Admin creates a new loan originator with external ID and name "Permission CRUD Originator"
|
||||
When Admin creates new user with "ORIGINATOR_NO_CREATE" username, "ORIGINATOR_NO_CREATE_ROLE" role name and given permissions:
|
||||
| READ_LOAN |
|
||||
Then Created user without CREATE_LOAN_ORIGINATOR permission fails to create an originator
|
||||
When Admin creates new user with "ORIGINATOR_NO_UPDATE" username, "ORIGINATOR_NO_UPDATE_ROLE" role name and given permissions:
|
||||
| READ_LOAN_ORIGINATOR |
|
||||
Then Created user without UPDATE_LOAN_ORIGINATOR permission fails to update the originator
|
||||
When Admin creates new user with "ORIGINATOR_NO_DELETE" username, "ORIGINATOR_NO_DELETE_ROLE" role name and given permissions:
|
||||
| READ_LOAN_ORIGINATOR |
|
||||
Then Created user without DELETE_LOAN_ORIGINATOR permission fails to delete the originator
|
||||
Reference in New Issue
Block a user