FINERACT-1678: Lock under processing loan accounts

This commit is contained in:
Adam Saghy
2022-09-09 10:00:01 +02:00
committed by Arnold Gálovics
parent fa169bb5d4
commit 2d73c25226
56 changed files with 1906 additions and 156 deletions
@@ -0,0 +1,167 @@
/**
* 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.cob;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import com.google.common.base.Splitter;
import io.cucumber.java8.En;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.TreeMap;
import org.apache.fineract.cob.domain.BatchBusinessStep;
import org.apache.fineract.cob.domain.BatchBusinessStepRepository;
import org.apache.fineract.cob.exceptions.BusinessStepException;
import org.apache.fineract.cob.loan.LoanCOBBusinessStep;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.mix.data.MixTaxonomyData;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.ApplicationContext;
public class COBBusinessStepServiceStepDefinitions implements En {
private ApplicationContext applicationContext = mock(ApplicationContext.class);
private ListableBeanFactory beanFactory = mock(ListableBeanFactory.class);
private BatchBusinessStepRepository batchBusinessStepRepository = mock(BatchBusinessStepRepository.class);
private final COBBusinessStepService businessStepService = new COBBusinessStepServiceImpl(batchBusinessStepRepository,
applicationContext, beanFactory);
private COBBusinessStep cobBusinessStep = mock(COBBusinessStep.class);
private COBBusinessStep notRegistereCobBusinessStep = mock(COBBusinessStep.class);
private TreeMap<Long, String> executionMap;
private AbstractAuditableCustom item;
private AbstractAuditableCustom outputItem = mock(AbstractAuditableCustom.class);
private AbstractAuditableCustom resultItem;
private HashMap<MixTaxonomyData, BigDecimal> data = new HashMap<>();
private String result;
private Class clazz;
private String jobName;
private BatchBusinessStep batchBusinessStep = mock(BatchBusinessStep.class);
private TreeMap<Long, String> resultMap;
public COBBusinessStepServiceStepDefinitions() {
Given("/^The COBBusinessStepService.run method with executeMap (.*)$/", (String executionMap) -> {
if ("null".equals(executionMap)) {
this.executionMap = null;
} else if ("".equals(executionMap)) {
this.executionMap = new TreeMap<>();
} else {
List<String> splitStr = Splitter.on(',').splitToList(executionMap);
Long key = Long.parseLong(splitStr.get(0));
String value = splitStr.get(1);
this.executionMap = new TreeMap<>();
this.executionMap.put(key, value);
}
this.item = mock(AbstractAuditableCustom.class);
lenient().when(this.applicationContext.getBean("test")).thenReturn(cobBusinessStep);
lenient().when(this.applicationContext.getBean("notExist")).thenThrow(BeanCreationException.class);
lenient().when(this.cobBusinessStep.execute(this.item)).thenReturn(outputItem);
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
});
Given("/^The COBBusinessStepService.getCOBBusinessStepMap method with businessStepClass (.*), and jobName (.*)$/",
(String className, String jobName) -> {
if ("null".equals(className)) {
this.clazz = null;
} else if ("empty".equals(className)) {
this.clazz = String.class;
} else if ("LoanCOBBusinessStep".equals(className)) {
this.clazz = LoanCOBBusinessStep.class;
} else {
this.clazz = Object.class;
}
this.jobName = jobName;
lenient().when(this.batchBusinessStepRepository.findAllByJobName("exist"))
.thenReturn(Collections.singletonList(this.batchBusinessStep));
lenient().when(this.batchBusinessStepRepository.findAllByJobName("notExist")).thenReturn(Collections.emptyList());
lenient().when(this.beanFactory.getBeanNamesForType((Class<?>) null)).thenReturn(new String[] { "notExist" });
lenient().when(this.beanFactory.getBeanNamesForType(Object.class)).thenReturn(new String[] { "testNotRegistered" });
lenient().when(this.beanFactory.getBeanNamesForType(String.class)).thenReturn(new String[] {});
lenient().when(this.beanFactory.getBeanNamesForType(LoanCOBBusinessStep.class)).thenReturn(new String[] { "test" });
lenient().when(this.applicationContext.getBean("test")).thenReturn(this.cobBusinessStep);
lenient().when(this.applicationContext.getBean("testNotRegistered")).thenReturn(this.notRegistereCobBusinessStep);
lenient().when(this.applicationContext.getBean("notExist")).thenThrow(BeanCreationException.class);
lenient().when(this.cobBusinessStep.getEnumStyledName()).thenReturn("registered");
lenient().when(this.notRegistereCobBusinessStep.getEnumStyledName()).thenReturn("notRegistered");
lenient().when(this.batchBusinessStep.getStepName()).thenReturn("registered");
lenient().when(this.batchBusinessStep.getStepOrder()).thenReturn(1L);
lenient().when(this.cobBusinessStep.execute(this.item)).thenReturn(outputItem);
});
When("COBBusinessStepService.run method executed", () -> {
resultItem = this.businessStepService.run(this.executionMap, this.item);
});
When("COBBusinessStepService.getCOBBusinessStepMap method executed", () -> {
resultMap = this.businessStepService.getCOBBusinessStepMap(this.clazz, this.jobName);
});
Then("The COBBusinessStepService.run result should match", () -> {
assertEquals(outputItem, resultItem);
assertEquals(ActionContext.COB, ThreadLocalContextUtil.getActionContext());
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
});
Then("throw exception COBBusinessStepService.run method", () -> {
assertThrows(BusinessStepException.class, () -> {
resultItem = this.businessStepService.run(this.executionMap, this.item);
});
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
});
Then("The COBBusinessStepService.getCOBBusinessStepMap result exception", () -> {
assertThrows(BeanCreationException.class, () -> {
this.businessStepService.getCOBBusinessStepMap(this.clazz, this.jobName);
});
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
});
Then("The COBBusinessStepService.getCOBBusinessStepMap result should match", () -> {
assertEquals(1, resultMap.size());
assertEquals("test", resultMap.get(1L));
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
});
Then("The COBBusinessStepService.getCOBBusinessStepMap result empty", () -> {
assertEquals(0, resultMap.size());
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
});
}
}
@@ -0,0 +1,67 @@
/**
* 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.cob.common;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import io.cucumber.java8.En;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.security.core.context.SecurityContextHolder;
public class InitialisationTaskletStepDefinitions implements En {
private AppUserRepositoryWrapper userRepository = mock(AppUserRepositoryWrapper.class);
private InitialisationTasklet initialisationTasklet = new InitialisationTasklet(userRepository);
private AppUser appUser = mock(AppUser.class);
private RepeatStatus resultItem;
public InitialisationTaskletStepDefinitions() {
Given("/^The InitialisationTasklet.execute method with action (.*)$/", (String action) -> {
if ("error".equals(action)) {
lenient().when(this.userRepository.fetchSystemUser()).thenThrow(new RuntimeException("fail"));
} else {
lenient().when(this.userRepository.fetchSystemUser()).thenReturn(appUser);
}
});
When("InitialisationTasklet.execute method executed", () -> {
resultItem = this.initialisationTasklet.execute(null, null);
});
Then("InitialisationTasklet.execute result should match", () -> {
assertEquals(RepeatStatus.FINISHED, resultItem);
assertEquals(appUser, SecurityContextHolder.getContext().getAuthentication().getPrincipal());
});
Then("throw exception InitialisationTasklet.execute method", () -> {
assertThrows(RuntimeException.class, () -> {
resultItem = this.initialisationTasklet.execute(null, null);
});
});
}
}
@@ -0,0 +1,123 @@
/**
* 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.cob.listener;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import io.cucumber.java8.En;
import java.util.List;
import java.util.Optional;
import org.apache.fineract.cob.domain.LoanAccountLock;
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
import org.apache.fineract.cob.domain.LockOwner;
import org.apache.fineract.cob.exceptions.LoanReadException;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.mockito.Mockito;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
public class LoanItemListenerStepDefinitions implements En {
private LoanAccountLockRepository accountLockRepository = mock(LoanAccountLockRepository.class);
private TransactionTemplate transactionTemplate = spy(TransactionTemplate.class);
private LoanItemListener loanItemListener = new LoanItemListener(accountLockRepository, transactionTemplate);
private Exception exception;
private LoanAccountLock loanAccountLock;
private Loan loan = mock(Loan.class);
public LoanItemListenerStepDefinitions() {
Given("/^The LoanItemListener.onReadError method (.*)$/", (String action) -> {
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
exception = new LoanReadException(1L, new RuntimeException("fail"));
loanAccountLock = new LoanAccountLock(1L, LockOwner.LOAN_COB_CHUNK_PROCESSING);
when(accountLockRepository.findByLoanIdAndLockOwner(1L, LockOwner.LOAN_COB_CHUNK_PROCESSING))
.thenReturn(Optional.of(loanAccountLock));
transactionTemplate.setTransactionManager(mock(PlatformTransactionManager.class));
when(loan.getId()).thenReturn(1L);
});
When("LoanItemListener.onReadError method executed", () -> {
this.loanItemListener.onReadError(exception);
});
Then("LoanItemListener.onReadError result should match", () -> {
verify(transactionTemplate, Mockito.times(1)).setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
verify(transactionTemplate, Mockito.times(1)).execute(any());
verify(accountLockRepository, Mockito.times(1)).findByLoanIdAndLockOwner(1L, LockOwner.LOAN_COB_CHUNK_PROCESSING);
assertEquals("Loan (id: 1) reading is failed", loanAccountLock.getError());
assertNotNull(loanAccountLock.getStacktrace());
});
Given("/^The LoanItemListener.onProcessError method (.*)$/", (String action) -> {
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
exception = new LoanReadException(1L, new RuntimeException("fail"));
loanAccountLock = new LoanAccountLock(2L, LockOwner.LOAN_COB_CHUNK_PROCESSING);
when(accountLockRepository.findByLoanIdAndLockOwner(2L, LockOwner.LOAN_COB_CHUNK_PROCESSING))
.thenReturn(Optional.of(loanAccountLock));
when(loan.getId()).thenReturn(2L);
transactionTemplate.setTransactionManager(mock(PlatformTransactionManager.class));
});
When("LoanItemListener.onProcessError method executed", () -> {
this.loanItemListener.onProcessError(loan, exception);
});
Then("LoanItemListener.onProcessError result should match", () -> {
verify(transactionTemplate, Mockito.times(1)).setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
verify(transactionTemplate, Mockito.times(1)).execute(any());
verify(accountLockRepository, Mockito.times(1)).findByLoanIdAndLockOwner(2L, LockOwner.LOAN_COB_CHUNK_PROCESSING);
assertEquals("Loan (id: 2) processing is failed", loanAccountLock.getError());
assertNotNull(loanAccountLock.getStacktrace());
});
Given("/^The LoanItemListener.onWriteError method (.*)$/", (String action) -> {
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
exception = new LoanReadException(3L, new RuntimeException("fail"));
loanAccountLock = new LoanAccountLock(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING);
when(accountLockRepository.findByLoanIdAndLockOwner(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING))
.thenReturn(Optional.of(loanAccountLock));
when(loan.getId()).thenReturn(3L);
transactionTemplate.setTransactionManager(mock(PlatformTransactionManager.class));
});
When("LoanItemListener.onWriteError method executed", () -> {
this.loanItemListener.onWriteError(exception, List.of(loan));
});
Then("LoanItemListener.onWriteError result should match", () -> {
verify(transactionTemplate, Mockito.times(1)).setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
verify(transactionTemplate, Mockito.times(1)).execute(any());
verify(accountLockRepository, Mockito.times(1)).findByLoanIdAndLockOwner(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING);
assertEquals("Loan (id: 3) writing is failed", loanAccountLock.getError());
assertNotNull(loanAccountLock.getStacktrace());
});
}
}
@@ -0,0 +1,92 @@
/**
* 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.cob.loan;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import io.cucumber.java8.En;
import java.util.List;
import org.apache.fineract.cob.domain.LoanAccountLock;
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
import org.apache.fineract.cob.domain.LockOwner;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.RepeatStatus;
public class ApplyLoanLockTaskletStepDefinitions implements En {
ArgumentCaptor<LoanAccountLock> valueCaptor = ArgumentCaptor.forClass(LoanAccountLock.class);
private LoanAccountLockRepository accountLockRepository = mock(LoanAccountLockRepository.class);
private ApplyLoanLockTasklet applyLoanLockTasklet = new ApplyLoanLockTasklet(accountLockRepository);
private RepeatStatus resultItem;
private StepContribution stepContribution;
public ApplyLoanLockTaskletStepDefinitions() {
Given("/^The ApplyLoanLockTasklet.execute method with action (.*)$/", (String action) -> {
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
StepExecution stepExecution = new StepExecution("test", null);
ExecutionContext executionContext = new ExecutionContext();
executionContext.put(LoanCOBConstant.LOAN_IDS, List.of(1L, 2L, 3L, 4L));
stepExecution.setExecutionContext(executionContext);
this.stepContribution = new StepContribution(stepExecution);
if ("error".equals(action)) {
lenient().when(this.accountLockRepository.findAllByLoanIdIn(Mockito.anyList())).thenThrow(new RuntimeException("fail"));
} else {
LoanAccountLock lock1 = new LoanAccountLock(1L, LockOwner.LOAN_COB_CHUNK_PROCESSING);
LoanAccountLock lock2 = new LoanAccountLock(2L, LockOwner.LOAN_COB_PARTITIONING);
LoanAccountLock lock3 = new LoanAccountLock(3L, LockOwner.LOAN_INLINE_COB_PROCESSING);
List<LoanAccountLock> accountLocks = List.of(lock1, lock2, lock3);
lenient().when(this.accountLockRepository.findAllByLoanIdIn(Mockito.anyList())).thenReturn(accountLocks);
}
});
When("ApplyLoanLockTasklet.execute method executed", () -> {
resultItem = this.applyLoanLockTasklet.execute(stepContribution, null);
});
Then("ApplyLoanLockTasklet.execute result should match", () -> {
assertEquals(RepeatStatus.FINISHED, resultItem);
assertEquals(3L, ((List) stepContribution.getStepExecution().getExecutionContext()
.get(LoanCOBWorkerConfiguration.ALREADY_LOCKED_LOAN_IDS)).get(0));
verify(this.accountLockRepository, Mockito.times(2)).save(valueCaptor.capture());
List<LoanAccountLock> values = valueCaptor.getAllValues();
assertEquals(2L, values.get(0).getLoanId());
assertEquals(LockOwner.LOAN_COB_CHUNK_PROCESSING, values.get(0).getLockOwner());
assertEquals(4L, values.get(1).getLoanId());
assertEquals(LockOwner.LOAN_COB_CHUNK_PROCESSING, values.get(1).getLockOwner());
});
Then("throw exception ApplyLoanLockTasklet.execute method", () -> {
assertThrows(RuntimeException.class, () -> {
resultItem = this.applyLoanLockTasklet.execute(stepContribution, null);
});
});
}
}
@@ -0,0 +1,113 @@
/**
* 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.cob.loan;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import io.cucumber.java8.En;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.apache.fineract.cob.COBBusinessStepService;
import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.apache.fineract.infrastructure.springbatch.PropertyService;
import org.mockito.Mockito;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.item.ExecutionContext;
public class LoanCOBPartitionerStepDefinitions implements En {
PropertyService propertyService = mock(PropertyService.class);
COBBusinessStepService cobBusinessStepService = mock(COBBusinessStepService.class);
JobOperator jobOperator = mock(JobOperator.class);
JobExplorer jobExplorer = mock(JobExplorer.class);
RetrieveLoanIdService retrieveLoanIdService = mock(RetrieveLoanIdService.class);
private LoanCOBPartitioner loanCOBPartitioner = new LoanCOBPartitioner(propertyService, cobBusinessStepService, jobOperator,
jobExplorer, retrieveLoanIdService);
private TreeMap<Long, String> cobBusinessMap = new TreeMap<>();
private Map<String, ExecutionContext> resultItem;
private String action;
public LoanCOBPartitionerStepDefinitions() {
Given("/^The LoanCOBPartitioner.partition method with action (.*)$/", (String action) -> {
this.action = action;
lenient().when(propertyService.getPartitionSize(LoanCOBConstant.JOB_NAME)).thenReturn(2);
if ("empty steps".equals(action)) {
lenient().when(cobBusinessStepService.getCOBBusinessStepMap(LoanCOBBusinessStep.class, LoanCOBConstant.LOAN_COB_JOB_NAME))
.thenReturn(new TreeMap<>());
lenient().when(jobExplorer.findRunningJobExecutions(JobName.LOAN_COB.name())).thenReturn(Set.of(new JobExecution(3L)));
lenient().when(jobOperator.stop(3L)).thenReturn(Boolean.TRUE);
} else if ("empty loanIds".equals(action)) {
cobBusinessMap.put(1L, "Business step");
lenient().when(cobBusinessStepService.getCOBBusinessStepMap(LoanCOBBusinessStep.class, LoanCOBConstant.LOAN_COB_JOB_NAME))
.thenReturn(cobBusinessMap);
lenient().when(retrieveLoanIdService.retrieveLoanIds()).thenReturn(Collections.emptyList());
lenient().when(jobExplorer.findRunningJobExecutions(JobName.LOAN_COB.name())).thenThrow(new RuntimeException("fail"));
} else if ("good".equals(action)) {
cobBusinessMap.put(1L, "Business step");
lenient().when(cobBusinessStepService.getCOBBusinessStepMap(LoanCOBBusinessStep.class, LoanCOBConstant.LOAN_COB_JOB_NAME))
.thenReturn(cobBusinessMap);
lenient().when(retrieveLoanIdService.retrieveLoanIds()).thenReturn(List.of(1L, 2L, 3L));
}
});
When("LoanCOBPartitioner.partition method executed", () -> {
resultItem = this.loanCOBPartitioner.partition(2);
});
Then("LoanCOBPartitioner.partition result should match", () -> {
if ("empty steps".equals(action)) {
verify(jobOperator, Mockito.times(1)).stop(3L);
assertTrue(resultItem.isEmpty());
} else if ("good".equals(action)) {
verify(jobOperator, Mockito.times(0)).stop(Mockito.anyLong());
assertEquals(2, resultItem.size());
assertTrue(resultItem.containsKey(LoanCOBPartitioner.PARTITION_PREFIX + "1"));
assertEquals(cobBusinessMap,
resultItem.get(LoanCOBPartitioner.PARTITION_PREFIX + "1").get(LoanCOBConstant.BUSINESS_STEP_MAP));
assertEquals(2, ((List) resultItem.get(LoanCOBPartitioner.PARTITION_PREFIX + "1").get(LoanCOBConstant.LOAN_IDS)).size());
assertEquals(1L, ((List) resultItem.get(LoanCOBPartitioner.PARTITION_PREFIX + "1").get(LoanCOBConstant.LOAN_IDS)).get(0));
assertEquals(2L, ((List) resultItem.get(LoanCOBPartitioner.PARTITION_PREFIX + "1").get(LoanCOBConstant.LOAN_IDS)).get(1));
assertTrue(resultItem.containsKey(LoanCOBPartitioner.PARTITION_PREFIX + "2"));
assertEquals(cobBusinessMap,
resultItem.get(LoanCOBPartitioner.PARTITION_PREFIX + "2").get(LoanCOBConstant.BUSINESS_STEP_MAP));
assertEquals(1, ((List) resultItem.get(LoanCOBPartitioner.PARTITION_PREFIX + "2").get(LoanCOBConstant.LOAN_IDS)).size());
assertEquals(3L, ((List) resultItem.get(LoanCOBPartitioner.PARTITION_PREFIX + "2").get(LoanCOBConstant.LOAN_IDS)).get(0));
}
});
Then("throw exception LoanCOBPartitioner.partition method", () -> {
assertThrows(RuntimeException.class, () -> {
resultItem = this.loanCOBPartitioner.partition(2);
});
});
}
}
@@ -0,0 +1,82 @@
/**
* 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.cob.loan;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import io.cucumber.java8.En;
import java.util.TreeMap;
import org.apache.fineract.cob.COBBusinessStepService;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.item.ExecutionContext;
public class LoanItemProcessorStepDefinitions implements En {
private COBBusinessStepService cobBusinessStepService = mock(COBBusinessStepService.class);
private LoanItemProcessor loanItemProcessor = new LoanItemProcessor(cobBusinessStepService);
private Loan loan = mock(Loan.class);
private Loan loanItem;
private Loan processedLoan = mock(Loan.class);
private Loan resultItem;
private TreeMap<Long, String> treeMap = mock(TreeMap.class);
public LoanItemProcessorStepDefinitions() {
Given("/^The LoanItemProcessor.process method with item (.*)$/", (String loanItem) -> {
StepExecution stepExecution = new StepExecution("test", null);
ExecutionContext stepExecutionContext = new ExecutionContext();
stepExecutionContext.put(LoanCOBConstant.BUSINESS_STEP_MAP, treeMap);
stepExecution.setExecutionContext(stepExecutionContext);
loanItemProcessor.beforeStep(stepExecution);
if (loanItem.isEmpty()) {
this.loanItem = null;
} else {
this.loanItem = loan;
}
lenient().when(this.cobBusinessStepService.run(treeMap, null)).thenThrow(new RuntimeException("fail"));
lenient().when(this.cobBusinessStepService.run(treeMap, loan)).thenReturn(processedLoan);
});
When("LoanItemProcessor.process method executed", () -> {
resultItem = this.loanItemProcessor.process(loanItem);
});
Then("LoanItemProcessor.process result should match", () -> {
assertEquals(processedLoan, resultItem);
});
Then("throw exception LoanItemProcessor.process method", () -> {
assertThrows(RuntimeException.class, () -> {
resultItem = this.loanItemProcessor.process(loanItem);
});
});
}
}
@@ -0,0 +1,99 @@
/**
* 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.cob.loan;
import static org.junit.Assert.assertNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import com.google.common.base.Splitter;
import io.cucumber.java8.En;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.fineract.cob.exceptions.LoanReadException;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.item.ExecutionContext;
public class LoanItemReaderStepDefinitions implements En {
private LoanRepository loanRepository = mock(LoanRepository.class);
private LoanItemReader loanItemReader = new LoanItemReader(loanRepository);
private Loan loan = mock(Loan.class);
private Loan resultItem;
public LoanItemReaderStepDefinitions() {
Given("/^The LoanItemReader.read method with loanIds (.*), lockedAccounts (.*)$/", (String loanIds, String lockedAccounts) -> {
JobExecution jobExecution = new JobExecution(1L);
ExecutionContext jobExecutionContext = new ExecutionContext();
List<Long> splitLockedAccounts;
if (lockedAccounts.isEmpty()) {
splitLockedAccounts = new ArrayList<>();
} else {
List<String> splitLockedAccountsStr = Splitter.on(',').splitToList(lockedAccounts);
splitLockedAccounts = splitLockedAccountsStr.stream().map(Long::parseLong).toList();
}
jobExecutionContext.put(LoanCOBWorkerConfiguration.ALREADY_LOCKED_LOAN_IDS, new ArrayList<>(splitLockedAccounts));
jobExecution.setExecutionContext(jobExecutionContext);
StepExecution stepExecution = new StepExecution("test", jobExecution);
ExecutionContext stepExecutionContext = new ExecutionContext();
List<Long> splitAccounts;
if (loanIds.isEmpty()) {
splitAccounts = new ArrayList<>();
} else {
List<String> splitStr = Splitter.on(',').splitToList(loanIds);
splitAccounts = splitStr.stream().map(Long::parseLong).toList();
}
stepExecutionContext.put(LoanCOBConstant.LOAN_IDS, new ArrayList<>(splitAccounts));
stepExecution.setExecutionContext(stepExecutionContext);
loanItemReader.beforeStep(stepExecution);
lenient().when(this.loanRepository.findById(0L)).thenReturn(Optional.empty());
lenient().when(this.loanRepository.findById(1L)).thenReturn(Optional.of(loan));
lenient().when(this.loanRepository.findById(-1L)).thenThrow(new RuntimeException("fail"));
});
When("LoanItemReader.read method executed", () -> {
resultItem = this.loanItemReader.read();
});
Then("The LoanItemReader.read result should match", () -> {
assertEquals(loan, resultItem);
});
Then("The LoanItemReader.read result null", () -> {
assertNull(resultItem);
});
Then("throw exception LoanItemReader.read method", () -> {
assertThrows(LoanReadException.class, () -> {
resultItem = this.loanItemReader.read();
});
});
}
}
@@ -0,0 +1,74 @@
/**
* 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.cob.loan;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import io.cucumber.java8.En;
import java.util.Collections;
import java.util.List;
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
import org.apache.fineract.cob.domain.LockOwner;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.mockito.Mockito;
public class LoanItemWriterStepDefinitions implements En {
private LoanAccountLockRepository accountLockRepository = mock(LoanAccountLockRepository.class);
private LoanRepository loanRepository = mock(LoanRepository.class);
private LoanItemWriter loanItemWriter = new LoanItemWriter(accountLockRepository);
private List<Loan> items;
public LoanItemWriterStepDefinitions() {
Given("/^The LoanItemWriter.write method with action (.*)$/", (String action) -> {
Loan loan = mock(Loan.class);
lenient().when(loan.getId()).thenReturn(1L);
if (action.equals("error")) {
this.items = Collections.emptyList();
lenient().doThrow(new RuntimeException("fail")).when(this.accountLockRepository)
.deleteByLoanIdInAndLockOwner(Collections.emptyList(), LockOwner.LOAN_COB_CHUNK_PROCESSING);
} else {
this.items = Collections.singletonList(loan);
lenient().doNothing().when(this.accountLockRepository).deleteByLoanIdInAndLockOwner(Mockito.anyList(), Mockito.any());
}
this.loanItemWriter.setRepository(loanRepository);
});
When("LoanItemWriter.write method executed", () -> {
this.loanItemWriter.write(items);
});
Then("LoanItemWriter.write result should match", () -> {
verify(this.accountLockRepository, Mockito.times(1)).deleteByLoanIdInAndLockOwner(Mockito.any(), Mockito.any());
});
Then("throw exception LoanItemWriter.write method", () -> {
assertThrows(RuntimeException.class, () -> {
this.loanItemWriter.write(items);
});
});
}
}
@@ -0,0 +1,72 @@
#
# 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.
#
Feature: COB
@cob
Scenario Outline: COB Business Step Service - run test
Given The COBBusinessStepService.run method with executeMap <executionMap>
When COBBusinessStepService.run method executed
Then The COBBusinessStepService.run result should match
Examples:
|executionMap|
|1,test|
@cob
Scenario Outline: COB Business Step Service - run test failure
Given The COBBusinessStepService.run method with executeMap <executionMap>
Then throw exception COBBusinessStepService.run method
Examples:
|executionMap|
|null|
|2,notExist|
|3,|
@cob
Scenario Outline: COB Business Step Service - getCOBBusinessStepMap test found bean
Given The COBBusinessStepService.getCOBBusinessStepMap method with businessStepClass <className>, and jobName <jobName>
When COBBusinessStepService.getCOBBusinessStepMap method executed
Then The COBBusinessStepService.getCOBBusinessStepMap result should match
Examples:
|className|jobName|
|LoanCOBBusinessStep|exist|
@cob
Scenario Outline: COB Business Step Service - getCOBBusinessStepMap empty
Given The COBBusinessStepService.getCOBBusinessStepMap method with businessStepClass <className>, and jobName <jobName>
When COBBusinessStepService.getCOBBusinessStepMap method executed
Then The COBBusinessStepService.getCOBBusinessStepMap result empty
Examples:
|className|jobName|
|LoanCOBBusinessStep|notExist|
|empty|exist|
|random|exist|
@cob
Scenario Outline: COB Business Step Service - getCOBBusinessStepMap no bean
Given The COBBusinessStepService.getCOBBusinessStepMap method with businessStepClass <className>, and jobName <jobName>
Then The COBBusinessStepService.getCOBBusinessStepMap result exception
Examples:
|className|jobName|
|null|notExist|
@@ -0,0 +1,39 @@
#
# 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.
#
Feature: COB Initialisation Step
@cob
Scenario Outline: InitialisationTasklet - run test
Given The InitialisationTasklet.execute method with action <action>
When InitialisationTasklet.execute method executed
Then InitialisationTasklet.execute result should match
Examples:
|action|
|good|
@cob
Scenario Outline: InitialisationTasklet - run test: exception
Given The InitialisationTasklet.execute method with action <action>
Then throw exception InitialisationTasklet.execute method
Examples:
|action|
|error|
@@ -0,0 +1,50 @@
#
# 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.
#
Feature: COB Loan item Listener
@cob
Scenario Outline: LoanItemListener.onReadError - run test
Given The LoanItemListener.onReadError method <action>
When LoanItemListener.onReadError method executed
Then LoanItemListener.onReadError result should match
Examples:
|action|
|run |
@cob
Scenario Outline: LoanItemListener.onProcessError - run test
Given The LoanItemListener.onProcessError method <action>
When LoanItemListener.onProcessError method executed
Then LoanItemListener.onProcessError result should match
Examples:
|action|
|run |
@cob
Scenario Outline: LoanItemListener.onWriteError - run test
Given The LoanItemListener.onWriteError method <action>
When LoanItemListener.onWriteError method executed
Then LoanItemListener.onWriteError result should match
Examples:
|action|
|run |
@@ -0,0 +1,39 @@
#
# 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.
#
Feature: COB Apply Loan Lock Step
@cob
Scenario Outline: ApplyLoanLockTasklet - run test
Given The ApplyLoanLockTasklet.execute method with action <action>
When ApplyLoanLockTasklet.execute method executed
Then ApplyLoanLockTasklet.execute result should match
Examples:
|action|
|good|
@cob
Scenario Outline: ApplyLoanLockTasklet - run test: exception
Given The ApplyLoanLockTasklet.execute method with action <action>
Then throw exception ApplyLoanLockTasklet.execute method
Examples:
|action|
|error|
@@ -0,0 +1,39 @@
#
# 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.
#
Feature: COB Process
@cob
Scenario Outline: LoanItemProcessor - run test
Given The LoanItemProcessor.process method with item <item>
When LoanItemProcessor.process method executed
Then LoanItemProcessor.process result should match
Examples:
|item|
|1 |
@cob
Scenario Outline: LoanItemProcessor - run test: exception
Given The LoanItemProcessor.process method with item <item>
Then throw exception LoanItemProcessor.process method
Examples:
|item|
||
@@ -0,0 +1,52 @@
#
# 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.
#
Feature: COB Reader
@cob
Scenario Outline: LoanItemReader - run test
Given The LoanItemReader.read method with loanIds <loanIds>, lockedAccounts <lockedAccounts>
When LoanItemReader.read method executed
Then The LoanItemReader.read result should match
Examples:
|loanIds|lockedAccounts|
|1 | |
|1,2 |3,4 |
@cob
Scenario Outline: LoanItemReader - run test: null
Given The LoanItemReader.read method with loanIds <loanIds>, lockedAccounts <lockedAccounts>
When LoanItemReader.read method executed
Then The LoanItemReader.read result null
Examples:
|loanIds|lockedAccounts|
| | |
@cob
Scenario Outline: LoanItemReader - run test: exception
Given The LoanItemReader.read method with loanIds <loanIds>, lockedAccounts <lockedAccounts>
Then throw exception LoanItemReader.read method
Examples:
|loanIds|lockedAccounts|
|-1 | |
|1 | 1 |
|0 | |
@@ -0,0 +1,39 @@
#
# 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.
#
Feature: COB Writer
@cob
Scenario Outline: LoanItemWriter - run test
Given The LoanItemWriter.write method with action <action>
When LoanItemWriter.write method executed
Then LoanItemWriter.write result should match
Examples:
|action|
|good|
@cob
Scenario Outline: LoanItemWriter - run test: exception
Given The LoanItemWriter.write method with action <action>
Then throw exception LoanItemWriter.write method
Examples:
|action|
|error|
@@ -0,0 +1,40 @@
#
# 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.
#
Feature: COB Loan partitioner Step
@cob
Scenario Outline: LoanCOBPartitioner - run test
Given The LoanCOBPartitioner.partition method with action <action>
When LoanCOBPartitioner.partition method executed
Then LoanCOBPartitioner.partition result should match
Examples:
|action|
|empty steps|
|good|
@cob
Scenario Outline: LoanCOBPartitioner - run test: exception
Given The LoanCOBPartitioner.partition method with action <action>
Then throw exception LoanCOBPartitioner.partition method
Examples:
|action|
|empty loanIds|