This is an automated email from the ASF dual-hosted git repository.
arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 7be193dd0 [FINERACT-1678] Reject write API calls for hard-locked loan
accounts
7be193dd0 is described below
commit 7be193dd0b95f7f5b51a23908e08e843f22881a3
Author: taskain7 <[email protected]>
AuthorDate: Mon Oct 17 12:12:48 2022 +0200
[FINERACT-1678] Reject write API calls for hard-locked loan accounts
---
.../cob/service/LoanAccountLockService.java | 2 +
.../cob/service/LoanAccountLockServiceImpl.java | 12 ++
.../infrastructure/core/config/SecurityConfig.java | 4 +
.../core/data/ApiGlobalErrorResponse.java | 10 ++
.../jobs/filter/LoanCOBApiFilter.java | 133 ++++++++++++++++++++
.../LoanWritePlatformServiceJpaRepositoryImpl.java | 29 +++--
.../jobs/filter/LoanCOBApiFilterTest.java | 135 +++++++++++++++++++++
7 files changed, 317 insertions(+), 8 deletions(-)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanAccountLockService.java
b/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanAccountLockService.java
index e1daca429..7f584d6ba 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanAccountLockService.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanAccountLockService.java
@@ -24,4 +24,6 @@ import org.apache.fineract.cob.domain.LoanAccountLock;
public interface LoanAccountLockService {
List<LoanAccountLock> getLockedLoanAccountByPage(int page, int limit);
+
+ boolean isLoanHardLocked(Long loanId);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanAccountLockServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanAccountLockServiceImpl.java
index 63a2cd606..4eeb31570 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanAccountLockServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanAccountLockServiceImpl.java
@@ -19,9 +19,12 @@
package org.apache.fineract.cob.service;
import java.util.List;
+import java.util.Optional;
import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.cob.domain.LoanAccountLock;
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
+import org.apache.fineract.cob.domain.LockOwner;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@@ -39,4 +42,13 @@ public class LoanAccountLockServiceImpl implements
LoanAccountLockService {
Page<LoanAccountLock> loanAccountLocks =
loanAccountLockRepository.findAll(loanAccountLockPage);
return loanAccountLocks.getContent();
}
+
+ @Override
+ public boolean isLoanHardLocked(Long loanId) {
+ Optional<LoanAccountLock> loanAccountLockOptional =
loanAccountLockRepository.findById(loanId);
+ return loanAccountLockOptional //
+ .filter(loanAccountLock ->
LockOwner.LOAN_COB_CHUNK_PROCESSING.equals(loanAccountLock.getLockOwner()) //
+ ||
LockOwner.LOAN_INLINE_COB_PROCESSING.equals(loanAccountLock.getLockOwner())) //
+ .filter(loanAccountLock ->
StringUtils.isBlank(loanAccountLock.getError())).isPresent();
+ }
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java
index eb3005199..3901e092f 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java
@@ -20,6 +20,7 @@
package org.apache.fineract.infrastructure.core.config;
import
org.apache.fineract.infrastructure.instancemode.filter.FineractInstanceModeApiFilter;
+import org.apache.fineract.infrastructure.jobs.filter.LoanCOBApiFilter;
import
org.apache.fineract.infrastructure.security.filter.TenantAwareBasicAuthenticationFilter;
import
org.apache.fineract.infrastructure.security.filter.TwoFactorAuthenticationFilter;
import
org.apache.fineract.infrastructure.security.service.TenantAwareJpaPlatformUserDetailsService;
@@ -56,6 +57,8 @@ public class SecurityConfig extends
WebSecurityConfigurerAdapter {
@Autowired
private FineractInstanceModeApiFilter fineractInstanceModeApiFilter;
+ @Autowired
+ private LoanCOBApiFilter loanCOBApiFilter;
@Autowired
private FineractProperties fineractProperties;
@@ -86,6 +89,7 @@ public class SecurityConfig extends
WebSecurityConfigurerAdapter {
.and() //
.addFilterAfter(fineractInstanceModeApiFilter,
SecurityContextPersistenceFilter.class) //
.addFilterAfter(tenantAwareBasicAuthenticationFilter(),
FineractInstanceModeApiFilter.class) //
+ .addFilterAfter(loanCOBApiFilter,
TenantAwareBasicAuthenticationFilter.class) //
.addFilterAfter(twoFactorAuthenticationFilter,
BasicAuthenticationFilter.class); //
if (serverProperties.getSsl().isEnabled()) {
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/ApiGlobalErrorResponse.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/ApiGlobalErrorResponse.java
index 753fe555b..8bf5e4664 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/ApiGlobalErrorResponse.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/ApiGlobalErrorResponse.java
@@ -89,6 +89,16 @@ public class ApiGlobalErrorResponse {
return globalErrorResponse;
}
+ public static ApiGlobalErrorResponse loanIsLocked(final Long loanId) {
+ final ApiGlobalErrorResponse globalErrorResponse = new
ApiGlobalErrorResponse();
+ globalErrorResponse.setHttpStatusCode(Status.CONFLICT.toString());
+ globalErrorResponse.setDeveloperMessage("Loan is locked by the COB
job. Loan ID: " + loanId);
+
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.loan.locked");
+ globalErrorResponse.setDefaultUserMessage("Loan is locked by the COB
job. Loan ID: \" + loanId");
+
+ return globalErrorResponse;
+ }
+
public static ApiGlobalErrorResponse unAuthorized(final String
defaultUserMessage) {
final ApiGlobalErrorResponse globalErrorResponse = new
ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("403");
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java
new file mode 100644
index 000000000..099161fd0
--- /dev/null
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java
@@ -0,0 +1,133 @@
+/**
+ * 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.infrastructure.jobs.filter;
+
+import com.google.common.base.Splitter;
+import com.sun.research.ws.wadl.HTTPMethods;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.cob.service.LoanAccountLockService;
+import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse;
+import
org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
+import
org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+@Component
+@RequiredArgsConstructor
+public class LoanCOBApiFilter extends OncePerRequestFilter {
+
+ private final GLIMAccountInfoRepository glimAccountInfoRepository;
+ private final LoanAccountLockService loanAccountLockService;
+
+ private static final List<HTTPMethods> HTTP_METHODS =
List.of(HTTPMethods.POST, HTTPMethods.PUT, HTTPMethods.DELETE);
+ private static final Function<String, Boolean> URL_FUNCTION = s ->
s.matches("/loans/\\d+.*") || s.matches("/loans/glimAccount/\\d+.*");
+ private static final Integer LOAN_ID_INDEX_IN_URL = 2;
+ private static final Integer GLIM_ID_INDEX_IN_URL = 3;
+ private static final Integer GLIM_STRING_INDEX_IN_URL = 2;
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+ if (!isOnApiList(request)) {
+ proceed(filterChain, request, response);
+ } else {
+ Iterable<String> split =
Splitter.on('/').split(request.getPathInfo());
+ Supplier<Stream<String>> streamSupplier = () ->
StreamSupport.stream(split.spliterator(), false);
+ boolean isGlim = isGlim(streamSupplier);
+ Long loanId = getLoanId(isGlim, streamSupplier);
+ if (isLoanLocked(loanId, isGlim)) {
+ reject(loanId, response);
+ } else {
+ proceed(filterChain, request, response);
+ }
+ }
+ }
+
+ private boolean isLoanLocked(Long loanId, boolean isGlim) {
+ if (!isGlim) {
+ return loanAccountLockService.isLoanHardLocked(loanId);
+ } else {
+ GroupLoanIndividualMonitoringAccount glimAccount =
glimAccountInfoRepository.findOneByIsAcceptingChildAndApplicationId(true,
+ BigDecimal.valueOf(loanId));
+ if (glimAccount != null) {
+ Set<Loan> loans = glimAccount.getChildLoan();
+ List<Long> loanIds = loans.stream().map(Loan::getId).toList();
+ return
loanIds.stream().anyMatch(loanAccountLockService::isLoanHardLocked);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private void proceed(FilterChain filterChain, HttpServletRequest request,
HttpServletResponse response)
+ throws IOException, ServletException {
+ filterChain.doFilter(request, response);
+ }
+
+ private void reject(Long loanId, HttpServletResponse response) throws
IOException {
+ response.setStatus(HttpStatus.SC_CONFLICT);
+ ApiGlobalErrorResponse errorResponse =
ApiGlobalErrorResponse.loanIsLocked(loanId);
+ response.getWriter().write(errorResponse.toJson());
+ }
+
+ private Long getLoanId(boolean isGlim, Supplier<Stream<String>>
streamSupplier) {
+ if (!isGlim) {
+ if (streamSupplier.get().count() >= LOAN_ID_INDEX_IN_URL + 1) {
+ return
Long.valueOf(streamSupplier.get().skip(LOAN_ID_INDEX_IN_URL).findFirst().get());
+ } else {
+ return null;
+ }
+ } else {
+ if (streamSupplier.get().count() >= GLIM_ID_INDEX_IN_URL + 1) {
+ return
Long.valueOf(streamSupplier.get().skip(GLIM_ID_INDEX_IN_URL).findFirst().get());
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private boolean isOnApiList(HttpServletRequest request) {
+ if (StringUtils.isBlank(request.getPathInfo())) {
+ return false;
+ }
+ return
HTTP_METHODS.contains(HTTPMethods.fromValue(request.getMethod())) &&
URL_FUNCTION.apply(request.getPathInfo());
+ }
+
+ private boolean isGlim(Supplier<Stream<String>> streamSupplier) {
+ if (streamSupplier.get().count() >= GLIM_STRING_INDEX_IN_URL + 1) {
+ return
"glimAccount".equals(streamSupplier.get().skip(GLIM_STRING_INDEX_IN_URL).findFirst().get());
+ }
+ return false;
+ }
+}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 349546f1c..1db95735b 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -39,6 +39,9 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import
org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
+import org.apache.fineract.cob.domain.LoanAccountLockRepository;
+import
org.apache.fineract.cob.exceptions.LoanAccountLockCannotBeOverruledException;
+import org.apache.fineract.cob.service.LoanAccountLockService;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import
org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
@@ -278,6 +281,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl
implements LoanWritePlatf
private final PostDatedChecksRepository postDatedChecksRepository;
private final LoanDisbursementDetailsRepository
loanDisbursementDetailsRepository;
private final LoanRepaymentScheduleInstallmentRepository
loanRepaymentScheduleInstallmentRepository;
+ private final LoanAccountLockRepository loanAccountLockRepository;
+ private final LoanAccountLockService loanAccountLockService;
private static boolean isPartOfThisInstallment(LoanCharge loanCharge,
LoanRepaymentScheduleInstallment e) {
return e.getFromDate().isBefore(loanCharge.getDueDate()) &&
!loanCharge.getDueDate().isAfter(e.getDueDate());
@@ -2551,20 +2556,28 @@ public class LoanWritePlatformServiceJpaRepositoryImpl
implements LoanWritePlatf
final Staff fromLoanOfficer =
this.loanAssembler.findLoanOfficerByIdIfProvided(fromLoanOfficerId);
final Staff toLoanOfficer =
this.loanAssembler.findLoanOfficerByIdIfProvided(toLoanOfficerId);
+ List<Long> lockedLoanIds = new ArrayList<>();
for (final String loanIdString : loanIds) {
final Long loanId = Long.valueOf(loanIdString);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
- businessEventNotifierService.notifyPreBusinessEvent(new
LoanReassignOfficerBusinessEvent(loan));
- checkClientOrGroupActive(loan);
+ if (loanAccountLockService.isLoanHardLocked(loanId)) {
+ lockedLoanIds.add(loanId);
+ } else {
+ businessEventNotifierService.notifyPreBusinessEvent(new
LoanReassignOfficerBusinessEvent(loan));
+ checkClientOrGroupActive(loan);
- if (!loan.hasLoanOfficer(fromLoanOfficer)) {
- throw new LoanOfficerAssignmentException(loanId,
fromLoanOfficerId);
- }
+ if (!loan.hasLoanOfficer(fromLoanOfficer)) {
+ throw new LoanOfficerAssignmentException(loanId,
fromLoanOfficerId);
+ }
- loan.reassignLoanOfficer(toLoanOfficer,
dateOfLoanOfficerAssignment);
- saveLoanWithDataIntegrityViolationChecks(loan);
- businessEventNotifierService.notifyPostBusinessEvent(new
LoanReassignOfficerBusinessEvent(loan));
+ loan.reassignLoanOfficer(toLoanOfficer,
dateOfLoanOfficerAssignment);
+ saveLoanWithDataIntegrityViolationChecks(loan);
+ businessEventNotifierService.notifyPostBusinessEvent(new
LoanReassignOfficerBusinessEvent(loan));
+ }
+ }
+ if (!lockedLoanIds.isEmpty()) {
+ throw new LoanAccountLockCannotBeOverruledException("There are
hard-lcoked loan accounts: " + lockedLoanIds);
}
this.loanRepositoryWrapper.flush();
diff --git
a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java
b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java
new file mode 100644
index 000000000..de7bf1ce1
--- /dev/null
+++
b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java
@@ -0,0 +1,135 @@
+/**
+ * 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.infrastructure.jobs.filter;
+
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.sun.research.ws.wadl.HTTPMethods;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.math.BigDecimal;
+import java.util.Collections;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import org.apache.fineract.cob.service.LoanAccountLockService;
+import
org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
+import
org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.http.HttpStatus;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+class LoanCOBApiFilterTest {
+
+ @InjectMocks
+ private LoanCOBApiFilter testObj;
+ @Mock
+ private LoanAccountLockService loanAccountLockService;
+ @Mock
+ private GLIMAccountInfoRepository glimAccountInfoRepository;
+
+ @Test
+ void shouldProceedWhenUrlDoesNotMatch() throws ServletException,
IOException {
+ MockHttpServletRequest request = mock(MockHttpServletRequest.class);
+ given(request.getPathInfo()).willReturn("/jobs/2/inline");
+ given(request.getMethod()).willReturn(HTTPMethods.POST.value());
+ MockHttpServletResponse response = mock(MockHttpServletResponse.class);
+ FilterChain filterChain = mock(FilterChain.class);
+ testObj.doFilterInternal(request, response, filterChain);
+ verify(filterChain, times(1)).doFilter(request, response);
+ }
+
+ @Test
+ void shouldProceedWhenLoanIsNotLocked() throws ServletException,
IOException {
+ MockHttpServletRequest request = mock(MockHttpServletRequest.class);
+ MockHttpServletResponse response = mock(MockHttpServletResponse.class);
+ FilterChain filterChain = mock(FilterChain.class);
+
+ given(request.getPathInfo()).willReturn("/loans/2/charges");
+ given(request.getMethod()).willReturn(HTTPMethods.POST.value());
+ given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false);
+
+ testObj.doFilterInternal(request, response, filterChain);
+ verify(filterChain, times(1)).doFilter(request, response);
+ }
+
+ @Test
+ void shouldProceedWhenLoanIsSoftLocked() throws ServletException,
IOException {
+ MockHttpServletRequest request = mock(MockHttpServletRequest.class);
+ MockHttpServletResponse response = mock(MockHttpServletResponse.class);
+ FilterChain filterChain = mock(FilterChain.class);
+
+ given(request.getPathInfo()).willReturn("/loans/2/charges");
+ given(request.getMethod()).willReturn(HTTPMethods.POST.value());
+ given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false);
+
+ testObj.doFilterInternal(request, response, filterChain);
+ verify(filterChain, times(1)).doFilter(request, response);
+ }
+
+ @Test
+ void shouldRejectWhenLoanIsHardLocked() throws ServletException,
IOException {
+ MockHttpServletRequest request = mock(MockHttpServletRequest.class);
+ MockHttpServletResponse response = mock(MockHttpServletResponse.class);
+ FilterChain filterChain = mock(FilterChain.class);
+ PrintWriter writer = mock(PrintWriter.class);
+
+ given(request.getPathInfo()).willReturn("/loans/2/charges");
+ given(request.getMethod()).willReturn(HTTPMethods.POST.value());
+ given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(true);
+ given(response.getWriter()).willReturn(writer);
+
+ testObj.doFilterInternal(request, response, filterChain);
+ verify(response, times(1)).setStatus(HttpStatus.SC_CONFLICT);
+ }
+
+ @Test
+ void shouldRejectWhenGlimLoanIsHardLocked() throws ServletException,
IOException {
+ MockHttpServletRequest request = mock(MockHttpServletRequest.class);
+ MockHttpServletResponse response = mock(MockHttpServletResponse.class);
+ FilterChain filterChain = mock(FilterChain.class);
+ PrintWriter writer = mock(PrintWriter.class);
+ GroupLoanIndividualMonitoringAccount glimAccount =
mock(GroupLoanIndividualMonitoringAccount.class);
+ Loan loan = mock(Loan.class);
+ Long loanId = 2L;
+
+ given(request.getPathInfo()).willReturn("/loans/glimAccount/2");
+ given(request.getMethod()).willReturn(HTTPMethods.POST.value());
+
given(glimAccountInfoRepository.findOneByIsAcceptingChildAndApplicationId(true,
BigDecimal.valueOf(2))).willReturn(glimAccount);
+
given(glimAccount.getChildLoan()).willReturn(Collections.singleton(loan));
+ given(loan.getId()).willReturn(loanId);
+
given(loanAccountLockService.isLoanHardLocked(loanId)).willReturn(true);
+ given(response.getWriter()).willReturn(writer);
+
+ testObj.doFilterInternal(request, response, filterChain);
+ verify(response, times(1)).setStatus(HttpStatus.SC_CONFLICT);
+ }
+}