FINERACT-2380: move retrofit client based test to feign

This commit is contained in:
Attila Budai
2025-11-26 10:47:29 +01:00
committed by Adam Saghy
parent 6204cde27c
commit 5d2a9c5390
77 changed files with 4908 additions and 5039 deletions
@@ -61,6 +61,7 @@ import org.apache.fineract.client.feign.services.EntityDataTableApi;
import org.apache.fineract.client.feign.services.EntityFieldConfigurationApi;
import org.apache.fineract.client.feign.services.ExternalAssetOwnerLoanProductAttributesApi;
import org.apache.fineract.client.feign.services.ExternalAssetOwnersApi;
import org.apache.fineract.client.feign.services.ExternalAssetOwnersApiExtension;
import org.apache.fineract.client.feign.services.ExternalEventConfigurationApi;
import org.apache.fineract.client.feign.services.ExternalServicesApi;
import org.apache.fineract.client.feign.services.FetchAuthenticatedUserDetailsApi;
@@ -95,6 +96,7 @@ import org.apache.fineract.client.feign.services.LoanCobCatchUpApi;
import org.apache.fineract.client.feign.services.LoanCollateralApi;
import org.apache.fineract.client.feign.services.LoanCollateralManagementApi;
import org.apache.fineract.client.feign.services.LoanDisbursementDetailsApi;
import org.apache.fineract.client.feign.services.LoanDisbursementDetailsApiExtension;
import org.apache.fineract.client.feign.services.LoanInterestPauseApi;
import org.apache.fineract.client.feign.services.LoanProductsApi;
import org.apache.fineract.client.feign.services.LoanReschedulingApi;
@@ -392,6 +394,10 @@ public final class FineractFeignClient {
return create(ExternalAssetOwnersApi.class);
}
public ExternalAssetOwnersApiExtension externalAssetOwnersExtension() {
return create(ExternalAssetOwnersApiExtension.class);
}
public ExternalEventConfigurationApi externalEventConfiguration() {
return create(ExternalEventConfigurationApi.class);
}
@@ -528,6 +534,10 @@ public final class FineractFeignClient {
return create(LoanDisbursementDetailsApi.class);
}
public LoanDisbursementDetailsApiExtension loanDisbursementDetailsExtension() {
return create(LoanDisbursementDetailsApiExtension.class);
}
public LoanInterestPauseApi loanInterestPause() {
return create(LoanInterestPauseApi.class);
}
@@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.fineract.client.feign.support.ApiResponseDecoder;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
@@ -95,7 +96,8 @@ public final class FineractFeignClientConfig {
Encoder multipartEncoder = new FineractMultipartEncoder(jacksonEncoder);
return Feign.builder().client(getOrCreateHttpClient()).encoder(multipartEncoder)
.decoder(new JacksonDecoder(ObjectMapperFactory.getShared())).errorDecoder(new FineractErrorDecoder())
.decoder(new ApiResponseDecoder(new JacksonDecoder(ObjectMapperFactory.getShared())))
.errorDecoder(new FineractErrorDecoder())
.options(new Request.Options(connectTimeout, TimeUnit.MILLISECONDS, readTimeout, TimeUnit.MILLISECONDS, true))
.retryer(Retryer.NEVER_RETRY).requestInterceptor(new BasicAuthRequestInterceptor(username, password))
.requestInterceptor(new TenantIdRequestInterceptor(tenantId)).logger(new Slf4jLogger(apiType))
@@ -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.
*/
package org.apache.fineract.client.feign.services;
import feign.Headers;
import feign.Param;
import feign.QueryMap;
import feign.RequestLine;
import java.util.Map;
import org.apache.fineract.client.models.ExternalAssetOwnerRequest;
import org.apache.fineract.client.models.PostInitiateTransferResponse;
public interface ExternalAssetOwnersApiExtension {
@RequestLine("POST /v1/external-asset-owners/transfers/{id}")
@Headers("Content-Type: application/json")
PostInitiateTransferResponse transferRequestWithIdWithBody(@Param("id") Long id, ExternalAssetOwnerRequest body,
@QueryMap Map<String, Object> queryParams);
@RequestLine("POST /v1/external-asset-owners/transfers/external-id/{externalId}")
@Headers("Content-Type: application/json")
PostInitiateTransferResponse transferRequestWithId1WithBody(@Param("externalId") String externalId, ExternalAssetOwnerRequest body,
@QueryMap Map<String, Object> queryParams);
}
@@ -16,15 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.test.stepdef.loan;
package org.apache.fineract.client.feign.services;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import retrofit2.Call;
import retrofit2.http.GET;
import feign.Param;
import feign.RequestLine;
import org.apache.fineract.client.models.PostAddAndDeleteDisbursementDetailRequest;
public interface LoanProductsCustomApi {
public interface LoanDisbursementDetailsApiExtension {
@RequestLine("PUT /v1/loans/{loanId}/disbursements/editDisbursements")
PostAddAndDeleteDisbursementDetailRequest addAndDeleteDisbursementDetail(@Param("loanId") Long loanId,
PostAddAndDeleteDisbursementDetailRequest postAddAndDeleteDisbursementDetailRequest);
@GET("v1/loanproducts/{productId}")
Call<GetLoanProductsProductIdResponse> retrieveLoanProductDetails(@retrofit2.http.Path("productId") Long productId,
@retrofit2.http.Query("template") String isTemplate);
}
@@ -0,0 +1,81 @@
/**
* 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.client.feign.support;
import feign.Response;
import feign.codec.Decoder;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.apache.fineract.client.models.ApiResponse;
/**
* Custom Feign decoder that handles ApiResponse&lt;T&gt; return types for *WithHttpInfo methods.
*
* When a Feign client method returns ApiResponse&lt;T&gt;, the standard JacksonDecoder cannot handle it because:
* <ul>
* <li>The server returns just T (the body), not ApiResponse&lt;T&gt;</li>
* <li>ApiResponse wraps the body with HTTP status code and headers</li>
* </ul>
*
* This decoder:
* <ol>
* <li>Detects if the return type is ApiResponse&lt;T&gt;</li>
* <li>Extracts the inner type T and delegates body decoding to the underlying decoder</li>
* <li>Wraps the decoded body with status code and headers into ApiResponse&lt;T&gt;</li>
* </ol>
*/
public final class ApiResponseDecoder implements Decoder {
private final Decoder delegate;
public ApiResponseDecoder(Decoder delegate) {
this.delegate = delegate;
}
@Override
public Object decode(Response response, Type type) throws IOException {
if (isApiResponseType(type)) {
Type innerType = getApiResponseInnerType(type);
Object body = delegate.decode(response, innerType);
return new ApiResponse<>(response.status(), response.headers(), body);
}
return delegate.decode(response, type);
}
private boolean isApiResponseType(Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
Type rawType = paramType.getRawType();
return rawType == ApiResponse.class;
}
return type == ApiResponse.class;
}
private Type getApiResponseInnerType(Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
Type[] typeArgs = paramType.getActualTypeArguments();
if (typeArgs.length > 0) {
return typeArgs[0];
}
}
return Object.class;
}
}
@@ -18,13 +18,9 @@
*/
package org.apache.fineract.client.feign.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.FeignException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FeignException;
/**
* Exception thrown by {@link FeignCalls} utility when Feign calls fail.
@@ -49,7 +45,7 @@ public class CallFailedRuntimeException extends RuntimeException {
sb.append(", request=").append(e.request().url());
}
String contentString = e.contentUTF8();
String contentString = e.responseBodyAsString();
if (contentString != null && !contentString.isEmpty()) {
sb.append(", errorBody=").append(contentString);
}
@@ -58,34 +54,9 @@ public class CallFailedRuntimeException extends RuntimeException {
}
private static String extractDeveloperMessage(FeignException e) {
try {
byte[] content = e.content();
if (content == null || content.length == 0) {
return e.getMessage();
}
String contentString = new String(content, StandardCharsets.UTF_8);
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(contentString);
if (root.has("developerMessage")) {
return root.get("developerMessage").asText();
}
if (root.has("errors")) {
JsonNode errors = root.get("errors");
if (errors.isArray() && errors.size() > 0) {
JsonNode firstError = errors.get(0);
if (firstError.has("developerMessage")) {
return firstError.get("developerMessage").asText();
}
}
}
return contentString;
} catch (IOException ex) {
log.warn("Failed to extract developer message from error response", ex);
return e.getMessage();
if (e.getDeveloperMessage() != null) {
return e.getDeveloperMessage();
}
return e.getMessage();
}
}
@@ -18,8 +18,8 @@
*/
package org.apache.fineract.client.feign.util;
import feign.FeignException;
import java.util.function.Supplier;
import org.apache.fineract.client.feign.FeignException;
/**
* Extension methods for Feign calls. This class is recommended to be statically imported.
@@ -77,4 +77,41 @@ public final class FeignCalls {
public static <T> T execute(Supplier<T> feignCall) throws FeignException {
return feignCall.get();
}
/**
* Execute a Feign call expecting failure (for negative tests). Returns the exception with error details.
*
* @param feignCall
* the Feign call to execute
* @return CallFailedRuntimeException containing status code and error message
* @throws AssertionError
* if the call succeeds when failure was expected
*/
public static CallFailedRuntimeException fail(Supplier<?> feignCall) {
try {
Object result = feignCall.get();
throw new AssertionError("Expected call to fail, but it succeeded with result: " + result);
} catch (FeignException e) {
return new CallFailedRuntimeException(e);
}
}
/**
* Execute a Feign call expecting failure with void return (for negative tests). Returns the exception with error
* details.
*
* @param feignCall
* the Feign call to execute
* @return CallFailedRuntimeException containing status code and error message
* @throws AssertionError
* if the call succeeds when failure was expected
*/
public static CallFailedRuntimeException failVoid(Runnable feignCall) {
try {
feignCall.run();
throw new AssertionError("Expected call to fail, but it succeeded");
} catch (FeignException e) {
return new CallFailedRuntimeException(e);
}
}
}
@@ -1,47 +1,326 @@
package {{package}};
import feign.*;
import feign.Param;
import feign.RequestLine;
{{#legacyDates}}
import {{invokerPackage}}.ParamExpander;
{{/legacyDates}}
import {{modelPackage}}.ApiResponse;
{{#imports}}import {{import}};
{{/imports}}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
{{#useBeanValidation}}
import {{javaxPackage}}.validation.constraints.*;
import {{javaxPackage}}.validation.Valid;
{{/useBeanValidation}}
import feign.*;
{{>generatedAnnotation}}
public interface {{classname}} {
{{#operations}}
{{#operation}}
/**
* {{summary}}
* {{notes}}
{{#allParams}}
* @param {{paramName}} {{description}}
{{/allParams}}
* @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
*/
@RequestLine("{{httpMethod}} {{path}}")
{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}}(
{{#pathParams}}
@Param("{{baseName}}") {{{dataType}}} {{paramName}}{{^-last}},{{/-last}}
{{/pathParams}}
{{#bodyParam}}
{{#hasPathParams}},{{/hasPathParams}}{{{dataType}}} {{paramName}}
{{/bodyParam}}
{{#hasQueryParams}}
{{#hasPathParams}}{{^bodyParam}},{{/bodyParam}}{{/hasPathParams}}{{#bodyParam}},{{/bodyParam}}
@QueryMap Map<String, Object> queryParams
{{/hasQueryParams}}
{{#hasHeaderParams}}
{{#hasPathParams}}{{^bodyParam}}{{^hasQueryParams}},{{/hasQueryParams}}{{/bodyParam}}{{/hasPathParams}}
{{#bodyParam}}{{^hasQueryParams}},{{/hasQueryParams}}{{/bodyParam}}
{{#hasQueryParams}},{{/hasQueryParams}}
@HeaderMap Map<String, Object> headerParams
{{/hasHeaderParams}}
);
{{#operations}}{{#operation}}
/**
* {{summary}}
* {{notes}}
{{#allParams}}
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/allParams}}
{{#returnType}}
* @return {{.}}
{{/returnType}}
{{#externalDocs}}
* {{description}}
* @see <a href="{{url}}">{{summary}} Documentation</a>
{{/externalDocs}}
{{#isDeprecated}}
* @deprecated
{{/isDeprecated}}
*/
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
@RequestLine("{{httpMethod}} {{{path}}}{{#hasQueryParams}}?{{/hasQueryParams}}{{#queryParams}}{{baseName}}={{=<% %>=}}{<%paramName%>}<%={{ }}=%>{{^-last}}&{{/-last}}{{/queryParams}}")
@Headers({
{{#vendorExtensions.x-content-type}} "Content-Type: {{{vendorExtensions.x-content-type}}}",
{{/vendorExtensions.x-content-type}} "Accept: {{#vendorExtensions.x-accepts}}{{{.}}}{{^-last}},{{/-last}}{{/vendorExtensions.x-accepts}}",{{#headerParams}}
"{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{^-last}},
{{/-last}}{{/headerParams}}
})
{{#returnType}}{{{.}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{nickname}}({{#allParams}}{{^isBodyParam}}{{^isFormParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isFormParam}}{{#isFormParam}}@Param("{{baseName}}") {{/isFormParam}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/allParams}}@HeaderMap Map<String, String> headers);
{{/operation}}
/**
* {{summary}}
* {{notes}}
* Convenience method with empty headers.
{{#allParams}}
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/allParams}}
{{#returnType}}
* @return {{.}}
{{/returnType}}
{{#isDeprecated}}
* @deprecated
{{/isDeprecated}}
*/
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
default {{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}}({{#allParams}}{{^isBodyParam}}{{^isFormParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isFormParam}}{{#isFormParam}}@Param("{{baseName}}") {{/isFormParam}}{{/isBodyParam}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) {
{{#returnType}}return {{/returnType}}{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}Map.of());
}
/**
* {{summary}}
* Similar to <code>{{operationId}}</code> but it also returns the http response headers .
* {{notes}}
{{#allParams}}
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/allParams}}
{{#returnType}}
* @return A ApiResponse that wraps the response boyd and the http headers.
{{/returnType}}
{{#externalDocs}}
* {{description}}
* @see <a href="{{url}}">{{summary}} Documentation</a>
{{/externalDocs}}
{{#isDeprecated}}
* @deprecated
{{/isDeprecated}}
*/
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
@RequestLine("{{httpMethod}} {{{path}}}{{#hasQueryParams}}?{{/hasQueryParams}}{{#queryParams}}{{baseName}}={{=<% %>=}}{<%paramName%>}<%={{ }}=%>{{^-last}}&{{/-last}}{{/queryParams}}")
@Headers({
{{#vendorExtensions.x-content-type}} "Content-Type: {{{vendorExtensions.x-content-type}}}",
{{/vendorExtensions.x-content-type}} "Accept: {{#vendorExtensions.x-accepts}}{{{.}}}{{^-last}},{{/-last}}{{/vendorExtensions.x-accepts}}",{{#headerParams}}
"{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{^-last}},
{{/-last}}{{/headerParams}}
})
ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{nickname}}WithHttpInfo({{#allParams}}{{^isBodyParam}}{{^isFormParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isFormParam}}{{#isFormParam}}@Param("{{baseName}}") {{/isFormParam}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/allParams}}@HeaderMap Map<String, String> headers);
{{#hasQueryParams}}
/**
* {{summary}}
* {{notes}}
* Note, this is equivalent to the other <code>{{operationId}}</code> method,
* but with the query parameters collected into a single Map parameter. This
* is convenient for services with optional query parameters, especially when
* used with the {@link {{operationIdCamelCase}}QueryParams} class that allows for
* building up this map in a fluent style.
{{#allParams}}
{{^isQueryParam}}
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/isQueryParam}}
{{/allParams}}
* @param queryParams Map of query parameters as name-value pairs
* <p>The following elements may be specified in the query map:</p>
* <ul>
{{#queryParams}}
* <li>{{paramName}} - {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}</li>
{{/queryParams}}
* </ul>
{{#returnType}}
* @return {{.}}
{{/returnType}}
{{#externalDocs}}
* {{description}}
* @see <a href="{{url}}">{{summary}} Documentation</a>
{{/externalDocs}}
{{#isDeprecated}}
* @deprecated
{{/isDeprecated}}
*/
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
@RequestLine("{{httpMethod}} {{{path}}}?{{#queryParams}}{{baseName}}={{=<% %>=}}{<%paramName%>}<%={{ }}=%>{{^-last}}&{{/-last}}{{/queryParams}}")
@Headers({
{{#vendorExtensions.x-content-type}} "Content-Type: {{{vendorExtensions.x-content-type}}}",
{{/vendorExtensions.x-content-type}} "Accept: {{#vendorExtensions.x-accepts}}{{{.}}}{{^-last}},{{/-last}}{{/vendorExtensions.x-accepts}}",{{#headerParams}}
"{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{^-last}},
{{/-last}}{{/headerParams}}
})
{{#returnType}}{{{.}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{nickname}}({{#allParams}}{{^isQueryParam}}{{^isBodyParam}}{{^isFormParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isFormParam}}{{#isFormParam}}@Param("{{baseName}}") {{/isFormParam}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}}{{/allParams}}@QueryMap(encoded=true) {{operationIdCamelCase}}QueryParams queryParams, @HeaderMap Map<String, String> headers);
/**
* {{summary}}
* {{notes}}
* Convenience method that uses typed QueryParams and empty headers.
{{#allParams}}
{{^isQueryParam}}
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/isQueryParam}}
{{/allParams}}
* @param queryParams Query parameters as typed object
{{#returnType}}
* @return {{.}}
{{/returnType}}
*/
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
default {{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}}({{#allParams}}{{^isQueryParam}}{{^isBodyParam}}{{^isFormParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isFormParam}}{{#isFormParam}}@Param("{{baseName}}") {{/isFormParam}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}}{{/allParams}}@QueryMap(encoded=true) {{operationIdCamelCase}}QueryParams queryParams) {
{{#returnType}}return {{/returnType}}{{nickname}}({{#allParams}}{{^isQueryParam}}{{paramName}}, {{/isQueryParam}}{{/allParams}}queryParams, Map.of());
}
/**
* {{summary}}
* {{notes}}
* Convenience method that accepts generic query parameters map.
{{#allParams}}
{{^isQueryParam}}
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/isQueryParam}}
{{/allParams}}
* @param queryParams Query parameters as generic map
* @param headers Custom headers to include in the request
{{#returnType}}
* @return {{.}}
{{/returnType}}
*/
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
@RequestLine("{{httpMethod}} {{{path}}}?{{#queryParams}}{{baseName}}={{=<% %>=}}{<%paramName%>}<%={{ }}=%>{{^-last}}&{{/-last}}{{/queryParams}}")
@Headers({
{{#vendorExtensions.x-content-type}} "Content-Type: {{{vendorExtensions.x-content-type}}}",
{{/vendorExtensions.x-content-type}} "Accept: {{#vendorExtensions.x-accepts}}{{{.}}}{{^-last}},{{/-last}}{{/vendorExtensions.x-accepts}}",{{#headerParams}}
"{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{^-last}},
{{/-last}}{{/headerParams}}
})
{{#returnType}}{{{.}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{nickname}}({{#allParams}}{{^isQueryParam}}{{^isBodyParam}}{{^isFormParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isFormParam}}{{#isFormParam}}@Param("{{baseName}}") {{/isFormParam}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}}{{/allParams}}@QueryMap(encoded=true) Map<String, Object> queryParams, @HeaderMap Map<String, String> headers);
/**
* {{summary}}
* {{notes}}
* Convenience method that accepts generic query parameters map with empty headers.
{{#allParams}}
{{^isQueryParam}}
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/isQueryParam}}
{{/allParams}}
* @param queryParams Query parameters as generic map
{{#returnType}}
* @return {{.}}
{{/returnType}}
*/
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
default {{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}}({{#allParams}}{{^isQueryParam}}{{^isBodyParam}}{{^isFormParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isFormParam}}{{#isFormParam}}@Param("{{baseName}}") {{/isFormParam}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}}{{/allParams}}@QueryMap(encoded=true) Map<String, Object> queryParams) {
{{#returnType}}return {{/returnType}}{{nickname}}({{#allParams}}{{^isQueryParam}}{{paramName}}, {{/isQueryParam}}{{/allParams}}queryParams, Map.of());
}
/**
* {{summary}}
* {{notes}}
* Note, this is equivalent to the other <code>{{operationId}}</code> that receives the query parameters as a map,
* but this one also exposes the Http response headers
{{#allParams}}
{{^isQueryParam}}
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/isQueryParam}}
{{/allParams}}
* @param queryParams Map of query parameters as name-value pairs
* <p>The following elements may be specified in the query map:</p>
* <ul>
{{#queryParams}}
* <li>{{paramName}} - {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}</li>
{{/queryParams}}
* </ul>
{{#returnType}}
* @return {{.}}
{{/returnType}}
{{#externalDocs}}
* {{description}}
* @see <a href="{{url}}">{{summary}} Documentation</a>
{{/externalDocs}}
{{#isDeprecated}}
* @deprecated
{{/isDeprecated}}
*/
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
@RequestLine("{{httpMethod}} {{{path}}}?{{#queryParams}}{{baseName}}={{=<% %>=}}{<%paramName%>}<%={{ }}=%>{{^-last}}&{{/-last}}{{/queryParams}}")
@Headers({
{{#vendorExtensions.x-content-type}} "Content-Type: {{{vendorExtensions.x-content-type}}}",
{{/vendorExtensions.x-content-type}} "Accept: {{#vendorExtensions.x-accepts}}{{{.}}}{{^-last}},{{/-last}}{{/vendorExtensions.x-accepts}}",{{#headerParams}}
"{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{^-last}},
{{/-last}}{{/headerParams}}
})
ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{nickname}}WithHttpInfo({{#allParams}}{{^isQueryParam}}{{^isBodyParam}}{{^isFormParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isFormParam}}{{#isFormParam}}@Param("{{baseName}}") {{/isFormParam}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}}{{/allParams}}@QueryMap(encoded=true) {{operationIdCamelCase}}QueryParams queryParams, @HeaderMap Map<String, String> headers);
/**
* A convenience class for generating query parameters for the
* <code>{{operationId}}</code> method in a fluent style.
*/
public static class {{operationIdCamelCase}}QueryParams extends HashMap<String, Object> {
{{#queryParams}}
public {{operationIdCamelCase}}QueryParams {{paramName}}(final {{{dataType}}} value) {
put("{{baseName}}", value);
return this;
}
{{/queryParams}}
}
{{/hasQueryParams}}
/**
* {{summary}}
* {{notes}}
* Universal method that accepts generic query parameters map with headers.
* Useful for passing undocumented or optional query parameters.
{{#allParams}}
{{^isQueryParam}}
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/isQueryParam}}
{{/allParams}}
* @param queryParams Query parameters as generic map (e.g., template=true, associations=all)
* @param headers Custom headers to include in the request
{{#returnType}}
* @return {{.}}
{{/returnType}}
*/
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
@RequestLine("{{httpMethod}} {{{path}}}")
@Headers({
{{#vendorExtensions.x-content-type}} "Content-Type: {{{vendorExtensions.x-content-type}}}",
{{/vendorExtensions.x-content-type}} "Accept: {{#vendorExtensions.x-accepts}}{{{.}}}{{^-last}},{{/-last}}{{/vendorExtensions.x-accepts}}",{{#headerParams}}
"{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{^-last}},
{{/-last}}{{/headerParams}}
})
{{#returnType}}{{{.}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{nickname}}Universal({{#allParams}}{{^isQueryParam}}{{^isBodyParam}}{{^isFormParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isFormParam}}{{#isFormParam}}@Param("{{baseName}}") {{/isFormParam}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}}{{/allParams}}@QueryMap(encoded=true) Map<String, Object> queryParams, @HeaderMap Map<String, String> headers);
/**
* {{summary}}
* {{notes}}
* Universal method that accepts generic query parameters map with empty headers.
{{#allParams}}
{{^isQueryParam}}
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/isQueryParam}}
{{/allParams}}
* @param queryParams Query parameters as generic map
{{#returnType}}
* @return {{.}}
{{/returnType}}
*/
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
default {{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}}Universal({{#allParams}}{{^isQueryParam}}{{^isBodyParam}}{{^isFormParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isFormParam}}{{#isFormParam}}@Param("{{baseName}}") {{/isFormParam}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}}{{/allParams}}@QueryMap(encoded=true) Map<String, Object> queryParams) {
{{#returnType}}return {{/returnType}}{{nickname}}Universal({{#allParams}}{{^isQueryParam}}{{paramName}}, {{/isQueryParam}}{{/allParams}}queryParams, Map.of());
}
{{/operation}}
{{/operations}}
}
+2
View File
@@ -59,6 +59,7 @@ tasks.named('compileTestJava') {
dependencies {
testImplementation(project(':fineract-avro-schemas'))
testImplementation(project(':fineract-client'))
testImplementation(project(':fineract-client-feign'))
testImplementation 'org.springframework:spring-context'
implementation 'org.springframework:spring-test'
@@ -67,6 +68,7 @@ dependencies {
testImplementation 'com.github.spotbugs:spotbugs-annotations'
testImplementation 'com.squareup.retrofit2:retrofit:2.11.0'
testImplementation 'io.github.openfeign:feign-core:13.6'
testImplementation 'org.apache.httpcomponents:httpclient:4.5.14'
testImplementation 'org.apache.commons:commons-lang3:3.18.0'
testImplementation ('com.googlecode.json-simple:json-simple:1.1.1') {
@@ -1,277 +0,0 @@
/**
* 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.api;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.services.BatchApiApi;
import org.apache.fineract.client.services.BusinessDateManagementApi;
import org.apache.fineract.client.services.BusinessStepConfigurationApi;
import org.apache.fineract.client.services.ChargesApi;
import org.apache.fineract.client.services.ClientApi;
import org.apache.fineract.client.services.CodeValuesApi;
import org.apache.fineract.client.services.CodesApi;
import org.apache.fineract.client.services.CurrencyApi;
import org.apache.fineract.client.services.DataTablesApi;
import org.apache.fineract.client.services.DefaultApi;
import org.apache.fineract.client.services.DelinquencyRangeAndBucketsManagementApi;
import org.apache.fineract.client.services.ExternalAssetOwnerLoanProductAttributesApi;
import org.apache.fineract.client.services.ExternalAssetOwnersApi;
import org.apache.fineract.client.services.ExternalEventConfigurationApi;
import org.apache.fineract.client.services.FundsApi;
import org.apache.fineract.client.services.GeneralLedgerAccountApi;
import org.apache.fineract.client.services.GlobalConfigurationApi;
import org.apache.fineract.client.services.InlineJobApi;
import org.apache.fineract.client.services.InternalCobApi;
import org.apache.fineract.client.services.JournalEntriesApi;
import org.apache.fineract.client.services.LoanAccountLockApi;
import org.apache.fineract.client.services.LoanBuyDownFeesApi;
import org.apache.fineract.client.services.LoanCapitalizedIncomeApi;
import org.apache.fineract.client.services.LoanChargesApi;
import org.apache.fineract.client.services.LoanCobCatchUpApi;
import org.apache.fineract.client.services.LoanDisbursementDetailsApi;
import org.apache.fineract.client.services.LoanInterestPauseApi;
import org.apache.fineract.client.services.LoanProductsApi;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.services.MappingFinancialActivitiesToAccountsApi;
import org.apache.fineract.client.services.PaymentTypeApi;
import org.apache.fineract.client.services.RescheduleLoansApi;
import org.apache.fineract.client.services.RolesApi;
import org.apache.fineract.client.services.SavingsAccountApi;
import org.apache.fineract.client.services.SavingsAccountTransactionsApi;
import org.apache.fineract.client.services.SavingsProductApi;
import org.apache.fineract.client.services.SchedulerApi;
import org.apache.fineract.client.services.SchedulerJobApi;
import org.apache.fineract.client.services.UsersApi;
import org.apache.fineract.client.util.FineractClient;
import org.apache.fineract.test.stepdef.loan.LoanProductsCustomApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@RequiredArgsConstructor
public class ApiConfiguration {
private final FineractClient fineractClient;
@Bean
public SchedulerApi schedulerApi() {
return fineractClient.createService(SchedulerApi.class);
}
@Bean
public SchedulerJobApi schedulerJobApi() {
return fineractClient.createService(SchedulerJobApi.class);
}
@Bean
public CurrencyApi currencyApi() {
return fineractClient.createService(CurrencyApi.class);
}
@Bean
public DataTablesApi dataTablesApi() {
return fineractClient.createService(DataTablesApi.class);
}
@Bean
public ChargesApi chargesApi() {
return fineractClient.createService(ChargesApi.class);
}
@Bean
public GeneralLedgerAccountApi generalLedgerAccountApi() {
return fineractClient.createService(GeneralLedgerAccountApi.class);
}
@Bean
public LoanProductsApi loanProductsApi() {
return fineractClient.createService(LoanProductsApi.class);
}
@Bean
public LoanProductsCustomApi loanProductsCustomApi() {
return fineractClient.createService(LoanProductsCustomApi.class);
}
@Bean
public SavingsProductApi savingsProductApi() {
return fineractClient.createService(SavingsProductApi.class);
}
@Bean
public SavingsAccountTransactionsApi savingsAccountTransactionsApi() {
return fineractClient.createService(SavingsAccountTransactionsApi.class);
}
@Bean
public SavingsAccountApi savingsAccountApi() {
return fineractClient.createService(SavingsAccountApi.class);
}
@Bean
public CodesApi codesApi() {
return fineractClient.createService(CodesApi.class);
}
@Bean
public CodeValuesApi codeValuesApi() {
return fineractClient.createService(CodeValuesApi.class);
}
@Bean
public DelinquencyRangeAndBucketsManagementApi delinquencyRangeAndBucketsManagementApi() {
return fineractClient.createService(DelinquencyRangeAndBucketsManagementApi.class);
}
@Bean
public FundsApi fundsApi() {
return fineractClient.createService(FundsApi.class);
}
@Bean
public GlobalConfigurationApi globalConfigurationApi() {
return fineractClient.createService(GlobalConfigurationApi.class);
}
@Bean
public PaymentTypeApi paymentTypeApi() {
return fineractClient.createService(PaymentTypeApi.class);
}
@Bean
public BusinessDateManagementApi businessDateManagementApi() {
return fineractClient.createService(BusinessDateManagementApi.class);
}
@Bean
public ClientApi clientApi() {
return fineractClient.createService(ClientApi.class);
}
@Bean
public BatchApiApi batchApiApi() {
return fineractClient.createService(BatchApiApi.class);
}
@Bean
public LoansApi loansApi() {
return fineractClient.createService(LoansApi.class);
}
@Bean
public JournalEntriesApi journalEntriesApi() {
return fineractClient.createService(JournalEntriesApi.class);
}
@Bean
public InlineJobApi inlineJobApi() {
return fineractClient.createService(InlineJobApi.class);
}
@Bean
public LoanTransactionsApi loanTransactionsApi() {
return fineractClient.createService(LoanTransactionsApi.class);
}
@Bean
public LoanChargesApi loanChargesApi() {
return fineractClient.createService(LoanChargesApi.class);
}
@Bean
public ExternalEventConfigurationApi externalEventConfigurationApi() {
return fineractClient.createService(ExternalEventConfigurationApi.class);
}
@Bean
public LoanCobCatchUpApi loanCobCatchUpApi() {
return fineractClient.createService(LoanCobCatchUpApi.class);
}
@Bean
public RolesApi rolesApi() {
return fineractClient.createService(RolesApi.class);
}
@Bean
public UsersApi usersApi() {
return fineractClient.createService(UsersApi.class);
}
@Bean
public ExternalAssetOwnersApi externalAssetOwnersApi() {
return fineractClient.createService(ExternalAssetOwnersApi.class);
}
@Bean
public ExternalAssetOwnerLoanProductAttributesApi externalAssetOwnerLoanProductAttributesApi() {
return fineractClient.createService(ExternalAssetOwnerLoanProductAttributesApi.class);
}
@Bean
public BusinessStepConfigurationApi businessStepConfigurationApi() {
return fineractClient.createService(BusinessStepConfigurationApi.class);
}
@Bean
public MappingFinancialActivitiesToAccountsApi mappingFinancialActivitiesToAccountsApi() {
return fineractClient.createService(MappingFinancialActivitiesToAccountsApi.class);
}
@Bean
public LoanAccountLockApi loanAccountLockApi() {
return fineractClient.createService(LoanAccountLockApi.class);
}
@Bean
public DefaultApi defaultApi() {
return fineractClient.createService(DefaultApi.class);
}
@Bean
public RescheduleLoansApi rescheduleLoansApi() {
return fineractClient.createService(RescheduleLoansApi.class);
}
@Bean
public LoanInterestPauseApi loanInterestPauseApi() {
return fineractClient.createService(LoanInterestPauseApi.class);
}
@Bean
public LoanDisbursementDetailsApi loanDisbursementDetailsApi() {
return fineractClient.createService(LoanDisbursementDetailsApi.class);
}
@Bean
public LoanBuyDownFeesApi loanBuyDownFeesApi() {
return fineractClient.createService(LoanBuyDownFeesApi.class);
}
@Bean
public LoanCapitalizedIncomeApi loanCapitalizedIncomeApi() {
return fineractClient.createService(LoanCapitalizedIncomeApi.class);
}
@Bean
public InternalCobApi internalCobApi() {
return fineractClient.createService(InternalCobApi.class);
}
}
@@ -18,10 +18,10 @@
*/
package org.apache.fineract.test.api;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.util.FineractClient;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -33,15 +33,18 @@ public class FineractClientConfiguration {
private final ApiProperties apiProperties;
@Bean
public FineractClient fineractClient() {
public FineractFeignClient fineractFeignClient() {
String baseUrl = apiProperties.getBaseUrl();
String username = apiProperties.getUsername();
String password = apiProperties.getPassword();
String tenantId = apiProperties.getTenantId();
long readTimeout = apiProperties.getReadTimeout();
String apiBaseUrl = baseUrl + "/fineract-provider/api/";
log.info("Using base URL '{}'", apiBaseUrl);
return FineractClient.builder().readTimeout(Duration.ofSeconds(readTimeout)).basicAuth(username, password).tenant(tenantId)
.baseURL(apiBaseUrl).insecure(true).build();
boolean debugEnabled = Boolean.parseBoolean(System.getProperty("fineract.feign.debug", "false"));
FineractFeignClient client = FineractFeignClient.builder().baseUrl(apiBaseUrl).credentials(username, password).tenantId(tenantId)
.disableSslVerification(true).debug(debugEnabled).connectTimeout(60, TimeUnit.SECONDS)
.readTimeout((int) readTimeout, TimeUnit.SECONDS).build();
return client;
}
}
@@ -18,41 +18,35 @@
*/
package org.apache.fineract.test.data.accounttype;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetGLAccountsResponse;
import org.apache.fineract.client.services.GeneralLedgerAccountApi;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component
@RequiredArgsConstructor
@Slf4j
public class AccountTypeResolver {
private final GeneralLedgerAccountApi glaApi;
private final FineractFeignClient fineractClient;
@Cacheable(key = "#accountType.getName()", value = "accountTypesByName")
public long resolve(AccountType accountType) {
try {
String accountTypeName = accountType.getName();
log.debug("Resolving account type by name [{}]", accountTypeName);
Response<List<GetGLAccountsResponse>> response = glaApi.retrieveAllAccounts(null, "", 1, true, false, false).execute();
if (!response.isSuccessful()) {
throw new IllegalStateException("Unable to get account types. Status code was HTTP " + response.code());
}
List<GetGLAccountsResponse> accountTypeResponses = response.body();
GetGLAccountsResponse foundAtr = accountTypeResponses.stream()//
.filter(atr -> accountTypeName.equals(atr.getName()))//
.findAny()//
.orElseThrow(() -> new IllegalArgumentException("Account type [%s] not found".formatted(accountTypeName)));//
String accountTypeName = accountType.getName();
log.debug("Resolving account type by name [{}]", accountTypeName);
List<GetGLAccountsResponse> accountTypeResponses = ok(() -> fineractClient.generalLedgerAccount().retrieveAllAccountsUniversal(
Map.of("usage", 1, "manualEntriesAllowed", true, "disabled", false, "fetchRunningBalance", false)));
GetGLAccountsResponse foundAtr = accountTypeResponses.stream()//
.filter(atr -> accountTypeName.equals(atr.getName()))//
.findAny()//
.orElseThrow(() -> new IllegalArgumentException("Account type [%s] not found".formatted(accountTypeName)));//
return foundAtr.getId();
} catch (IOException e) {
throw new RuntimeException(e);
}
return foundAtr.getId();
}
}
@@ -18,41 +18,33 @@
*/
package org.apache.fineract.test.data.codevalue;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetCodeValuesDataResponse;
import org.apache.fineract.client.services.CodeValuesApi;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component
@RequiredArgsConstructor
@Slf4j
public class CodeValueResolver {
private final CodeValuesApi codeValuesApi;
private final FineractFeignClient fineractClient;
@Cacheable(key = "#codeValue.getName()", value = "codeValuesByName")
public long resolve(Long codeId, CodeValue codeValue) {
try {
String codeValueName = codeValue.getName();
String codeValueName = codeValue.getName();
log.debug("Resolving code value by code id and name [{}]", codeValue);
Response<List<GetCodeValuesDataResponse>> response = codeValuesApi.retrieveAllCodeValues(codeId).execute();
if (!response.isSuccessful()) {
throw new IllegalStateException("Unable to get payment types. Status code was HTTP " + response.code());
}
log.debug("Resolving code value by code id and name [{}]", codeValue);
List<GetCodeValuesDataResponse> codeValuesResponses = ok(() -> fineractClient.codeValues().retrieveAllCodeValues(codeId, Map.of()));
GetCodeValuesDataResponse foundPtr = codeValuesResponses.stream().filter(ptr -> codeValueName.equals(ptr.getName())).findAny()
.orElseThrow(() -> new IllegalArgumentException("Payment type [%s] not found".formatted(codeValueName)));
List<GetCodeValuesDataResponse> codeValuesResponses = response.body();
GetCodeValuesDataResponse foundPtr = codeValuesResponses.stream().filter(ptr -> codeValueName.equals(ptr.getName())).findAny()
.orElseThrow(() -> new IllegalArgumentException("Payment type [%s] not found".formatted(codeValueName)));
return foundPtr.getId();
} catch (IOException e) {
throw new RuntimeException(e);
}
return foundPtr.getId();
}
}
@@ -18,6 +18,7 @@
*/
package org.apache.fineract.test.data.datatable;
import java.util.Locale;
import org.apache.fineract.test.helper.Utils;
import org.springframework.stereotype.Component;
@@ -25,6 +26,6 @@ import org.springframework.stereotype.Component;
public class DatatableNameGenerator {
public String generate(DatatableEntityType entityType) {
return Utils.randomStringGenerator("dt_%s_".formatted(entityType.getReferencedTableName()), 5).toLowerCase();
return Utils.randomStringGenerator("dt_%s_".formatted(entityType.getReferencedTableName()), 5).toLowerCase(Locale.ROOT);
}
}
@@ -18,34 +18,28 @@
*/
package org.apache.fineract.test.data.job;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetJobsResponse;
import org.apache.fineract.client.services.SchedulerJobApi;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component
@RequiredArgsConstructor
@Slf4j
public class JobResolver {
private final SchedulerJobApi schedulerJobApi;
private final FineractFeignClient fineractClient;
@Cacheable(key = "#job.getShortName()", value = "jobsByShortName")
public long resolve(Job job) {
try {
String shortName = job.getShortName();
log.debug("Resolving job by short-name [{}]", shortName);
Response<GetJobsResponse> response = schedulerJobApi.retrieveByShortName(shortName).execute();
if (!response.isSuccessful()) {
throw new IllegalStateException("Unable to get job. Status code was HTTP " + response.code());
}
return response.body().getJobId();
} catch (IOException e) {
throw new RuntimeException(e);
}
String shortName = job.getShortName();
log.debug("Resolving job by short-name [{}]", shortName);
GetJobsResponse response = ok(() -> fineractClient.schedulerJob().retrieveByShortName(shortName, Map.of()));
return response.getJobId();
}
}
@@ -18,39 +18,33 @@
*/
package org.apache.fineract.test.data.loanproduct;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetLoanProductsResponse;
import org.apache.fineract.client.services.LoanProductsApi;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component
@RequiredArgsConstructor
@Slf4j
public class LoanProductResolver {
private final LoanProductsApi loanProductsApi;
private final FineractFeignClient fineractClient;
@Cacheable(key = "#loanProduct.getName()", value = "loanProductsByName")
public long resolve(LoanProduct loanProduct) {
try {
String loanProductName = loanProduct.getName();
log.debug("Resolving loan product by name [{}]", loanProductName);
Response<List<GetLoanProductsResponse>> response = loanProductsApi.retrieveAllLoanProducts().execute();
if (!response.isSuccessful()) {
throw new IllegalStateException("Unable to get loan products. Status code was HTTP " + response.code());
}
String loanProductName = loanProduct.getName();
log.debug("Resolving loan product by name [{}]", loanProductName);
List<GetLoanProductsResponse> loanProductsResponses = ok(() -> fineractClient.loanProducts().retrieveAllLoanProducts(Map.of()));
List<GetLoanProductsResponse> loanProductsResponses = response.body();
GetLoanProductsResponse foundLpr = loanProductsResponses.stream().filter(lpr -> loanProductName.equals(lpr.getName())).findAny()
.orElseThrow(() -> new IllegalArgumentException("Loan product [%s] not found".formatted(loanProductName)));
return foundLpr.getId();
} catch (IOException e) {
throw new RuntimeException(e);
}
log.info("Retrieved {} loan products from API", loanProductsResponses.size());
log.info("Available loan products: {}", loanProductsResponses.stream().map(GetLoanProductsResponse::getName).toList());
GetLoanProductsResponse foundLpr = loanProductsResponses.stream().filter(lpr -> loanProductName.equals(lpr.getName())).findAny()
.orElseThrow(() -> new IllegalArgumentException("Loan product [%s] not found".formatted(loanProductName)));
return foundLpr.getId();
}
}
@@ -18,40 +18,33 @@
*/
package org.apache.fineract.test.data.paymenttype;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.PaymentTypeData;
import org.apache.fineract.client.services.PaymentTypeApi;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component
@RequiredArgsConstructor
@Slf4j
public class PaymentTypeResolver {
private final PaymentTypeApi paymentTypeApi;
private final FineractFeignClient fineractClient;
@Cacheable(key = "#paymentType.getName()", value = "paymentTypesByName")
public long resolve(PaymentType paymentType) {
try {
String paymentTypeName = paymentType.getName();
log.debug("Resolving payment type by name [{}]", paymentTypeName);
Response<List<PaymentTypeData>> response = paymentTypeApi.getAllPaymentTypes(false).execute();
if (!response.isSuccessful()) {
throw new IllegalStateException("Unable to get payment types. Status code was HTTP " + response.code());
}
String paymentTypeName = paymentType.getName();
log.debug("Resolving payment type by name [{}]", paymentTypeName);
List<PaymentTypeData> paymentTypesResponses = ok(() -> fineractClient.paymentType().getAllPaymentTypesUniversal(Map.of()));
List<PaymentTypeData> paymentTypesResponses = response.body();
PaymentTypeData foundPtr = paymentTypesResponses.stream().filter(ptr -> paymentTypeName.equals(ptr.getName())).findAny()
.orElseThrow(() -> new IllegalArgumentException("Payment type [%s] not found".formatted(paymentTypeName)));
PaymentTypeData foundPtr = paymentTypesResponses.stream().filter(ptr -> paymentTypeName.equals(ptr.getName())).findAny()
.orElseThrow(() -> new IllegalArgumentException("Payment type [%s] not found".formatted(paymentTypeName)));
return foundPtr.getId();
} catch (IOException e) {
throw new RuntimeException(e);
}
return foundPtr.getId();
}
}
@@ -18,17 +18,21 @@
*/
package org.apache.fineract.test.helper;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.BusinessDateResponse;
import org.apache.fineract.client.models.BusinessDateUpdateRequest;
import org.apache.fineract.client.models.BusinessDateUpdateResponse;
import org.apache.fineract.client.services.BusinessDateManagementApi;
import org.apache.fineract.test.support.TestContext;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Slf4j
@RequiredArgsConstructor
@Component
public class BusinessDateHelper {
@@ -38,18 +42,23 @@ public class BusinessDateHelper {
public static final String BUSINESS_DATE = "BUSINESS_DATE";
public static final String COB = "COB_DATE";
private final BusinessDateManagementApi businessDateManagementApi;
private final FineractFeignClient fineractClient;
public void setBusinessDate(String businessDate) throws IOException {
public void setBusinessDate(String businessDate) {
BusinessDateUpdateRequest businessDateRequest = defaultBusinessDateRequest().date(businessDate);
try {
BusinessDateUpdateResponse response = ok(
() -> fineractClient.businessDateManagement().updateBusinessDate(null, businessDateRequest, Map.of()));
TestContext.INSTANCE.set(TestContextKey.BUSINESS_DATE_RESPONSE, response);
ok(() -> fineractClient.businessDateManagement().getBusinessDate(BUSINESS_DATE, Map.of()));
Response<BusinessDateUpdateResponse> businessDateRequestResponse = businessDateManagementApi
.updateBusinessDate(null, businessDateRequest).execute();
ErrorHelper.checkSuccessfulApiCall(businessDateRequestResponse);
TestContext.INSTANCE.set(TestContextKey.BUSINESS_DATE_RESPONSE, businessDateRequestResponse);
} catch (Exception e) {
log.error("Error: {}", e.getMessage());
throw e;
}
}
public void setBusinessDateToday() throws IOException {
public void setBusinessDateToday() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String today = formatter.format(Utils.now());
setBusinessDate(today);
@@ -60,7 +69,9 @@ public class BusinessDateHelper {
.locale(DEFAULT_LOCALE);
}
public String getBusinessDate() throws IOException {
return businessDateManagementApi.getBusinessDate(BUSINESS_DATE).toString();
public String getBusinessDate() {
log.debug("Getting business date (using Feign)");
BusinessDateResponse response = ok(() -> fineractClient.businessDateManagement().getBusinessDate(BUSINESS_DATE, Map.of()));
return response.toString();
}
}
@@ -18,16 +18,15 @@
*/
package org.apache.fineract.test.helper;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetCodesResponse;
import org.apache.fineract.client.models.PostCodeValueDataResponse;
import org.apache.fineract.client.models.PostCodeValuesDataRequest;
import org.apache.fineract.client.services.CodeValuesApi;
import org.apache.fineract.client.services.CodesApi;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component
@RequiredArgsConstructor
@@ -37,31 +36,30 @@ public class CodeHelper {
private static final String STATE_CODE_NAME = "STATE";
private static final String ADDRESS_TYPE_CODE_NAME = "ADDRESS_TYPE";
private final CodesApi codesApi;
private final CodeValuesApi codeValuesApi;
private final FineractFeignClient fineractClient;
public Response<PostCodeValueDataResponse> createAddressTypeCodeValue(String addressTypeName) throws IOException {
public PostCodeValueDataResponse createAddressTypeCodeValue(String addressTypeName) {
Long codeId = retrieveCodeByName(ADDRESS_TYPE_CODE_NAME).getId();
return codeValuesApi.createCodeValue(codeId, new PostCodeValuesDataRequest().name(addressTypeName)).execute();
return ok(
() -> fineractClient.codeValues().createCodeValue(codeId, new PostCodeValuesDataRequest().name(addressTypeName), Map.of()));
}
public Response<PostCodeValueDataResponse> createCountryCodeValue(String countryName) throws IOException {
public PostCodeValueDataResponse createCountryCodeValue(String countryName) {
Long codeId = retrieveCodeByName(COUNTRY_CODE_NAME).getId();
return codeValuesApi.createCodeValue(codeId, new PostCodeValuesDataRequest().name(countryName)).execute();
return ok(() -> fineractClient.codeValues().createCodeValue(codeId, new PostCodeValuesDataRequest().name(countryName), Map.of()));
}
public Response<PostCodeValueDataResponse> createStateCodeValue(String stateName) throws IOException {
public PostCodeValueDataResponse createStateCodeValue(String stateName) {
Long codeId = retrieveCodeByName(STATE_CODE_NAME).getId();
return codeValuesApi.createCodeValue(codeId, new PostCodeValuesDataRequest().name(stateName)).execute();
return ok(() -> fineractClient.codeValues().createCodeValue(codeId, new PostCodeValuesDataRequest().name(stateName), Map.of()));
}
@SneakyThrows
public GetCodesResponse retrieveCodeByName(String name) {
return codesApi.retrieveCodes().execute().body().stream().filter(r -> name.equals(r.getName())).findAny()
return ok(() -> fineractClient.codes().retrieveCodes(Map.of())).stream().filter(r -> name.equals(r.getName())).findAny()
.orElseThrow(() -> new IllegalArgumentException("Code with name " + name + " has not been found"));
}
public Response<PostCodeValueDataResponse> createCodeValue(Long codeId, PostCodeValuesDataRequest request) throws IOException {
return codeValuesApi.createCodeValue(codeId, request).execute();
public PostCodeValueDataResponse createCodeValue(Long codeId, PostCodeValuesDataRequest request) {
return ok(() -> fineractClient.codeValues().createCodeValue(codeId, request, Map.of()));
}
}
@@ -1,53 +0,0 @@
/**
* 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.helper;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.util.List;
import org.apache.fineract.client.models.BatchResponse;
import retrofit2.Response;
public final class ErrorHelper {
private ErrorHelper() {}
public static void checkSuccessfulApiCall(Response response) throws IOException {
assertThat(response.isSuccessful()).as(ErrorMessageHelper.requestFailed(response)).isTrue();
if (response.code() != 200 && response.code() != 202 && response.code() != 204) {
throw new AssertionError(ErrorMessageHelper.requestFailedWithCode(response));
}
}
public static void checkFailedApiCall(Response response, int requiredCode) throws IOException {
assertThat(!response.isSuccessful()).as(ErrorMessageHelper.requestFailed(response)).isTrue();
if (response.code() != requiredCode) {
throw new AssertionError("Request success but should fail with code: " + requiredCode);
}
}
public static void checkSuccessfulBatchApiCall(Response<List<BatchResponse>> batchResponseList) {
batchResponseList.body().forEach(response -> {
assertThat(response.getStatusCode()).as(ErrorMessageHelper.batchRequestFailedWithCode(response)).isEqualTo(200);
});
}
}
@@ -23,12 +23,10 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.client.models.BatchResponse;
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
import org.apache.fineract.client.models.Header;
import org.apache.fineract.client.models.LoanAccountLockResponseDTO;
import retrofit2.Response;
@@ -134,10 +132,6 @@ public final class ErrorMessageHelper {
}
public static String transactionDateInFutureFailureMsg() {
return "The transaction date cannot be in the future.";
}
public static String repaymentUndoFailureDueToChargeOff(Long loanId) {
String loanIdStr = String.valueOf(loanId);
return String.format("Loan transaction: %s adjustment is not allowed before or on the date when the loan got charged-off",
@@ -286,11 +280,6 @@ public final class ErrorMessageHelper {
expected);
}
public static String wrongDataInTransactionsTransactionDate(String actual, String expected) {
return String.format("Wrong data in Transactions / Transaction date. Actual value is: %s - But expected value is: %s", actual,
expected);
}
public static String transactionIsNotReversedError(Boolean actual, Boolean expected) {
return String.format("The transaction should be reversed, but it is not. Actual value is: %s - But expected value is: %s", actual,
expected);
@@ -303,34 +292,6 @@ public final class ErrorMessageHelper {
expectedToStr);
}
public static String wrongAmountInTransactionsPrincipal(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
return String.format("Wrong amount in Transactions / Principal. Actual amount is: %s - But expected amount is: %s", actualToStr,
expectedToStr);
}
public static String wrongAmountInTransactionsInterest(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
return String.format("Wrong amount in Transactions / Interest. Actual amount is: %s - But expected amount is: %s", actualToStr,
expectedToStr);
}
public static String wrongAmountInTransactionsFees(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
return String.format("Wrong amount in Transactions / Fees. Actual amount is: %s - But expected amount is: %s", actualToStr,
expectedToStr);
}
public static String wrongAmountInTransactionsPenalties(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
return String.format("Wrong amount in Transactions / Penalties. Actual amount is: %s - But expected amount is: %s", actualToStr,
expectedToStr);
}
public static String wrongAmountInTransactionsOverpayment(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
@@ -338,57 +299,10 @@ public final class ErrorMessageHelper {
expectedToStr);
}
public static String wrongAmountInTransactionsBalance(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
return String.format("Wrong amount in Transactions / Loan Balance. Actual amount is: %s - But expected amount is: %s", actualToStr,
expectedToStr);
}
public static String transactionHasNullResourceValue(String transactionType, String resourceName) {
return String.format("The transaction %s should has non-null value for %s, but it is null.", transactionType, resourceName);
}
public static String wrongDataInChargesName(String actual, String expected) {
return String.format("Wrong data in Charges / Name. Actual value is: %s - But expected value is: %s", actual, expected);
}
public static String wrongDataInChargesIsPenalty(String actual, String expected) {
return String.format("Wrong data in Charges / isPenalty. Actual value is: %s - But expected value is: %s", actual, expected);
}
public static String wrongDataInChargesDueDate(String actual, String expected) {
return String.format("Wrong data in Charges / Due Date. Actual value is: %s - But expected value is: %s", actual, expected);
}
public static String wrongDataInChargesAmountDue(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
return String.format("Wrong amount in Charges / Due amount. Actual amount is: %s - But expected amount is: %s", actualToStr,
expectedToStr);
}
public static String wrongDataInChargesAmountPaid(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
return String.format("Wrong amount in Charges / Paid amount. Actual amount is: %s - But expected amount is: %s", actualToStr,
expectedToStr);
}
public static String wrongDataInChargesAmountWaived(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
return String.format("Wrong amount in Charges / Waived amount. Actual amount is: %s - But expected amount is: %s", actualToStr,
expectedToStr);
}
public static String wrongDataInChargesAmountOutstanding(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
return String.format("Wrong amount in Charges / Outstanding amount. Actual amount is: %s - But expected amount is: %s", actualToStr,
expectedToStr);
}
public static String wrongAmountInTotalOutstanding(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
@@ -464,20 +378,6 @@ public final class ErrorMessageHelper {
return "Loan Repayment (or its types) or Waiver is not allowed. Loan Account is not active.";
}
public static String noTransactionMetCriteria(String transactionType, String date) {
return String.format(
"There are no transaction in Transactions met the following criteria: Transaction type = %s, Transaction date = %s",
transactionType, date);
}
public static String missingMatchInJournalEntries(Map<String, String> entryPairs,
List<GetJournalEntriesTransactionIdResponse> entryDataList) {
String entryPairsStr = entryPairs.toString();
String entryDataListStr = entryDataList.toString();
return String.format("One or more entry pairs missing from Journal entries. Expected entry pairs: %s. Actual Journal entries: %s",
entryPairsStr, entryDataListStr);
}
public static String wrongErrorCodeInFailedChargeAdjustment(Integer actual, Integer expected) {
return String.format("Not the expected error code in error body: Actual error message is: %s. Expected error code is: %s",
actual.toString(), expected.toString());
@@ -596,31 +496,6 @@ public final class ErrorMessageHelper {
resourceId, line, actual, expected);
}
public static String wrongDataInJournalEntriesGlAccountType(int line, String actual, String expected) {
return String.format("Wrong data in Journal entries, line %s / GL account type. " //
+ "Actual value is: %s - But expected value is: %s", line, actual, expected);
}
public static String wrongDataInJournalEntriesGlAccountCode(int line, String actual, String expected) {
return String.format("Wrong data in Journal entries, line %s / GL account code. Actual value is: %s - But expected value is: %s",
line, actual, expected);
}
public static String wrongDataInJournalEntriesGlAccountName(int line, String actual, String expected) {
return String.format("Wrong data in Journal entries, line %s / GL account name. Actual value is: %s - But expected value is: %s",
line, actual, expected);
}
public static String wrongDataInJournalEntriesDebit(int line, String actual, String expected) {
return String.format("Wrong data in Journal entries, line %s / Debit. Actual value is: %s - But expected value is: %s", line,
actual, expected);
}
public static String wrongDataInJournalEntriesCredit(int line, String actual, String expected) {
return String.format("Wrong data in Journal entries, line %s / Credit. Actual value is: %s - But expected value is: %s", line,
actual, expected);
}
public static String wrongDataInActualMaturityDate(String actual, String expected) {
return String.format("Wrong data in Loan details/Timeline/actualMaturityDate. Actual value is: %s - But expected value is: %s",
actual, expected);
@@ -707,10 +582,6 @@ public final class ErrorMessageHelper {
return String.format("No attribute %s for loan product %s is found!", attributeKey, loanProduct);
}
public static String wrongData(String actual, String expected) {
return String.format("Wrong data. Actual value is: %s - But expected value is: %s", actual, expected);
}
public static String wrongValueInExternalAssetDetails(int line, List<List<String>> actual, List<String> expected) {
String lineStr = String.valueOf(line);
String expectedStr = expected.toString();
@@ -774,13 +645,13 @@ public final class ErrorMessageHelper {
expectedStr);
}
public static String listOfLockedLoansNotEmpty(Response<LoanAccountLockResponseDTO> response) {
String bodyStr = response.body().toString();
public static String listOfLockedLoansNotEmpty(LoanAccountLockResponseDTO response) {
String bodyStr = response.toString();
return String.format("List of locked loan accounts is not empty. Actual response is: %n%s", bodyStr);
}
public static String listOfLockedLoansContainsLoan(Long loanId, Response<LoanAccountLockResponseDTO> response) {
String bodyStr = response.body().toString();
public static String listOfLockedLoansContainsLoan(Long loanId, LoanAccountLockResponseDTO response) {
String bodyStr = response.toString();
return String.format("List of locked loan accounts contains the loan with loanId %s. List of locked loans: %n%s", loanId, bodyStr);
}
@@ -1081,6 +952,13 @@ public final class ErrorMessageHelper {
actual, expected);
}
public static String wrongAmountInDeferredCapitalizedIncome(BigDecimal actual, BigDecimal expected) {
String actualToStr = actual == null ? "null" : actual.toString();
String expectedToStr = expected == null ? "null" : expected.toString();
return String.format("Wrong amount in Deferred Capitalized Income. Actual amount is: %s - But expected amount is: %s", actualToStr,
expectedToStr);
}
public static String reAgeChargedOffLoanFailure() {
return "Loan re-aging is not allowed on charged-off loan.";
}
@@ -18,13 +18,14 @@
*/
package org.apache.fineract.test.helper;
import com.google.gson.Gson;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.client.feign.ObjectMapperFactory;
import retrofit2.Response;
@NoArgsConstructor
@@ -32,36 +33,88 @@ import retrofit2.Response;
@Setter
public class ErrorResponse {
private static final Gson GSON = new JSON().getGson();
private static final ObjectMapper OBJECT_MAPPER = ObjectMapperFactory.getShared();
private String developerMessage;
private Integer httpStatusCode;
private List<Error> errors;
private List<ErrorDetail> errors;
public ErrorDetail getSingleError() {
if (hasTopLevelErrorOnly()) {
return createErrorFromDeveloperMessage();
}
if (errors == null || errors.isEmpty()) {
if (this.developerMessage != null) {
return createErrorFromDeveloperMessage();
}
throw new IllegalStateException("No errors found in response");
}
public Error getSingleError() {
if (errors.size() != 1) {
throw new IllegalStateException("Multiple errors found");
} else {
return errors.iterator().next();
}
return errors.iterator().next();
}
private boolean hasTopLevelErrorOnly() {
return this.httpStatusCode != null && this.httpStatusCode == 400 && this.developerMessage != null
&& this.developerMessage.contains("invalid") && (this.errors == null || this.errors.isEmpty());
}
private ErrorDetail createErrorFromDeveloperMessage() {
ErrorDetail error = new ErrorDetail();
error.setDeveloperMessage(this.developerMessage);
return error;
}
public static ErrorResponse from(Response retrofitResponse) {
try {
String errorBody = retrofitResponse.errorBody().string();
return GSON.fromJson(errorBody, ErrorResponse.class);
return OBJECT_MAPPER.readValue(errorBody, ErrorResponse.class);
} catch (IOException e) {
throw new RuntimeException("Error while parsing the error body", e);
}
}
public static ErrorResponse fromFeignException(feign.FeignException feignException) {
try {
String errorBody = feignException.contentUTF8();
return OBJECT_MAPPER.readValue(errorBody, ErrorResponse.class);
} catch (JsonProcessingException e) {
throw new RuntimeException("Error while parsing the error body", e);
}
}
public static ErrorResponse fromFeignException(org.apache.fineract.client.feign.FeignException feignException) {
try {
String errorBody = feignException.responseBodyAsString();
ErrorResponse errorResponse = OBJECT_MAPPER.readValue(errorBody, ErrorResponse.class);
errorResponse.setHttpStatusCode(feignException.status());
return errorResponse;
} catch (JsonProcessingException e) {
throw new RuntimeException("Error while parsing the error body", e);
}
}
@NoArgsConstructor
@Getter
@Setter
public static class Error {
public static class ErrorDetail {
private String developerMessage;
private List<ErrorMessageArg> args;
public String getDeveloperMessageWithoutPrefix() {
if (developerMessage == null) {
return null;
}
if (developerMessage.startsWith("[") && developerMessage.contains("] ")) {
return developerMessage.substring(developerMessage.indexOf("] ") + 2);
}
return developerMessage;
}
}
@NoArgsConstructor
@@ -18,66 +18,60 @@
*/
package org.apache.fineract.test.helper;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GlobalConfigurationPropertyData;
import org.apache.fineract.client.models.PutGlobalConfigurationsRequest;
import org.apache.fineract.client.models.PutGlobalConfigurationsResponse;
import org.apache.fineract.client.services.GlobalConfigurationApi;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component
@RequiredArgsConstructor
public class GlobalConfigurationHelper {
private final GlobalConfigurationApi globalConfigurationApi;
private final FineractFeignClient fineractClient;
public void disableGlobalConfiguration(String configKey, Long value) throws IOException {
public void disableGlobalConfiguration(String configKey, Long value) {
switchAndSetGlobalConfiguration(configKey, false, value);
}
public void enableGlobalConfiguration(String configKey, Long value) throws IOException {
public void enableGlobalConfiguration(String configKey, Long value) {
switchAndSetGlobalConfiguration(configKey, true, value);
}
private void switchAndSetGlobalConfiguration(String configKey, boolean enabled, Long value) throws IOException {
Response<GlobalConfigurationPropertyData> configuration = globalConfigurationApi.retrieveOneByName(configKey).execute();
ErrorHelper.checkSuccessfulApiCall(configuration);
Long configId = configuration.body().getId();
private void switchAndSetGlobalConfiguration(String configKey, boolean enabled, Long value) {
GlobalConfigurationPropertyData configuration = ok(
() -> fineractClient.globalConfiguration().retrieveOneByName(configKey, Map.of()));
Long configId = configuration.getId();
PutGlobalConfigurationsRequest updateRequest = new PutGlobalConfigurationsRequest().enabled(enabled).value(value);
Response<PutGlobalConfigurationsResponse> updateResponse = globalConfigurationApi.updateConfiguration1(configId, updateRequest)
.execute();
assertThat(updateResponse.code()).isEqualTo(HttpStatus.SC_OK);
Response<GlobalConfigurationPropertyData> updatedConfiguration = globalConfigurationApi.retrieveOneByName(configKey).execute();
boolean isEnabled = BooleanUtils.toBoolean(updatedConfiguration.body().getEnabled());
ok(() -> fineractClient.globalConfiguration().updateConfiguration1(configId, updateRequest, Map.of()));
GlobalConfigurationPropertyData updatedConfiguration = ok(
() -> fineractClient.globalConfiguration().retrieveOneByName(configKey, Map.of()));
boolean isEnabled = BooleanUtils.toBoolean(updatedConfiguration.getEnabled());
assertThat(isEnabled).isEqualTo(enabled);
}
public void setGlobalConfigValueString(String configKey, String value) throws IOException {
Response<GlobalConfigurationPropertyData> configuration = globalConfigurationApi.retrieveOneByName(configKey).execute();
ErrorHelper.checkSuccessfulApiCall(configuration);
Long configId = configuration.body().getId();
public void setGlobalConfigValueString(String configKey, String value) {
GlobalConfigurationPropertyData configuration = ok(
() -> fineractClient.globalConfiguration().retrieveOneByName(configKey, Map.of()));
Long configId = configuration.getId();
PutGlobalConfigurationsRequest updateRequest = new PutGlobalConfigurationsRequest().enabled(true).stringValue(value);
Response<PutGlobalConfigurationsResponse> updateResponse = globalConfigurationApi.updateConfiguration1(configId, updateRequest)
.execute();
assertThat(updateResponse.code()).isEqualTo(HttpStatus.SC_OK);
Response<GlobalConfigurationPropertyData> updatedConfiguration = globalConfigurationApi.retrieveOneByName(configKey).execute();
boolean isEnabled = BooleanUtils.toBoolean(updatedConfiguration.body().getEnabled());
ok(() -> fineractClient.globalConfiguration().updateConfiguration1(configId, updateRequest, Map.of()));
GlobalConfigurationPropertyData updatedConfiguration = ok(
() -> fineractClient.globalConfiguration().retrieveOneByName(configKey, Map.of()));
boolean isEnabled = BooleanUtils.toBoolean(updatedConfiguration.getEnabled());
assertThat(isEnabled).isEqualTo(true);
}
public GlobalConfigurationPropertyData getGlobalConfiguration(String configKey) throws IOException {
Response<GlobalConfigurationPropertyData> configuration = globalConfigurationApi.retrieveOneByName(configKey).execute();
ErrorHelper.checkSuccessfulApiCall(configuration);
return configuration.body();
public GlobalConfigurationPropertyData getGlobalConfiguration(String configKey) {
return ok(() -> fineractClient.globalConfiguration().retrieveOneByName(configKey, Map.of()));
}
}
@@ -18,18 +18,20 @@
*/
package org.apache.fineract.test.helper;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.BusinessStep;
import org.apache.fineract.client.models.BusinessStepRequest;
import org.apache.fineract.client.models.JobBusinessStepConfigData;
import org.apache.fineract.client.services.BusinessStepConfigurationApi;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@RequiredArgsConstructor
@Component
@@ -38,9 +40,9 @@ public class WorkFlowJobHelper {
private static final String WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS = "LOAN_CLOSE_OF_BUSINESS";
private final BusinessStepConfigurationApi businessStepConfigurationApi;
private final FineractFeignClient fineractClient;
public void setWorkflowJobs() throws IOException {
public void setWorkflowJobs() {
List<BusinessStep> businessSteps = List.of(new BusinessStep().stepName("APPLY_CHARGE_TO_OVERDUE_LOANS").order(1L), //
new BusinessStep().stepName("LOAN_DELINQUENCY_CLASSIFICATION").order(2L), //
new BusinessStep().stepName("CHECK_LOAN_REPAYMENT_DUE").order(3L), //
@@ -55,18 +57,15 @@ public class WorkFlowJobHelper {
new BusinessStep().stepName("EXTERNAL_ASSET_OWNER_TRANSFER").order(12L)//
);
BusinessStepRequest request = new BusinessStepRequest().businessSteps(businessSteps);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
// --- log changes ---
executeVoid(() -> fineractClient.businessStepConfiguration().updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS,
request, Map.of()));
logChanges();
}
private void logChanges() throws IOException {
// --- log changes ---
Response<JobBusinessStepConfigData> changesResponse = businessStepConfigurationApi
.retrieveAllConfiguredBusinessStep(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS).execute();
List<BusinessStep> businessStepsChanged = changesResponse.body().getBusinessSteps();
private void logChanges() {
JobBusinessStepConfigData changesResponse = ok(() -> fineractClient.businessStepConfiguration()
.retrieveAllConfiguredBusinessStep(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, Map.of()));
List<BusinessStep> businessStepsChanged = changesResponse.getBusinessSteps();
List<String> changes = businessStepsChanged//
.stream()//
.sorted(Comparator.comparingLong(BusinessStep::getOrder))//
@@ -41,6 +41,12 @@ public class FineractInitializer implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
// ALWAYS log initializer counts at INFO level for debugging
log.info("=== FineractInitializer.afterPropertiesSet() called ===");
log.info("Global initializers count: {}", globalInitializerSteps.size());
log.info("Suite initializers count: {}", suiteInitializerSteps.size());
log.info("Scenario initializers count: {}", scenarioInitializerSteps.size());
if (log.isDebugEnabled()) {
String globalInitializers = globalInitializerSteps.stream().map(Object::getClass).map(Class::getName)
.collect(Collectors.joining(", "));
@@ -54,6 +60,11 @@ public class FineractInitializer implements InitializingBean {
Suite initializers: [{}]
Scenario initializers: [{}]
""", globalInitializers, suiteInitializers, scenarioInitializers);
} else {
// Always log the suite initializers at INFO since this is critical
String suiteInitializers = suiteInitializerSteps.stream().map(Object::getClass).map(Class::getName)
.collect(Collectors.joining(", "));
log.info("Suite initializers: [{}]", suiteInitializers);
}
}
@@ -65,7 +76,9 @@ public class FineractInitializer implements InitializingBean {
}
public void setupDefaultsForSuite() throws Exception {
log.info("=== setupDefaultsForSuite() called - {} suite initializers to execute ===", suiteInitializerSteps.size());
for (FineractSuiteInitializerStep initializerStep : suiteInitializerSteps) {
log.info("Executing suite initializer: {}", initializerStep.getClass().getName());
initializerStep.initializeForSuite();
}
businessDateHelper.setBusinessDateToday();
@@ -18,10 +18,18 @@
*/
package org.apache.fineract.test.initializer.global;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.ChargeData;
import org.apache.fineract.client.models.ChargeRequest;
import org.apache.fineract.client.models.PostChargesResponse;
import org.apache.fineract.client.services.ChargesApi;
import org.apache.fineract.test.data.ChargeCalculationType;
import org.apache.fineract.test.data.ChargePaymentMode;
import org.apache.fineract.test.data.ChargeProductAppliesTo;
@@ -31,8 +39,8 @@ import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.support.TestContext;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Slf4j
@RequiredArgsConstructor
@Component
public class ChargeGlobalInitializerStep implements FineractGlobalInitializerStep {
@@ -78,137 +86,128 @@ public class ChargeGlobalInitializerStep implements FineractGlobalInitializerSte
public static final Integer CHARGE_CALCULATION_TYPE_PERCENTAGE_DISBURSEMENT_AMOUNT = ChargeCalculationType.PERCENTAGE_DISBURSEMENT_AMOUNT.value;
public static final Integer CHARGE_CALCULATION_TYPE_PERCENTAGE_LOAN_AMOUNT_PLUS_INTEREST = ChargeCalculationType.PERCENTAGE_LOAN_AMOUNT_PLUS_INTEREST.value;
private final ChargesApi chargesApi;
private final FineractFeignClient fineractClient;
@Override
public void initialize() throws Exception {
// Loan - % late (overdue) fee
ChargeRequest requestLoanPercentLate = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_PERCENTAGE_LATE_FEE,
CHARGE_TIME_TYPE_OVERDUE_FEES, CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT, CHARGE_AMOUNT_OVERDUE_PERCENTAGE, true, true);
Response<PostChargesResponse> responseLoanPercentLate = chargesApi.createCharge(requestLoanPercentLate).execute();
List<ChargeData> existingCharges = new ArrayList<>();
try {
existingCharges = fineractClient.charges().retrieveAllCharges(Map.of());
} catch (Exception e) {
log.debug("Could not retrieve existing charges, will create them", e);
}
final List<ChargeData> charges = existingCharges;
PostChargesResponse responseLoanPercentLate = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_PERCENTAGE_LATE_FEE, CHARGE_TIME_TYPE_OVERDUE_FEES, CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT,
CHARGE_AMOUNT_OVERDUE_PERCENTAGE, true, true);
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_PERCENT_LATE_CREATE_RESPONSE, responseLoanPercentLate);
// Loan - % processing fee
ChargeRequest requestLoanPercentProcessing = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_PERCENTAGE_PROCESSING_FEE,
CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_PERCENTAGE_LOAN_AMOUNT_PLUS_INTEREST, CHARGE_AMOUNT_PERCENTAGE,
true, false);
Response<PostChargesResponse> responseLoanPercentProcessing = chargesApi.createCharge(requestLoanPercentProcessing).execute();
PostChargesResponse responseLoanPercentProcessing = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_PERCENTAGE_PROCESSING_FEE, CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE,
CHARGE_CALCULATION_TYPE_PERCENTAGE_LOAN_AMOUNT_PLUS_INTEREST, CHARGE_AMOUNT_PERCENTAGE, true, false);
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_PERCENT_PROCESSING_CREATE_RESPONSE, responseLoanPercentProcessing);
// Loan - fixed late (overdue) fee
ChargeRequest requestLoanFixedLate = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_FIXED_LATE_FEE,
PostChargesResponse responseLoanFixedLate = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_FIXED_LATE_FEE,
CHARGE_TIME_TYPE_OVERDUE_FEES, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, true);
Response<PostChargesResponse> responseLoanFixedLate = chargesApi.createCharge(requestLoanFixedLate).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_FIXED_LATE_CREATE_RESPONSE, responseLoanFixedLate);
// Loan - fixed returned payment fee
ChargeRequest requestLoanFixedReturnedPayment = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
PostChargesResponse responseLoanFixedReturnedPayment = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_FIXED_RETURNED_PAYMENT_FEE, CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_FLAT,
CHARGE_AMOUNT_FLAT, true, false);
Response<PostChargesResponse> responseLoanFixedReturnedPayment = chargesApi.createCharge(requestLoanFixedReturnedPayment).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_FIXED_RETURNED_PAYMENT_CREATE_RESPONSE, responseLoanFixedReturnedPayment);
// Loan - snooze fee
ChargeRequest requestLoanSnooze = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_SNOOZE_FEE,
PostChargesResponse responseLoanSnooze = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_SNOOZE_FEE,
CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, false);
Response<PostChargesResponse> responseLoanSnooze = chargesApi.createCharge(requestLoanSnooze).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_SNOOZE_FEE_CREATE_RESPONSE, responseLoanSnooze);
// Loan - NSF fee
ChargeRequest requestLoanNsf = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_NSF_FEE,
PostChargesResponse responseLoanNsf = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_NSF_FEE,
CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, true);
Response<PostChargesResponse> responseLoanNsf = chargesApi.createCharge(requestLoanNsf).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_NSF_FEE_CREATE_RESPONSE, responseLoanNsf);
// Loan - Disbursement % fee
ChargeRequest requestLoanDisbursePercent = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_DISBURSEMENT_PERCENT_FEE,
CHARGE_TIME_TYPE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT, CHARGE_AMOUNT_DISBURSEMENT_PERCENTAGE, true,
false);
Response<PostChargesResponse> responseLoanDisbursePercent = chargesApi.createCharge(requestLoanDisbursePercent).execute();
PostChargesResponse responseLoanDisbursePercent = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_DISBURSEMENT_PERCENT_FEE, CHARGE_TIME_TYPE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT,
CHARGE_AMOUNT_DISBURSEMENT_PERCENTAGE, true, false);
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_DISBURSEMENET_FEE_CREATE_RESPONSE, responseLoanDisbursePercent);
// Loan - Tranche Disbursement % fee
ChargeRequest requestLoanTrancheDisbursePercent = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
PostChargesResponse responseLoanTrancheDisbursePercent = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_TRANCHE_DISBURSEMENT_PERCENT_FEE, CHARGE_TIME_TYPE_TRANCHE_DISBURSEMENT,
CHARGE_CALCULATION_TYPE_PERCENTAGE_DISBURSEMENT_AMOUNT, CHARGE_AMOUNT_DISBURSEMENT_PERCENTAGE, true, false);
Response<PostChargesResponse> responseLoanTrancheDisbursePercent = chargesApi.createCharge(requestLoanTrancheDisbursePercent)
.execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_TRANCHE_DISBURSEMENT_PERCENT_CREATE_RESPONSE,
responseLoanTrancheDisbursePercent);
// Loan - Installment % fee Amount + Interest
final ChargeRequest requestLoanInstallmentPercentAmountPlusInterest = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
PostChargesResponse responseLoanInstallmentPercentAmountPlusInterest = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_INSTALLMENT_FEE_PERCENT_AMOUNT_PLUS_INTEREST, CHARGE_TIME_TYPE_INSTALLMENT,
CHARGE_CALCULATION_TYPE_PERCENTAGE_LOAN_AMOUNT_PLUS_INTEREST, CHARGE_INSTALLMENT_FEE_AMOUNT_PERCENTAGE, true, false);
final Response<PostChargesResponse> responseLoanInstallmentPercentAmountPlusInterest = chargesApi
.createCharge(requestLoanInstallmentPercentAmountPlusInterest).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_INSTALLMENT_FEE_PERCENTAGE_AMOUNT_PLUS_INTEREST_CREATE_RESPONSE,
responseLoanInstallmentPercentAmountPlusInterest);
// Loan - % late (overdue) fee amount+interest
ChargeRequest requestLoanPercentAmountPlusInterestLate = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
PostChargesResponse responseLoanPercentAmountPlusInterestLate = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_PERCENTAGE_LATE_FEE_AMOUNT_PLUS_INTEREST, CHARGE_TIME_TYPE_OVERDUE_FEES,
CHARGE_CALCULATION_TYPE_PERCENTAGE_LOAN_AMOUNT_PLUS_INTEREST, CHARGE_AMOUNT_OVERDUE_PERCENTAGE, true, true);
Response<PostChargesResponse> responseLoanPercentAmountPlusInterestLate = chargesApi
.createCharge(requestLoanPercentAmountPlusInterestLate).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_PERCENT_LATE_AMOUNT_PLUS_INTEREST_CREATE_RESPONSE,
responseLoanPercentAmountPlusInterestLate);
// Client - fixed fee
ChargeRequest requestClientFixed = defaultChargesRequest(CHARGE_APPLIES_TO_CLIENT, CHARGE_CLIENT_FIXED_FEE,
PostChargesResponse responseClientFixed = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_CLIENT, CHARGE_CLIENT_FIXED_FEE,
CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, false);
Response<PostChargesResponse> responseClientFixed = chargesApi.createCharge(requestClientFixed).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_CLIENT_FIXED_FEE_CREATE_RESPONSE, responseClientFixed);
// Loan - Disbursement fixed fee
ChargeRequest requestDisbursementCharge = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_DISBURSEMENT_CHARGE,
CHARGE_TIME_TYPE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, false);
Response<PostChargesResponse> responseDisbursementCharge = chargesApi.createCharge(requestDisbursementCharge).execute();
PostChargesResponse responseDisbursementCharge = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_DISBURSEMENT_CHARGE, CHARGE_TIME_TYPE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, false);
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_DISBURSEMENT_CHARGE_CREATE_RESPONSE, responseDisbursementCharge);
// Loan - Tranche Disbursement Charge (Flat)
ChargeRequest requestTrancheDisbursementCharge = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
PostChargesResponse responseTrancheDisbursementCharge = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_TRANCHE_DISBURSEMENT_CHARGE_AMOUNT, CHARGE_TIME_TYPE_TRANCHE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_FLAT, 10.0,
true, false);
Response<PostChargesResponse> responseTrancheDisbursementCharge = chargesApi.createCharge(requestTrancheDisbursementCharge)
.execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_TRANCHE_DISBURSEMENT_CHARGE_FLAT_CREATE_RESPONSE,
responseTrancheDisbursementCharge);
// Loan - Tranche Disbursement Charge (%)
ChargeRequest requestTrancheDisbursementChargePercent = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
PostChargesResponse responseTrancheDisbursementChargePercent = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_TRANCHE_DISBURSEMENT_CHARGE_PERCENT, CHARGE_TIME_TYPE_TRANCHE_DISBURSEMENT,
CHARGE_CALCULATION_TYPE_PERCENTAGE_DISBURSEMENT_AMOUNT, 2.0, true, false);
Response<PostChargesResponse> responseTrancheDisbursementChargePercent = chargesApi
.createCharge(requestTrancheDisbursementChargePercent).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_TRANCHE_DISBURSEMENT_CHARGE_PERCENT_CREATE_RESPONSE,
responseTrancheDisbursementChargePercent);
// Loan - Installment flat fee
final ChargeRequest requestLoanInstallmentFlat = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_INSTALLMENT_FEE_FLAT,
CHARGE_TIME_TYPE_INSTALLMENT, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_INSTALLMENT_FEE_AMOUNT_FLAT, true, false);
final Response<PostChargesResponse> responseLoanInstallmentFlat = chargesApi.createCharge(requestLoanInstallmentFlat).execute();
PostChargesResponse responseLoanInstallmentFlat = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_INSTALLMENT_FEE_FLAT, CHARGE_TIME_TYPE_INSTALLMENT, CHARGE_CALCULATION_TYPE_FLAT,
CHARGE_INSTALLMENT_FEE_AMOUNT_FLAT, true, false);
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_INSTALLMENT_FEE_FLAT_CREATE_RESPONSE, responseLoanInstallmentFlat);
// Loan - Installment % fee Amount
final ChargeRequest requestLoanInstallmentPercentAmount = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
PostChargesResponse responseLoanInstallmentPercentAmount = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_INSTALLMENT_FEE_PERCENT_AMOUNT, CHARGE_TIME_TYPE_INSTALLMENT, CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT,
CHARGE_INSTALLMENT_FEE_AMOUNT_PERCENTAGE, true, false);
final Response<PostChargesResponse> responseLoanInstallmentPercentAmount = chargesApi
.createCharge(requestLoanInstallmentPercentAmount).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_INSTALLMENT_FEE_PERCENTAGE_AMOUNT_CREATE_RESPONSE,
responseLoanInstallmentPercentAmount);
// Loan - Installment % fee Interest
final ChargeRequest requestLoanInstallmentPercentInterest = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
PostChargesResponse responseLoanInstallmentPercentInterest = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_INSTALLMENT_FEE_PERCENT_INTEREST, CHARGE_TIME_TYPE_INSTALLMENT, CHARGE_CALCULATION_TYPE_PERCENTAGE_INTEREST,
CHARGE_AMOUNT_PERCENTAGE, true, false);
final Response<PostChargesResponse> responseLoanInstallmentPercentInterest = chargesApi
.createCharge(requestLoanInstallmentPercentInterest).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_INSTALLMENT_FEE_PERCENTAGE_INTEREST_CREATE_RESPONSE,
responseLoanInstallmentPercentInterest);
}
private PostChargesResponse createChargeIfNotExists(List<ChargeData> existingCharges, Enum<ChargeProductAppliesTo> appliesTo,
String name, Integer chargeTimeType, Integer chargeCalculationType, Double amount, Boolean isActive, Boolean isPenalty)
throws Exception {
ChargeRequest request = defaultChargesRequest(appliesTo, name, chargeTimeType, chargeCalculationType, amount, isActive, isPenalty);
try {
return ok(() -> fineractClient.charges().createCharge(request, Map.of()));
} catch (CallFailedRuntimeException e) {
if (e.getStatus() == 403 && e.getDeveloperMessage() != null && e.getDeveloperMessage().contains("already exists")) {
log.debug("Charge '{}' already exists, retrieving existing charge", name);
ChargeData existing = existingCharges.stream().filter(c -> name.equals(c.getName())).findFirst().orElse(null);
if (existing != null) {
PostChargesResponse response = new PostChargesResponse();
response.setResourceId(existing.getId());
return response;
}
}
throw e;
}
}
public static ChargeRequest defaultChargesRequest(Enum<ChargeProductAppliesTo> appliesTo, String name, Integer chargeTimeType,
Integer chargeCalculationType, Double amount, Boolean isActive, Boolean isPenalty) throws Exception {
ChargeRequest request = new ChargeRequest();
@@ -29,7 +29,7 @@ public class CobBusinessStepInitializerStep implements FineractGlobalInitializer
private final WorkFlowJobHelper workFlowJobHelper;
@Override
public void initialize() throws Exception {
public void initialize() {
workFlowJobHelper.setWorkflowJobs();
}
}
@@ -18,19 +18,23 @@
*/
package org.apache.fineract.test.initializer.global;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.PostCodeValuesDataRequest;
import org.apache.fineract.client.models.PostCodesRequest;
import org.apache.fineract.client.models.PutCodeValuesDataRequest;
import org.apache.fineract.client.services.CodeValuesApi;
import org.apache.fineract.client.services.CodesApi;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@@ -114,11 +118,10 @@ public class CodeGlobalInitializerStep implements FineractGlobalInitializerStep
public static final String CODE_VALUE_WRITE_OFF_REASON_TEST_2 = "Forgiven";
public static final String CODE_VALUE_WRITE_OFF_REASON_TEST_3 = "Test";
private final CodesApi codesApi;
private final CodeValuesApi codeValuesApi;
private final FineractFeignClient fineractClient;
@Override
public void initialize() throws Exception {
public void initialize() {
createCodeNames();
createCodeValues();
}
@@ -267,9 +270,14 @@ public class CodeGlobalInitializerStep implements FineractGlobalInitializerStep
postCodeValuesDataRequest.position(position);
try {
codeValuesApi.createCodeValue(codeId, postCodeValuesDataRequest).execute();
} catch (IOException e) {
throw new RuntimeException("Error while creating code value", e);
executeVoid(() -> fineractClient.codeValues().createCodeValue(codeId, postCodeValuesDataRequest, Map.of()));
log.info("Code value '{}' created successfully", name);
} catch (CallFailedRuntimeException e) {
if (e.getStatus() == 403 && e.getDeveloperMessage() != null && e.getDeveloperMessage().contains("already exists")) {
log.debug("Code value '{}' already exists, skipping creation", name);
return;
}
throw e;
}
});
}
@@ -282,11 +290,7 @@ public class CodeGlobalInitializerStep implements FineractGlobalInitializerStep
putCodeValuesDataRequest.name(name);
putCodeValuesDataRequest.position(position);
try {
codeValuesApi.updateCodeValue(codeId, (long) position, putCodeValuesDataRequest).execute();
} catch (IOException e) {
throw new RuntimeException("Error while updating code value", e);
}
executeVoid(() -> fineractClient.codeValues().updateCodeValue(codeId, (long) position, putCodeValuesDataRequest, Map.of()));
});
}
@@ -301,11 +305,13 @@ public class CodeGlobalInitializerStep implements FineractGlobalInitializerStep
codesNameList.add(CODE_NAME_ACTIVE_DUTY_TAG);
codesNameList.forEach(codeName -> {
PostCodesRequest postCodesRequest = new PostCodesRequest();
try {
codesApi.createCode(postCodesRequest.name(codeName)).execute();
} catch (IOException e) {
throw new RuntimeException("Error while creating code", e);
fineractClient.codes().retrieveCodeByName(codeName);
// Code already exists, skip creation
} catch (Exception e) {
// Code doesn't exist, create it
PostCodesRequest postCodesRequest = new PostCodesRequest();
executeVoid(() -> fineractClient.codes().createCode(postCodesRequest.name(codeName), Map.of()));
}
});
}
@@ -18,11 +18,14 @@
*/
package org.apache.fineract.test.initializer.global;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.CurrencyUpdateRequest;
import org.apache.fineract.client.services.CurrencyApi;
import org.apache.fineract.test.support.TestContext;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.core.Ordered;
@@ -36,12 +39,12 @@ public class CurrencyGlobalInitializerStep implements FineractGlobalInitializerS
public static final List<String> CURRENCIES = Arrays.asList("EUR", "USD");
private final CurrencyApi currencyApi;
private final FineractFeignClient fineractClient;
@Override
public void initialize() throws Exception {
public void initialize() {
var request = new CurrencyUpdateRequest();
var response = currencyApi.updateCurrencies(request.currencies(CURRENCIES)).execute();
var response = ok(() -> fineractClient.currency().updateCurrencies(request.currencies(CURRENCIES), Map.of()));
TestContext.INSTANCE.set(TestContextKey.PUT_CURRENCIES_RESPONSE, response);
}
}
@@ -18,16 +18,21 @@
*/
package org.apache.fineract.test.initializer.global;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.PostColumnHeaderData;
import org.apache.fineract.client.models.PostDataTablesRequest;
import org.apache.fineract.client.services.DataTablesApi;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@@ -71,10 +76,10 @@ public class DatatablesGlobalInitializerStep implements FineractGlobalInitialize
public static final String DATA_TABLE_3_COLUMN_5_TYPE = "Dropdown";
public static final String DATA_TABLE_3_COLUMN_5_CODE = "active_duty_tag";
private final DataTablesApi dataTablesApi;
private final FineractFeignClient fineractClient;
@Override
public void initialize() throws Exception {
public void initialize() {
// autopay
PostColumnHeaderData column1 = new PostColumnHeaderData();
column1.name(DATA_TABLE_1_COLUMN_1_NAME);
@@ -97,7 +102,7 @@ public class DatatablesGlobalInitializerStep implements FineractGlobalInitialize
postDataTablesRequest.multiRow(true);
postDataTablesRequest.columns(columns);
dataTablesApi.createDatatable(postDataTablesRequest).execute();
createDatatableIdempotent(postDataTablesRequest);
// scheduled payments
PostColumnHeaderData columnScheduled1 = new PostColumnHeaderData();
@@ -134,7 +139,7 @@ public class DatatablesGlobalInitializerStep implements FineractGlobalInitialize
postDataTablesRequestScheduled.multiRow(true);
postDataTablesRequestScheduled.columns(columnsScheduled);
dataTablesApi.createDatatable(postDataTablesRequestScheduled).execute();
createDatatableIdempotent(postDataTablesRequestScheduled);
// 3 tags
PostColumnHeaderData column3Tags1 = new PostColumnHeaderData();
@@ -181,6 +186,16 @@ public class DatatablesGlobalInitializerStep implements FineractGlobalInitialize
postDataTablesRequest3Tags.multiRow(false);
postDataTablesRequest3Tags.columns(columns3Tags);
dataTablesApi.createDatatable(postDataTablesRequest3Tags).execute();
createDatatableIdempotent(postDataTablesRequest3Tags);
}
private void createDatatableIdempotent(PostDataTablesRequest datatableRequest) {
String datatableName = datatableRequest.getDatatableName();
try {
fineractClient.dataTables().getDatatable(datatableName, Map.of());
} catch (Exception e) {
log.debug("Datatable '{}' does not exist yet, will create it", datatableName);
executeVoid(() -> fineractClient.dataTables().createDatatable(datatableRequest, Map.of()));
}
}
}
@@ -18,18 +18,26 @@
*/
package org.apache.fineract.test.initializer.global;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.DelinquencyBucketData;
import org.apache.fineract.client.models.DelinquencyBucketRequest;
import org.apache.fineract.client.models.DelinquencyRangeData;
import org.apache.fineract.client.models.DelinquencyRangeRequest;
import org.apache.fineract.client.services.DelinquencyRangeAndBucketsManagementApi;
import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@@ -39,18 +47,38 @@ public class DelinquencyGlobalInitializerStep implements FineractGlobalInitializ
public static final List<Integer> DEFAULT_DELINQUENCY_RANGES = Arrays.asList(1, 3, 30, 60, 90, 120, 150, 180, 240);
public static final String DEFAULT_DELINQUENCY_BUCKET_NAME = "Default delinquency bucket";
private final DelinquencyRangeAndBucketsManagementApi delinquencyApi;
private final FineractFeignClient fineractClient;
private final List<Long> createdRangeIds = new ArrayList<>();
@Override
public void initialize() throws Exception {
public void initialize() {
setDefaultDelinquencyRanges();
setDefaultDelinquencyBucket();
}
public void setDefaultDelinquencyRanges() throws IOException {
public void setDefaultDelinquencyRanges() {
List<DelinquencyRangeData> existingRanges;
try {
existingRanges = fineractClient.delinquencyRangeAndBucketsManagement().getDelinquencyRanges(Map.of());
} catch (Exception e) {
log.debug("Could not retrieve existing delinquency ranges, will create them", e);
existingRanges = new ArrayList<>();
}
for (int i = 0; i < DEFAULT_DELINQUENCY_RANGES.size() - 1; i++) {
String classification = "Delinquency range " + DEFAULT_DELINQUENCY_RANGES.get(i).toString();
DelinquencyRangeData existingRange = existingRanges.stream().filter(r -> classification.equals(r.getClassification()))
.findFirst().orElse(null);
if (existingRange != null) {
createdRangeIds.add(existingRange.getId());
continue;
}
DelinquencyRangeRequest postDelinquencyRangeRequest = new DelinquencyRangeRequest();
postDelinquencyRangeRequest.classification("Delinquency range " + DEFAULT_DELINQUENCY_RANGES.get(i).toString());
postDelinquencyRangeRequest.classification(classification);
postDelinquencyRangeRequest.locale(DEFAULT_LOCALE);
if (DEFAULT_DELINQUENCY_RANGES.get(i) == 1) {
postDelinquencyRangeRequest.minimumAgeDays(1);
@@ -60,30 +88,49 @@ public class DelinquencyGlobalInitializerStep implements FineractGlobalInitializ
postDelinquencyRangeRequest.maximumAgeDays(DEFAULT_DELINQUENCY_RANGES.get(i + 1));
}
delinquencyApi.createDelinquencyRange(postDelinquencyRangeRequest).execute();
PostDelinquencyRangeResponse response = ok(() -> fineractClient.delinquencyRangeAndBucketsManagement()
.createDelinquencyRange(postDelinquencyRangeRequest, Map.of()));
createdRangeIds.add(response.getResourceId());
}
String lastClassification = "Delinquency range " + DEFAULT_DELINQUENCY_RANGES.get(DEFAULT_DELINQUENCY_RANGES.size() - 1).toString();
DelinquencyRangeData existingLastRange = existingRanges.stream().filter(r -> lastClassification.equals(r.getClassification()))
.findFirst().orElse(null);
if (existingLastRange != null) {
createdRangeIds.add(existingLastRange.getId());
return;
}
DelinquencyRangeRequest lastRange = new DelinquencyRangeRequest();
lastRange.classification("Delinquency range " + DEFAULT_DELINQUENCY_RANGES.get(DEFAULT_DELINQUENCY_RANGES.size() - 1).toString());
lastRange.classification(lastClassification);
lastRange.locale(DEFAULT_LOCALE);
lastRange.minimumAgeDays(DEFAULT_DELINQUENCY_RANGES.get(DEFAULT_DELINQUENCY_RANGES.size() - 1) + 1);
lastRange.maximumAgeDays(null);
delinquencyApi.createDelinquencyRange(lastRange).execute();
PostDelinquencyRangeResponse lastResponse = ok(
() -> fineractClient.delinquencyRangeAndBucketsManagement().createDelinquencyRange(lastRange, Map.of()));
createdRangeIds.add(lastResponse.getResourceId());
}
public void setDefaultDelinquencyBucket() throws IOException {
List<Long> rangesNr = new ArrayList<>();
public void setDefaultDelinquencyBucket() {
try {
List<DelinquencyBucketData> existingBuckets = fineractClient.delinquencyRangeAndBucketsManagement()
.getDelinquencyBuckets(Map.of());
boolean bucketExists = existingBuckets.stream().anyMatch(b -> DEFAULT_DELINQUENCY_BUCKET_NAME.equals(b.getName()));
for (int i = 1; i < DEFAULT_DELINQUENCY_RANGES.size() + 1; i++) {
rangesNr.add((long) DEFAULT_DELINQUENCY_RANGES.indexOf(DEFAULT_DELINQUENCY_RANGES.get(i - 1)));
if (bucketExists) {
return;
}
} catch (Exception e) {
log.debug("Could not retrieve existing delinquency buckets, will create default bucket", e);
}
rangesNr.add((long) DEFAULT_DELINQUENCY_RANGES.size());
DelinquencyBucketRequest postDelinquencyBucketRequest = new DelinquencyBucketRequest();
postDelinquencyBucketRequest.name(DEFAULT_DELINQUENCY_BUCKET_NAME);
postDelinquencyBucketRequest.ranges(rangesNr);
postDelinquencyBucketRequest.ranges(createdRangeIds);
delinquencyApi.createDelinquencyBucket(postDelinquencyBucketRequest).execute();
executeVoid(() -> fineractClient.delinquencyRangeAndBucketsManagement().createDelinquencyBucket(postDelinquencyBucketRequest,
Map.of()));
}
}
@@ -18,11 +18,17 @@
*/
package org.apache.fineract.test.initializer.global;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.PostFinancialActivityAccountsRequest;
import org.apache.fineract.client.services.MappingFinancialActivitiesToAccountsApi;
import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor
@Component
public class FinancialActivityMappingGlobalInitializerStep implements FineractGlobalInitializerStep {
@@ -30,13 +36,22 @@ public class FinancialActivityMappingGlobalInitializerStep implements FineractGl
public static final Long FINANCIAL_ACTIVITY_ID_ASSET_TRANSFER = 100L;
public static final Long GL_ACCOUNT_ID_ASSET_TRANSFER = 21L;
private final MappingFinancialActivitiesToAccountsApi mappingFinancialActivitiesToAccountsApi;
private final FineractFeignClient fineractClient;
@Override
public void initialize() throws Exception {
public void initialize() {
PostFinancialActivityAccountsRequest request = new PostFinancialActivityAccountsRequest()
.financialActivityId(FINANCIAL_ACTIVITY_ID_ASSET_TRANSFER).glAccountId(GL_ACCOUNT_ID_ASSET_TRANSFER);
mappingFinancialActivitiesToAccountsApi.createGLAccount(request).execute();
try {
executeVoid(() -> fineractClient.mappingFinancialActivitiesToAccounts().createGLAccount(request, Map.of()));
log.info("Financial activity mapping created successfully");
} catch (CallFailedRuntimeException e) {
if (e.getStatus() == 403 && e.getDeveloperMessage() != null && e.getDeveloperMessage().contains("already exists")) {
log.debug("Financial activity mapping already exists, skipping creation");
return;
}
throw e;
}
}
}
@@ -18,16 +18,21 @@
*/
package org.apache.fineract.test.initializer.global;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.FundData;
import org.apache.fineract.client.models.FundRequest;
import org.apache.fineract.client.services.FundsApi;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
@@ -36,21 +41,30 @@ public class FundGlobalInitializerStep implements FineractGlobalInitializerStep
public static final String FUNDS_LENDER_A = "Lender A";
public static final String FUNDS_LENDER_B = "Lender B";
private final FundsApi fundsApi;
private final FineractFeignClient fineractClient;
@Override
public void initialize() throws Exception {
public void initialize() {
List<FundData> existingFunds = new ArrayList<>();
try {
existingFunds = fineractClient.funds().retrieveFunds(Map.of());
} catch (Exception e) {
log.debug("Could not retrieve existing funds, will create them", e);
}
final List<FundData> funds = existingFunds;
List<String> fundNames = new ArrayList<>();
fundNames.add(FUNDS_LENDER_A);
fundNames.add(FUNDS_LENDER_B);
fundNames.forEach(name -> {
boolean fundExists = funds.stream().anyMatch(f -> name.equals(f.getName()));
if (fundExists) {
return;
}
FundRequest postFundsRequest = new FundRequest();
postFundsRequest.name(name);
try {
fundsApi.createFund(postFundsRequest).execute();
} catch (IOException e) {
throw new RuntimeException("Error while creating fund", e);
}
executeVoid(() -> fineractClient.funds().createFund(postFundsRequest, Map.of()));
});
}
@@ -18,9 +18,16 @@
*/
package org.apache.fineract.test.initializer.global;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetGLAccountsResponse;
import org.apache.fineract.client.models.PostGLAccountsRequest;
import org.apache.fineract.client.services.GeneralLedgerAccountApi;
import org.apache.fineract.test.data.GLAType;
import org.apache.fineract.test.data.GLAUsage;
import org.apache.fineract.test.factory.GLAccountRequestFactory;
@@ -28,6 +35,7 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@@ -87,82 +95,52 @@ public class GLGlobalInitializerStep implements FineractGlobalInitializerStep {
public static final String GLA_GL_CODE_23 = "450280";
public static final String GLA_GL_CODE_24 = "450281";
private final GeneralLedgerAccountApi glaApi;
private final FineractFeignClient fineractClient;
@Override
public void initialize() throws Exception {
public void initialize() {
List<GetGLAccountsResponse> existingAccounts = new ArrayList<>();
try {
existingAccounts = fineractClient.generalLedgerAccount().retrieveAllAccountsUniversal(Map.of());
} catch (Exception e) {
log.debug("Could not retrieve existing GL accounts, will create them", e);
}
PostGLAccountsRequest postGLAccountsRequest1 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_1, GLA_GL_CODE_1,
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest1).execute();
PostGLAccountsRequest postGLAccountsRequest2 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_2, GLA_GL_CODE_2,
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest2).execute();
PostGLAccountsRequest postGLAccountsRequest3 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_3, GLA_GL_CODE_3,
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest3).execute();
PostGLAccountsRequest postGLAccountsRequest4 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_4, GLA_GL_CODE_4,
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest4).execute();
PostGLAccountsRequest postGLAccountsRequest5 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_5, GLA_GL_CODE_5,
GLA_TYPE_LIABILITY, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest5).execute();
PostGLAccountsRequest postGLAccountsRequest6 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_6, GLA_GL_CODE_6,
GLA_TYPE_LIABILITY, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest6).execute();
PostGLAccountsRequest postGLAccountsRequest7 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_7, GLA_GL_CODE_7,
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest7).execute();
PostGLAccountsRequest postGLAccountsRequest8 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_8, GLA_GL_CODE_8,
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest8).execute();
PostGLAccountsRequest postGLAccountsRequest9 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_9, GLA_GL_CODE_9,
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest9).execute();
PostGLAccountsRequest postGLAccountsRequest10 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_10, GLA_GL_CODE_10,
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest10).execute();
PostGLAccountsRequest postGLAccountsRequest11 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_11, GLA_GL_CODE_11,
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest11).execute();
PostGLAccountsRequest postGLAccountsRequest12 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_12, GLA_GL_CODE_12,
GLA_TYPE_EXPENSE, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest12).execute();
PostGLAccountsRequest postGLAccountsRequest13 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_13, GLA_GL_CODE_13,
GLA_TYPE_EXPENSE, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest13).execute();
PostGLAccountsRequest postGLAccountsRequest14 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_14, GLA_GL_CODE_14,
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest14).execute();
PostGLAccountsRequest postGLAccountsRequest15 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_15, GLA_GL_CODE_15,
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest15).execute();
PostGLAccountsRequest postGLAccountsRequest16 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_16, GLA_GL_CODE_16,
GLA_TYPE_EXPENSE, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest16).execute();
PostGLAccountsRequest postGLAccountsRequest17 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_17, GLA_GL_CODE_17,
GLA_TYPE_LIABILITY, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest17).execute();
PostGLAccountsRequest postGLAccountsRequest18 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_18, GLA_GL_CODE_18,
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest18).execute();
PostGLAccountsRequest postGLAccountsRequest19 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_19, GLA_GL_CODE_19,
GLA_TYPE_EXPENSE, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest19).execute();
PostGLAccountsRequest postGLAccountsRequest20 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_20, GLA_GL_CODE_20,
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest20).execute();
PostGLAccountsRequest postGLAccountsRequest21 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_21, GLA_GL_CODE_21,
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest21).execute();
PostGLAccountsRequest postGLAccountsRequest22 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_22, GLA_GL_CODE_22,
GLA_TYPE_LIABILITY, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest22).execute();
PostGLAccountsRequest postGLAccountsRequest23 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_23, GLA_GL_CODE_23,
GLA_TYPE_EXPENSE, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest23).execute();
PostGLAccountsRequest postGLAccountsRequest24 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_24, GLA_GL_CODE_24,
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest24).execute();
final List<GetGLAccountsResponse> accounts = existingAccounts;
createGLAccountIfNotExists(accounts, GLA_NAME_1, GLA_GL_CODE_1, GLA_TYPE_ASSET);
createGLAccountIfNotExists(accounts, GLA_NAME_2, GLA_GL_CODE_2, GLA_TYPE_ASSET);
createGLAccountIfNotExists(accounts, GLA_NAME_3, GLA_GL_CODE_3, GLA_TYPE_ASSET);
createGLAccountIfNotExists(accounts, GLA_NAME_4, GLA_GL_CODE_4, GLA_TYPE_ASSET);
createGLAccountIfNotExists(accounts, GLA_NAME_5, GLA_GL_CODE_5, GLA_TYPE_LIABILITY);
createGLAccountIfNotExists(accounts, GLA_NAME_6, GLA_GL_CODE_6, GLA_TYPE_LIABILITY);
createGLAccountIfNotExists(accounts, GLA_NAME_7, GLA_GL_CODE_7, GLA_TYPE_INCOME);
createGLAccountIfNotExists(accounts, GLA_NAME_8, GLA_GL_CODE_8, GLA_TYPE_INCOME);
createGLAccountIfNotExists(accounts, GLA_NAME_9, GLA_GL_CODE_9, GLA_TYPE_INCOME);
createGLAccountIfNotExists(accounts, GLA_NAME_10, GLA_GL_CODE_10, GLA_TYPE_INCOME);
createGLAccountIfNotExists(accounts, GLA_NAME_11, GLA_GL_CODE_11, GLA_TYPE_INCOME);
createGLAccountIfNotExists(accounts, GLA_NAME_12, GLA_GL_CODE_12, GLA_TYPE_EXPENSE);
createGLAccountIfNotExists(accounts, GLA_NAME_13, GLA_GL_CODE_13, GLA_TYPE_EXPENSE);
createGLAccountIfNotExists(accounts, GLA_NAME_14, GLA_GL_CODE_14, GLA_TYPE_ASSET);
createGLAccountIfNotExists(accounts, GLA_NAME_15, GLA_GL_CODE_15, GLA_TYPE_INCOME);
createGLAccountIfNotExists(accounts, GLA_NAME_16, GLA_GL_CODE_16, GLA_TYPE_EXPENSE);
createGLAccountIfNotExists(accounts, GLA_NAME_17, GLA_GL_CODE_17, GLA_TYPE_LIABILITY);
createGLAccountIfNotExists(accounts, GLA_NAME_18, GLA_GL_CODE_18, GLA_TYPE_ASSET);
createGLAccountIfNotExists(accounts, GLA_NAME_19, GLA_GL_CODE_19, GLA_TYPE_EXPENSE);
createGLAccountIfNotExists(accounts, GLA_NAME_20, GLA_GL_CODE_20, GLA_TYPE_INCOME);
createGLAccountIfNotExists(accounts, GLA_NAME_21, GLA_GL_CODE_21, GLA_TYPE_ASSET);
createGLAccountIfNotExists(accounts, GLA_NAME_22, GLA_GL_CODE_22, GLA_TYPE_LIABILITY);
createGLAccountIfNotExists(accounts, GLA_NAME_23, GLA_GL_CODE_23, GLA_TYPE_EXPENSE);
createGLAccountIfNotExists(accounts, GLA_NAME_24, GLA_GL_CODE_24, GLA_TYPE_INCOME);
}
private void createGLAccountIfNotExists(List<GetGLAccountsResponse> existingAccounts, String name, String glCode, Integer type) {
boolean accountExists = existingAccounts.stream().anyMatch(a -> glCode.equals(a.getGlCode()));
if (accountExists) {
return;
}
PostGLAccountsRequest request = GLAccountRequestFactory.defaultGLAccountRequest(name, glCode, type, GLA_USAGE_DETAIL, true);
executeVoid(() -> fineractClient.generalLedgerAccount().createGLAccount1(request, Map.of()));
}
}
@@ -18,17 +18,22 @@
*/
package org.apache.fineract.test.initializer.global;
import java.io.IOException;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.PaymentTypeData;
import org.apache.fineract.client.models.PaymentTypeRequest;
import org.apache.fineract.client.services.PaymentTypeApi;
import org.apache.fineract.test.factory.PaymentTypesRequestFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@@ -43,30 +48,40 @@ public class PaymentTypeGlobalInitializerStep implements FineractGlobalInitializ
public static final String PAYMENT_TYPE_REPAYMENT_ADJUSTMENT_CHARGEBACK = "REPAYMENT_ADJUSTMENT_CHARGEBACK";
public static final String PAYMENT_TYPE_REPAYMENT_ADJUSTMENT_REFUND = "REPAYMENT_ADJUSTMENT_REFUND";
private final PaymentTypeApi paymentTypeApi;
private final FineractFeignClient fineractClient;
@Override
public void initialize() throws Exception {
List<String> paymentTypes = new ArrayList<>();
paymentTypes.add(PAYMENT_TYPE_AUTOPAY);
paymentTypes.add(PAYMENT_TYPE_DOWN_PAYMENT);
paymentTypes.add(PAYMENT_TYPE_REAL_TIME);
paymentTypes.add(PAYMENT_TYPE_SCHEDULED);
paymentTypes.add(PAYMENT_TYPE_CHECK_PAYMENT);
paymentTypes.add(PAYMENT_TYPE_OCA_PAYMENT);
paymentTypes.add(PAYMENT_TYPE_REPAYMENT_ADJUSTMENT_CHARGEBACK);
paymentTypes.add(PAYMENT_TYPE_REPAYMENT_ADJUSTMENT_REFUND);
public void initialize() {
List<PaymentTypeData> existingPaymentTypes = new ArrayList<>();
try {
existingPaymentTypes = fineractClient.paymentType().getAllPaymentTypesUniversal(Map.of());
} catch (Exception e) {
log.debug("Could not retrieve existing payment types, will create them", e);
}
paymentTypes.forEach(paymentType -> {
Integer position = paymentTypes.indexOf(paymentType) + 2;
PaymentTypeRequest postPaymentTypesRequest = PaymentTypesRequestFactory.defaultPaymentTypeRequest(paymentType, paymentType,
false, position);
final List<PaymentTypeData> paymentTypes = existingPaymentTypes;
try {
paymentTypeApi.createPaymentType(postPaymentTypesRequest).execute();
} catch (IOException e) {
throw new RuntimeException("Error while creating payment type", e);
List<String> paymentTypeNames = new ArrayList<>();
paymentTypeNames.add(PAYMENT_TYPE_AUTOPAY);
paymentTypeNames.add(PAYMENT_TYPE_DOWN_PAYMENT);
paymentTypeNames.add(PAYMENT_TYPE_REAL_TIME);
paymentTypeNames.add(PAYMENT_TYPE_SCHEDULED);
paymentTypeNames.add(PAYMENT_TYPE_CHECK_PAYMENT);
paymentTypeNames.add(PAYMENT_TYPE_OCA_PAYMENT);
paymentTypeNames.add(PAYMENT_TYPE_REPAYMENT_ADJUSTMENT_CHARGEBACK);
paymentTypeNames.add(PAYMENT_TYPE_REPAYMENT_ADJUSTMENT_REFUND);
paymentTypeNames.forEach(paymentTypeName -> {
boolean paymentTypeExists = paymentTypes.stream().anyMatch(pt -> paymentTypeName.equals(pt.getName()));
if (paymentTypeExists) {
return;
}
Integer position = paymentTypeNames.indexOf(paymentTypeName) + 2;
PaymentTypeRequest postPaymentTypesRequest = PaymentTypesRequestFactory.defaultPaymentTypeRequest(paymentTypeName,
paymentTypeName, false, position);
executeVoid(() -> fineractClient.paymentType().createPaymentType(postPaymentTypesRequest, Map.of()));
});
}
}
@@ -26,10 +26,8 @@ import org.springframework.stereotype.Component;
public class SavingsProductGlobalInitializer implements FineractGlobalInitializerStep {
@Override
public void initialize() throws Exception {
/**
* TODO uncomment and check when PS-1088 is done
*/
public void initialize() {
// TODO uncomment and check when PS-1088 is done
// //EUR
// PostSavingsProductsRequest savingsProductsRequestEUR =
// SavingsProductRequestFactory.defaultSavingsProductRequest();
@@ -18,8 +18,11 @@
*/
package org.apache.fineract.test.initializer.global;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.services.SchedulerApi;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@@ -31,10 +34,10 @@ public class SchedulerGlobalInitializerStep implements FineractGlobalInitializer
private static final String SCHEDULER_STATUS_STOP = "stop";
private final SchedulerApi schedulerApi;
private final FineractFeignClient fineractClient;
@Override
public void initialize() throws Exception {
schedulerApi.changeSchedulerStatus(SCHEDULER_STATUS_STOP);
executeVoid(() -> fineractClient.scheduler().changeSchedulerStatus(Map.of("command", SCHEDULER_STATUS_STOP)));
}
}
@@ -34,14 +34,10 @@ public class GlobalConfigurationScenarioInitializerStep implements FineractScena
@Override
public void initializeForScenario() throws Exception {
/**
* Enable-address set to false
*/
// Enable-address set to false
globalConfigurationHelper.disableGlobalConfiguration(CONFIG_KEY_ENABLE_ADDRESS, 0L);
/**
* Enable business date and COB date
*/
// Enable business date and COB date
globalConfigurationHelper.enableGlobalConfiguration(CONFIG_KEY_ENABLE_BUSINESS_DATE, 0L);
globalConfigurationHelper.enableGlobalConfiguration(CONFIG_KEY_ENABLE_RECALCULATE_COB_DATE, 0L);
}
@@ -18,36 +18,52 @@
*/
package org.apache.fineract.test.initializer.suite;
import static java.lang.System.lineSeparator;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.awaitility.Awaitility.await;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.ExternalEventConfigurationItemResponse;
import org.apache.fineract.client.models.ExternalEventConfigurationResponse;
import org.apache.fineract.client.models.ExternalEventConfigurationUpdateRequest;
import org.apache.fineract.client.services.ExternalEventConfigurationApi;
import org.apache.fineract.test.messaging.config.EventProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.config.JmsListenerEndpointRegistry;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Slf4j
@RequiredArgsConstructor
@Component
public class ExternalEventSuiteInitializerStep implements FineractSuiteInitializerStep {
private final ExternalEventConfigurationApi eventConfigurationApi;
private static final Duration JMS_STARTUP_TIMEOUT = Duration.ofSeconds(30);
private final FineractFeignClient fineractClient;
@Autowired(required = false)
private JmsListenerEndpointRegistry registry;
@Autowired(required = false)
private EventProperties eventProperties;
@Override
public void initializeForSuite() throws Exception {
public void initializeForSuite() throws InterruptedException {
log.info("=== ExternalEventSuiteInitializerStep.initializeForSuite() - START ===");
// Step 1: Enable all external events
Map<String, Boolean> eventConfigMap = new HashMap<>();
Response<ExternalEventConfigurationResponse> response = eventConfigurationApi.getExternalEventConfigurations().execute();
if (!response.isSuccessful()) {
String responseBody = response.errorBody().string();
throw new RuntimeException("Cannot configure external events due to " + lineSeparator() + responseBody);
}
ExternalEventConfigurationResponse response = ok(
() -> fineractClient.externalEventConfiguration().getExternalEventConfigurations(Map.of()));
List<ExternalEventConfigurationItemResponse> externalEventConfiguration = response.body().getExternalEventConfiguration();
List<ExternalEventConfigurationItemResponse> externalEventConfiguration = response.getExternalEventConfiguration();
externalEventConfiguration.forEach(e -> {
eventConfigMap.put(e.getType(), true);
});
@@ -55,6 +71,28 @@ public class ExternalEventSuiteInitializerStep implements FineractSuiteInitializ
ExternalEventConfigurationUpdateRequest request = new ExternalEventConfigurationUpdateRequest()
.externalEventConfigurations(eventConfigMap);
eventConfigurationApi.updateExternalEventConfigurations("", request).execute();
executeVoid(() -> fineractClient.externalEventConfiguration().updateExternalEventConfigurations(null, request, Map.of()));
log.info("=== External event configuration updated - all events enabled ===");
// Step 2: Wait for JMS Listener to be ready before proceeding
if (eventProperties != null && eventProperties.isEventVerificationEnabled()) {
if (registry == null) {
log.warn("=== JmsListenerEndpointRegistry not available - skipping JMS listener readiness check ===");
log.warn("=== This is expected in CI environments where JMS may not be fully initialized during suite setup ===");
} else {
log.info("=== Waiting for JMS Listener to connect to ActiveMQ (max {}s) ===", JMS_STARTUP_TIMEOUT.toSeconds());
DefaultMessageListenerContainer container = (DefaultMessageListenerContainer) registry
.getListenerContainer("eventStoreListener");
if (container == null) {
log.warn("=== JMS Listener container 'eventStoreListener' not found - event verification may not work ===");
} else {
await().atMost(JMS_STARTUP_TIMEOUT).pollInterval(Duration.ofMillis(200)).until(container::isRunning);
log.info("=== JMS Listener is running and ready to receive events ===");
}
}
}
log.info("=== ExternalEventSuiteInitializerStep.initializeForSuite() - COMPLETED ===");
}
}
@@ -18,38 +18,115 @@
*/
package org.apache.fineract.test.initializer.suite;
import java.io.IOException;
import lombok.RequiredArgsConstructor;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.ExecuteJobRequest;
import org.apache.fineract.client.models.GetJobsResponse;
import org.apache.fineract.client.models.PutJobsJobIDRequest;
import org.apache.fineract.client.services.SchedulerJobApi;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor
public class JobSuiteInitializerStep implements FineractSuiteInitializerStep {
public static final String SEND_ASYNCHRONOUS_EVENTS_JOB_NAME = "Send Asynchronous Events";
public static final String EVERY_1_SECONDS = "0/1 * * * * ?";
public static final String EVERY_60_SECONDS = "0 0/1 * * * ?";
private final SchedulerJobApi jobApi;
private final FineractFeignClient fineractClient;
@Override
public void initializeForSuite() throws Exception {
updateExternalEventJobFrequency(EVERY_1_SECONDS);
public JobSuiteInitializerStep(FineractFeignClient fineractClient) {
log.info("=== JobSuiteInitializerStep: Constructor called - bean is being created ===");
this.fineractClient = fineractClient;
log.info("=== JobSuiteInitializerStep: FineractFeignClient injected successfully ===");
}
@Override
public void resetAfterSuite() throws Exception {
public void initializeForSuite() throws InterruptedException {
log.info("=== JobSuiteInitializerStep.initializeForSuite() - START ===");
enableAndExecuteEventJob();
log.info("=== JobSuiteInitializerStep.initializeForSuite() - COMPLETED successfully ===");
}
private void enableAndExecuteEventJob() throws InterruptedException {
log.info("=== Initializing Send Asynchronous Events job ===");
Long jobId = updateExternalEventJobFrequency(EVERY_1_SECONDS);
log.info("=== Updated cron expression to EVERY_1_SECONDS ===");
// CRITICAL: SchedulerGlobalInitializerStep stops the scheduler globally
// Solution: START the scheduler so the job runs every 1 second automatically
log.info("Starting scheduler to enable automatic job execution every 1 second...");
executeVoid(() -> fineractClient.scheduler().changeSchedulerStatus("start", Map.of()));
log.info("Scheduler started successfully");
// Manually execute once immediately to publish any queued events from initialization
log.info("Manually executing '{}' job once to publish queued events...", SEND_ASYNCHRONOUS_EVENTS_JOB_NAME);
executeVoid(() -> fineractClient.schedulerJob().executeJob(jobId, new ExecuteJobRequest(), Map.of("command", "executeJob")));
// Poll job history to confirm it ran
log.info("Polling job history to confirm initial execution...");
Long initialRunCount = getJobRunCount(jobId);
log.info("Initial job run count: {}", initialRunCount);
boolean jobRan = false;
for (int i = 0; i < 30; i++) {
Thread.sleep(200);
Long currentRunCount = getJobRunCount(jobId);
if (currentRunCount > initialRunCount) {
log.info("Job execution confirmed! Run count increased from {} to {}", initialRunCount, currentRunCount);
jobRan = true;
break;
}
}
if (!jobRan) {
log.warn("WARNING: Job execution could not be confirmed via history polling");
}
// Wait for events to propagate to ActiveMQ
log.info("Waiting 1 second for event propagation to ActiveMQ...");
Thread.sleep(1000);
log.info("Scheduler is now running - job will execute every 1 second automatically");
}
private Long getJobRunCount(Long jobId) {
try {
var history = ok(() -> fineractClient.schedulerJob().retrieveHistory(jobId, Map.of()));
return (long) history.getTotalFilteredRecords();
} catch (Exception e) {
log.warn("Failed to retrieve job history: {}", e.getMessage());
return 0L;
}
}
@Override
public void resetAfterSuite() {
log.info("=== JobSuiteInitializerStep.resetAfterSuite() - START ===");
// Stop the scheduler to prevent jobs from running between test suites
log.info("Stopping scheduler...");
try {
executeVoid(() -> fineractClient.scheduler().changeSchedulerStatus(Map.of("command", "stop")));
log.info("Scheduler stopped successfully");
} catch (Exception e) {
log.warn("Failed to stop scheduler: {}", e.getMessage());
}
// Reset cron expression to default
updateExternalEventJobFrequency(EVERY_60_SECONDS);
log.info("=== JobSuiteInitializerStep.resetAfterSuite() - COMPLETED ===");
}
private void updateExternalEventJobFrequency(String cronExpression) throws IOException {
GetJobsResponse externalEventJobResponse = jobApi.retrieveAll8().execute().body().stream()
private Long updateExternalEventJobFrequency(String cronExpression) {
GetJobsResponse externalEventJobResponse = ok(() -> fineractClient.schedulerJob().retrieveAll8()).stream()
.filter(r -> r.getDisplayName().equals(SEND_ASYNCHRONOUS_EVENTS_JOB_NAME)).findAny()
.orElseThrow(() -> new IllegalStateException(SEND_ASYNCHRONOUS_EVENTS_JOB_NAME + " is not found"));
Long jobId = externalEventJobResponse.getJobId();
jobApi.updateJobDetail(jobId, new PutJobsJobIDRequest().cronExpression(cronExpression)).execute();
executeVoid(() -> fineractClient.schedulerJob().updateJobDetail(jobId, new PutJobsJobIDRequest().cronExpression(cronExpression)));
return jobId;
}
}
@@ -18,14 +18,15 @@
*/
package org.apache.fineract.test.messaging.event;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.apache.fineract.test.stepdef.loan.LoanRepaymentStepDef.DATE_FORMAT;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.avro.client.v1.ClientDataV1;
@@ -36,6 +37,7 @@ import org.apache.fineract.avro.loan.v1.LoanInstallmentDelinquencyBucketDataV1;
import org.apache.fineract.avro.loan.v1.LoanOwnershipTransferDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionAdjustmentDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.ExternalTransferData;
import org.apache.fineract.client.models.GetClientsClientIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdDelinquencyPausePeriod;
@@ -47,9 +49,6 @@ import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.ClientApi;
import org.apache.fineract.client.services.ExternalAssetOwnersApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.test.data.AssetExternalizationTransferStatus;
import org.apache.fineract.test.data.AssetExternalizationTransferStatusReason;
import org.apache.fineract.test.data.TransactionType;
@@ -83,7 +82,6 @@ import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransaction
import org.apache.fineract.test.messaging.event.loan.transaction.LoanUndoContractTerminationBusinessEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Slf4j
@Component
@@ -91,103 +89,85 @@ import retrofit2.Response;
public class EventCheckHelper {
private static final DateTimeFormatter FORMATTER_EVENTS = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final long TRANSACTION_COMMIT_DELAY_MS = 500L;
@Autowired
private ClientApi clientApi;
@Autowired
private LoansApi loansApi;
private FineractFeignClient fineractClient;
@Autowired
private EventAssertion eventAssertion;
@Autowired
private ExternalAssetOwnersApi externalAssetOwnersApi;
@Autowired
private GlobalConfigurationHelper configurationHelper;
@Autowired
private org.apache.fineract.test.messaging.config.EventProperties eventProperties;
public void clientEventCheck(Response<PostClientsResponse> clientCreationResponse) throws IOException {
Response<GetClientsClientIdResponse> clientDetails = clientApi.retrieveOne11(clientCreationResponse.body().getClientId(), false)
.execute();
private void waitForTransactionCommit() {
if (eventProperties.isEventVerificationEnabled() && TRANSACTION_COMMIT_DELAY_MS > 0) {
try {
Thread.sleep(TRANSACTION_COMMIT_DELAY_MS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted while waiting for transaction commit", e);
}
}
}
public void clientEventCheck(PostClientsResponse clientCreationResponse) {
waitForTransactionCommit();
GetClientsClientIdResponse body = ok(() -> fineractClient.clients().retrieveOne11(clientCreationResponse.getClientId(),
Map.of("staffInSelectedOfficeOnly", false)));
GetClientsClientIdResponse body = clientDetails.body();
Long clientId = Long.valueOf(body.getId());
Integer status = body.getStatus().getId().intValue();
String firstname = body.getFirstname();
String lastname = body.getLastname();
Boolean active = body.getActive();
eventAssertion.assertEvent(ClientCreatedEvent.class, clientCreationResponse.body().getClientId())//
eventAssertion.assertEvent(ClientCreatedEvent.class, clientCreationResponse.getClientId())//
.extractingData(ClientDataV1::getId).isEqualTo(clientId)//
.extractingData(clientDataV1 -> clientDataV1.getStatus().getId()).isEqualTo(status)//
.extractingData(ClientDataV1::getFirstname).isEqualTo(firstname)//
.extractingData(ClientDataV1::getLastname).isEqualTo(lastname)//
.extractingData(ClientDataV1::getActive).isEqualTo(active);//
eventAssertion.assertEvent(ClientActivatedEvent.class, clientCreationResponse.body().getClientId())//
eventAssertion.assertEvent(ClientActivatedEvent.class, clientCreationResponse.getClientId())//
.extractingData(ClientDataV1::getActive).isEqualTo(true)//
.extractingData(clientDataV1 -> clientDataV1.getStatus().getId()).isEqualTo(status);//
}
public void createLoanEventCheck(Response<PostLoansResponse> createLoanResponse) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(createLoanResponse.body().getLoanId(), false, "all", "", "")
.execute();
GetLoansLoanIdResponse body = loanDetails.body();
eventAssertion.assertEvent(LoanCreatedEvent.class, createLoanResponse.body().getLoanId())//
.extractingData(LoanAccountDataV1::getId).isEqualTo(body.getId())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getId()).isEqualTo(body.getStatus().getId())//
.extractingData(LoanAccountDataV1::getClientId).isEqualTo(body.getClientId())//
.extractingBigDecimal(LoanAccountDataV1::getPrincipal).isEqualTo(body.getPrincipal())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getSummary().getCurrency().getCode())
.isEqualTo(body.getCurrency().getCode());//
}
public void approveLoanEventCheck(Response<PostLoansLoanIdResponse> loanApproveResponse) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanApproveResponse.body().getLoanId(), false, "", "", "")
.execute();
GetLoansLoanIdResponse body = loanDetails.body();
eventAssertion.assertEvent(LoanApprovedEvent.class, loanApproveResponse.body().getLoanId())//
.extractingData(LoanAccountDataV1::getId).isEqualTo(body.getId())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getId()).isEqualTo(body.getStatus().getId())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getCode()).isEqualTo(body.getStatus().getCode())//
.extractingData(LoanAccountDataV1::getClientId).isEqualTo(Long.valueOf(body.getClientId()))//
.extractingBigDecimal(LoanAccountDataV1::getApprovedPrincipal).isEqualTo(body.getApprovedPrincipal())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getTimeline().getApprovedOnDate())//
.isEqualTo(FORMATTER_EVENTS.format(body.getTimeline().getApprovedOnDate()))//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getSummary().getCurrency().getCode())
.isEqualTo(body.getCurrency().getCode());//
}
public void undoApproveLoanEventCheck(Response<PostLoansLoanIdResponse> loanUndoApproveResponse) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanUndoApproveResponse.body().getLoanId(), false, "", "", "")
.execute();
GetLoansLoanIdResponse body = loanDetails.body();
public void undoApproveLoanEventCheck(PostLoansLoanIdResponse loanUndoApproveResponse) {
waitForTransactionCommit();
GetLoansLoanIdResponse body = ok(() -> fineractClient.loans().retrieveLoan(loanUndoApproveResponse.getLoanId(),
Map.of("staffInSelectedOfficeOnly", false, "associations", "", "exclude", "", "fields", "")));
eventAssertion.assertEventRaised(LoanUndoApprovalEvent.class, body.getId());
}
public void loanRejectedEventCheck(Response<PostLoansLoanIdResponse> loanRejectedResponse) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanRejectedResponse.body().getLoanId(), false, "", "", "")
.execute();
GetLoansLoanIdResponse body = loanDetails.body();
public void loanRejectedEventCheck(PostLoansLoanIdResponse loanRejectedResponse) {
waitForTransactionCommit();
GetLoansLoanIdResponse body = ok(() -> fineractClient.loans().retrieveLoan(loanRejectedResponse.getLoanId(),
Map.of("staffInSelectedOfficeOnly", false, "associations", "", "exclude", "", "fields", "")));
eventAssertion.assertEventRaised(LoanRejectedEvent.class, body.getId());
}
public void disburseLoanEventCheck(Long loanId) throws IOException {
public void disburseLoanEventCheck(Long loanId) {
waitForTransactionCommit();
loanAccountDataV1Check(LoanDisbursalEvent.class, loanId);
}
public void loanBalanceChangedEventCheck(Long loanId) throws IOException {
public void loanBalanceChangedEventCheck(Long loanId) {
waitForTransactionCommit();
loanAccountDataV1Check(LoanBalanceChangedEvent.class, loanId);
}
public void loanStatusChangedEventCheck(Long loanId) throws IOException {
public void loanStatusChangedEventCheck(Long loanId) {
waitForTransactionCommit();
loanAccountDataV1Check(LoanStatusChangedEvent.class, loanId);
}
private void loanAccountDataV1Check(Class<? extends AbstractLoanEvent> eventClazz, Long loanId) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", "", "").execute();
GetLoansLoanIdResponse body = loanDetails.body();
private void loanAccountDataV1Check(Class<? extends AbstractLoanEvent> eventClazz, Long loanId) {
GetLoansLoanIdResponse body = ok(() -> fineractClient.loans().retrieveLoan(loanId,
Map.of("staffInSelectedOfficeOnly", false, "associations", "all", "exclude", "", "fields", "")));
eventAssertion.assertEvent(eventClazz, loanId)//
.extractingData(loanAccountDataV1 -> {
@@ -254,10 +234,10 @@ public class EventCheckHelper {
return targetTransaction;
}
public GetLoansLoanIdTransactions findNthTransaction(String nthItemStr, String transactionType, String transactionDate, long loanId)
throws IOException {
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
.getTransactions();
public GetLoansLoanIdTransactions findNthTransaction(String nthItemStr, String transactionType, String transactionDate, long loanId) {
GetLoansLoanIdResponse loanResponse = ok(() -> fineractClient.loans().retrieveLoan(loanId,
Map.of("staffInSelectedOfficeOnly", false, "associations", "transactions", "exclude", "", "fields", "")));
List<GetLoansLoanIdTransactions> transactions = loanResponse.getTransactions();
GetLoansLoanIdTransactions targetTransaction = getNthTransactionType(nthItemStr, transactionType, transactionDate, transactions);
return targetTransaction;
}
@@ -276,6 +256,7 @@ public class EventCheckHelper {
}
public void loanUndoContractTerminationEventCheck(final GetLoansLoanIdTransactions transaction) {
waitForTransactionCommit();
eventAssertion.assertEventRaised(LoanUndoContractTerminationBusinessEvent.class, transaction.getId());
}
@@ -286,12 +267,12 @@ public class EventCheckHelper {
return actual.compareTo(expected) == 0;
}
public void loanDisbursalTransactionEventCheck(Response<PostLoansLoanIdResponse> loanDisburseResponse) throws IOException {
Long disbursementTransactionId = loanDisburseResponse.body().getSubResourceId();
public void loanDisbursalTransactionEventCheck(PostLoansLoanIdResponse loanDisburseResponse) {
waitForTransactionCommit();
Long disbursementTransactionId = loanDisburseResponse.getSubResourceId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi
.retrieveLoan(loanDisburseResponse.body().getLoanId(), false, "transactions", "", "").execute();
GetLoansLoanIdResponse body = loanDetails.body();
GetLoansLoanIdResponse body = ok(() -> fineractClient.loans().retrieveLoan(loanDisburseResponse.getLoanId(),
Map.of("staffInSelectedOfficeOnly", false, "associations", "transactions", "exclude", "", "fields", "")));
List<GetLoansLoanIdTransactions> transactions = body.getTransactions();
GetLoansLoanIdTransactions disbursementTransaction = transactions//
.stream()//
@@ -306,12 +287,12 @@ public class EventCheckHelper {
}
public EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> transactionEventCheck(
Response<PostLoansLoanIdTransactionsResponse> transactionResponse, TransactionType transactionType, String externalOwnerId)
throws IOException {
Long loanId = transactionResponse.body().getLoanId();
Long transactionId = transactionResponse.body().getResourceId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
PostLoansLoanIdTransactionsResponse transactionResponse, TransactionType transactionType, String externalOwnerId) {
Long loanId = transactionResponse.getLoanId();
Long transactionId = transactionResponse.getResourceId();
GetLoansLoanIdResponse loanDetailsResponse = ok(() -> fineractClient.loans().retrieveLoan(loanId,
Map.of("staffInSelectedOfficeOnly", false, "associations", "transactions", "exclude", "", "fields", "")));
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
GetLoansLoanIdTransactions transactionFound = transactions//
.stream()//
.filter(t -> t.getId().equals(transactionId))//
@@ -330,16 +311,17 @@ public class EventCheckHelper {
};
EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> eventBuilder = eventAssertion.assertEvent(eventClass, transactionId);
eventBuilder.extractingData(LoanTransactionDataV1::getLoanId).isEqualTo(loanDetailsResponse.body().getId())//
eventBuilder.extractingData(LoanTransactionDataV1::getLoanId).isEqualTo(loanDetailsResponse.getId())//
.extractingData(LoanTransactionDataV1::getDate).isEqualTo(FORMATTER_EVENTS.format(transactionFound.getDate()))//
.extractingBigDecimal(LoanTransactionDataV1::getAmount).isEqualTo(transactionFound.getAmount())//
.extractingData(LoanTransactionDataV1::getExternalOwnerId).isEqualTo(externalOwnerId);//
return eventBuilder;
}
public void loanOwnershipTransferBusinessEventCheck(Long loanId, Long transferId) throws IOException {
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute();
List<ExternalTransferData> content = response.body().getContent();
public void loanOwnershipTransferBusinessEventCheck(Long loanId, Long transferId) {
waitForTransactionCommit();
PageExternalTransferData response = ok(() -> fineractClient.externalAssetOwners().getTransfers(Map.of("loanId", loanId)));
List<ExternalTransferData> content = response.getContent();
ExternalTransferData filtered = content.stream().filter(t -> transferId.equals(t.getTransferId())).reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException("No element found"));
@@ -368,9 +350,9 @@ public class EventCheckHelper {
}
public void loanOwnershipTransferBusinessEventWithStatusCheck(Long loanId, Long transferId, String transferStatus,
String transferStatusReason) throws IOException {
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute();
List<ExternalTransferData> content = response.body().getContent();
String transferStatusReason) {
PageExternalTransferData response = ok(() -> fineractClient.externalAssetOwners().getTransfers(Map.of("loanId", loanId)));
List<ExternalTransferData> content = response.getContent();
ExternalTransferData filtered = content.stream().filter(t -> transferId.equals(t.getTransferId())).reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException("No element found"));
@@ -414,9 +396,9 @@ public class EventCheckHelper {
}
public void loanOwnershipTransferBusinessEventWithTypeCheck(Long loanId, ExternalTransferData transferData, String transferType,
String previousAssetOwner) throws IOException {
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute();
List<ExternalTransferData> content = response.body().getContent();
String previousAssetOwner) {
PageExternalTransferData response = ok(() -> fineractClient.externalAssetOwners().getTransfers(Map.of("loanId", loanId)));
List<ExternalTransferData> content = response.getContent();
Long transferId = transferData.getTransferId();
String assetOwner = transferData.getOwner() == null ? null : transferData.getOwner().getExternalId();
@@ -454,9 +436,10 @@ public class EventCheckHelper {
.extractingData(LoanOwnershipTransferDataV1::getPreviousOwnerExternalId).isEqualTo(previousAssetOwner);
}
public void loanAccountSnapshotBusinessEventCheck(Long loanId, Long transferId) throws IOException {
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute();
List<ExternalTransferData> content = response.body().getContent();
public void loanAccountSnapshotBusinessEventCheck(Long loanId, Long transferId) {
waitForTransactionCommit();
PageExternalTransferData response = ok(() -> fineractClient.externalAssetOwners().getTransfers(Map.of("loanId", loanId)));
List<ExternalTransferData> content = response.getContent();
ExternalTransferData filtered = content.stream().filter(t -> transferId.equals(t.getTransferId())).reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException("No element found"));
@@ -467,9 +450,10 @@ public class EventCheckHelper {
GlobalConfigurationPropertyData outstandingInterestStrategy = configurationHelper
.getGlobalConfiguration("outstanding-interest-calculation-strategy-for-external-asset-transfer");
if ("PAYABLE_OUTSTANDING_INTEREST".equals(outstandingInterestStrategy.getStringValue())) {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", null, null).execute();
totalOutstandingBalanceAmountExpected = zeroConversion(loanDetails.body().getSummary().getTotalOutstanding());
outstandingInterestPortionExpected = zeroConversion(loanDetails.body().getSummary().getInterestOutstanding());
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId,
Map.of("staffInSelectedOfficeOnly", false, "associations", "all", "exclude", "", "fields", "")));
totalOutstandingBalanceAmountExpected = zeroConversion(loanDetails.getSummary().getTotalOutstanding());
outstandingInterestPortionExpected = zeroConversion(loanDetails.getSummary().getInterestOutstanding());
}
String ownerExternalIdExpected = filtered.getStatus().getValue().equals("BUYBACK") ? null : filtered.getOwner().getExternalId();
@@ -498,10 +482,11 @@ public class EventCheckHelper {
.isEqualTo(overPaymentPortionExpected);
}
public void loanAccountDelinquencyPauseChangedBusinessEventCheck(Long loanId) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", "", "").execute();
List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriodsActual = loanDetails.body().getDelinquent()
.getDelinquencyPausePeriods();
public void loanAccountDelinquencyPauseChangedBusinessEventCheck(Long loanId) {
waitForTransactionCommit();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId,
Map.of("staffInSelectedOfficeOnly", false, "associations", "all", "exclude", "", "fields", "")));
List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriodsActual = loanDetails.getDelinquent().getDelinquencyPausePeriods();
eventAssertion.assertEvent(LoanDelinquencyPauseChangedEvent.class, loanId)//
.extractingData(LoanAccountDataV1::getId).isEqualTo(loanId)//
@@ -539,6 +524,7 @@ public class EventCheckHelper {
}
public void installmentLevelDelinquencyRangeChangeEventCheck(Long loanId) {
waitForTransactionCommit();
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId).extractingData(loanAccountDelinquencyRangeDataV1 -> {
// check if sum of total amounts equal the sum of amount types in installmentDelinquencyBuckets
BigDecimal totalAmountSum = loanAccountDelinquencyRangeDataV1.getInstallmentDelinquencyBuckets().stream()//
@@ -582,4 +568,36 @@ public class EventCheckHelper {
private BigDecimal zeroConversion(BigDecimal input) {
return input.compareTo(new BigDecimal("0.000000")) == 0 ? new BigDecimal(input.toEngineeringString()) : input.setScale(8);
}
public void createLoanEventCheck(PostLoansResponse createLoanResponse) {
waitForTransactionCommit();
GetLoansLoanIdResponse body = ok(() -> fineractClient.loans().retrieveLoan(createLoanResponse.getLoanId(),
Map.of("staffInSelectedOfficeOnly", false, "associations", "all", "exclude", "", "fields", "")));
eventAssertion.assertEvent(LoanCreatedEvent.class, createLoanResponse.getLoanId())//
.extractingData(LoanAccountDataV1::getId).isEqualTo(body.getId())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getId()).isEqualTo(body.getStatus().getId())//
.extractingData(LoanAccountDataV1::getClientId).isEqualTo(body.getClientId())//
.extractingBigDecimal(LoanAccountDataV1::getPrincipal).isEqualTo(body.getPrincipal())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getSummary().getCurrency().getCode())
.isEqualTo(body.getCurrency().getCode());//
}
public void approveLoanEventCheck(PostLoansLoanIdResponse loanApproveResponse) {
waitForTransactionCommit();
GetLoansLoanIdResponse body = ok(() -> fineractClient.loans().retrieveLoan(loanApproveResponse.getLoanId(),
Map.of("staffInSelectedOfficeOnly", false, "associations", "", "exclude", "", "fields", "")));
eventAssertion.assertEvent(LoanApprovedEvent.class, loanApproveResponse.getLoanId())//
.extractingData(LoanAccountDataV1::getId).isEqualTo(body.getId())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getId()).isEqualTo(body.getStatus().getId())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getCode()).isEqualTo(body.getStatus().getCode())//
.extractingData(LoanAccountDataV1::getClientId).isEqualTo(Long.valueOf(body.getClientId()))//
.extractingBigDecimal(LoanAccountDataV1::getApprovedPrincipal).isEqualTo(body.getApprovedPrincipal())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getTimeline().getApprovedOnDate())//
.isEqualTo(FORMATTER_EVENTS.format(body.getTimeline().getApprovedOnDate()))//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getSummary().getCurrency().getCode())
.isEqualTo(body.getCurrency().getCode());//
}
}
@@ -29,7 +29,7 @@ public class MessageConsumer {
private final EventStore eventStore;
@JmsListener(destination = "${fineract-test.messaging.jms.topic-name}")
@JmsListener(id = "eventStoreListener", destination = "${fineract-test.messaging.jms.topic-name}")
public void receiveMessage(ActiveMQBytesMessage message) {
try {
byte[] buffer = new byte[(int) message.getBodyLength()];
@@ -18,23 +18,24 @@
*/
package org.apache.fineract.test.service;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.awaitility.Awaitility.await;
import java.io.IOException;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.ExecuteJobRequest;
import org.apache.fineract.client.models.GetJobsResponse;
import org.apache.fineract.client.services.SchedulerJobApi;
import org.apache.fineract.test.data.job.Job;
import org.apache.fineract.test.data.job.JobResolver;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.messaging.config.JobPollingProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component
@RequiredArgsConstructor
@@ -42,7 +43,7 @@ import retrofit2.Response;
public class JobService {
@Autowired
private SchedulerJobApi schedulerJobApi;
private FineractFeignClient fineractClient;
@Autowired
private JobPollingProperties jobPollingProperties;
@@ -50,13 +51,10 @@ public class JobService {
private final JobResolver jobResolver;
public void execute(Job job) {
try {
Long jobId = jobResolver.resolve(job);
Response<Void> response = schedulerJobApi.executeJob(jobId, "executeJob", new ExecuteJobRequest()).execute();
ErrorHelper.checkSuccessfulApiCall(response);
} catch (IOException e) {
throw new RuntimeException("Exception while executing job %s".formatted(job.getName()), e);
}
Long jobId = jobResolver.resolve(job);
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("command", "executeJob");
executeVoid(() -> fineractClient.schedulerJob().executeJob(jobId, new ExecuteJobRequest(), queryParams));
}
public void executeAndWait(Job job) {
@@ -73,9 +71,8 @@ public class JobService {
.until(() -> {
log.debug("Waiting for job {} to finish", jobName);
Long jobId = jobResolver.resolve(job);
Response<GetJobsResponse> getJobsResponse = schedulerJobApi.retrieveOne5(jobId).execute();
ErrorHelper.checkSuccessfulApiCall(getJobsResponse);
Boolean currentlyRunning = getJobsResponse.body().getCurrentlyRunning();
GetJobsResponse getJobsResponse = ok(() -> fineractClient.schedulerJob().retrieveOne5(jobId));
Boolean currentlyRunning = getJobsResponse.getCurrentlyRunning();
return BooleanUtils.isFalse(currentlyRunning);
});
}
@@ -36,9 +36,9 @@
*/
package org.apache.fineract.test.stepdef.assetexternalization;
import static org.apache.fineract.client.feign.util.FeignCalls.fail;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
@@ -46,10 +46,17 @@ import java.io.IOException;
import java.math.RoundingMode;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.CommandProcessingResult;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.services.ExternalAssetOwnerLoanProductAttributesApi;
import org.apache.fineract.client.feign.services.ExternalAssetOwnersApi;
import org.apache.fineract.client.feign.services.ExternalAssetOwnersApiExtension;
import org.apache.fineract.client.feign.services.LoanProductsApi;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.ExternalAssetOwnerRequest;
import org.apache.fineract.client.models.ExternalOwnerJournalEntryData;
import org.apache.fineract.client.models.ExternalOwnerTransferJournalEntryData;
@@ -63,15 +70,8 @@ import org.apache.fineract.client.models.PostExternalAssetOwnerLoanProductAttrib
import org.apache.fineract.client.models.PostInitiateTransferResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PutExternalAssetOwnerLoanProductAttributeRequest;
import org.apache.fineract.client.services.ExternalAssetOwnerLoanProductAttributesApi;
import org.apache.fineract.client.services.ExternalAssetOwnersApi;
import org.apache.fineract.client.services.LoanProductsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.data.AssetExternalizationErrorMessage;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.helper.Utils;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.EventCheckHelper;
@@ -79,7 +79,6 @@ import org.apache.fineract.test.messaging.event.assetexternalization.LoanOwnersh
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class AssetExternalizationStepDef extends AbstractStepDef {
@@ -91,10 +90,9 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
public static final String TRANSACTION_TYPE_BUYBACK = "buyback";
public static final String TRANSACTION_TYPE_INTERMEDIARY_SALE = "intermediarySale";
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT_ASSET_EXT);
private static final Gson GSON = new JSON().getGson();
@Autowired
private ExternalAssetOwnersApi externalAssetOwnersApi;
private FineractFeignClient fineractFeignClient;
@Autowired
private EventCheckHelper eventCheckHelper;
@@ -102,6 +100,22 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
@Autowired
private EventAssertion eventAssertion;
private ExternalAssetOwnersApi externalAssetOwnersApi() {
return fineractFeignClient.externalAssetOwners();
}
private ExternalAssetOwnersApiExtension externalAssetOwnersApiExtension() {
return fineractFeignClient.externalAssetOwnersExtension();
}
private LoanProductsApi loanProductsApi() {
return fineractFeignClient.loanProducts();
}
private ExternalAssetOwnerLoanProductAttributesApi externalAssetOwnerLoanProductAttributesApi() {
return fineractFeignClient.externalAssetOwnerLoanProductAttributes();
}
@When("Admin makes asset externalization request by Loan ID with unique ownerExternalId, user-generated transferExternalId and the following data:")
public void createAssetExternalizationRequestByLoanIdUserGeneratedExtId(DataTable table) throws IOException {
// if user created transferExternalId previously, it will use that, otherwise create a new one
@@ -128,8 +142,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest();
if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) {
@@ -138,12 +152,11 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute();
PostInitiateTransferResponse response = externalAssetOwnersApi().transferRequestWithLoanId(loanId, request, transferData.get(0),
Collections.emptyMap());
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
ErrorHelper.checkSuccessfulApiCall(response);
response.getResourceExternalId());
} else if ((transferData.get(0).equals(TRANSACTION_TYPE_SALE) || transferData.get(0).equals(TRANSACTION_TYPE_INTERMEDIARY_SALE))) {
String ownerExternalId;
@@ -160,21 +173,18 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute();
PostInitiateTransferResponse response = externalAssetOwnersApi().transferRequestWithLoanId(loanId, request,
Map.of("command", transferData.get(0)));
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
response.getResourceExternalId());
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID, ownerExternalId);
if (transferData.get(0).equals(TRANSACTION_TYPE_INTERMEDIARY_SALE)) {
assertThat(ownerExternalId).isNotNull();
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_PREVIOUS_OWNER_EXTERNAL_ID, ownerExternalId);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_INTERMEDIARY_SALE_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
response.getResourceExternalId());
}
ErrorHelper.checkSuccessfulApiCall(response);
} else {
throw new IllegalStateException(String.format("%s is not supported Asset externalization transaction", transferData.get(0)));
}
@@ -182,8 +192,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
@When("Admin makes asset externalization BUYBACK request with ownerExternalId = null and settlement date {string} by Loan ID with system-generated transferExternalId")
public void createAssetExternalizationBuybackRequestOwnerNullByLoanIdSystemGeneratedExtId(String settlementDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest()//
.settlementDate(settlementDate)//
@@ -192,12 +202,11 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, TRANSACTION_TYPE_BUYBACK).execute();
PostInitiateTransferResponse response = externalAssetOwnersApi().transferRequestWithLoanId(loanId, request,
Map.of("command", TRANSACTION_TYPE_BUYBACK));
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
ErrorHelper.checkSuccessfulApiCall(response);
response.getResourceExternalId());
}
@When("Admin makes asset externalization request by Loan external ID with unique ownerExternalId, user-generated transferExternalId and the following data:")
@@ -221,8 +230,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest();
if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) {
@@ -231,13 +240,11 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanExternalId(loanExternalId, request, transferData.get(0)).execute();
PostInitiateTransferResponse response = externalAssetOwnersApi().transferRequestWithLoanExternalId(loanExternalId, request,
Map.of("command", transferData.get(0)));
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
ErrorHelper.checkSuccessfulApiCall(response);
response.getResourceExternalId());
} else if (transferData.get(0).equals(TRANSACTION_TYPE_SALE)) {
String ownerExternalId = Utils.randomNameGenerator(OWNER_EXTERNAL_ID_PREFIX, 3);
@@ -248,14 +255,12 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanExternalId(loanExternalId, request, transferData.get(0)).execute();
PostInitiateTransferResponse response = externalAssetOwnersApi().transferRequestWithLoanExternalId(loanExternalId, request,
Map.of("command", transferData.get(0)));
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
response.getResourceExternalId());
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID, ownerExternalId);
ErrorHelper.checkSuccessfulApiCall(response);
} else {
throw new IllegalStateException(String.format("%s is not supported Asset externalization transaction", transferData.get(0)));
}
@@ -267,13 +272,12 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
String transferExternalIdExpected = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<PostInitiateTransferResponse> response = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
PostInitiateTransferResponse body = response.body();
Long loanIdActual = body.getSubResourceId();
String transferExternalIdActual = body.getResourceExternalId();
PostInitiateTransferResponse response = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
Long loanIdActual = response.getSubResourceId();
String transferExternalIdActual = response.getResourceExternalId();
log.debug("loanId: {}", loanId);
log.debug("ownerExternalIdStored: {}", ownerExternalIdStored);
@@ -281,7 +285,7 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
log.debug("transferExternalIdActual: {}", transferExternalIdActual);
assertThat(loanIdActual).as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(loanIdActual, loanId)).isEqualTo(loanId);
assertThat(body.getResourceId()).isNotNull();
assertThat(response.getResourceId()).isNotNull();
if (transferExternalIdExpected != null) {
assertThat(transferExternalIdActual)
.as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(transferExternalIdActual, transferExternalIdExpected))
@@ -293,23 +297,20 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
@Then("Fetching Asset externalization details by loan id gives numberOfElements: {int} with correct ownerExternalId and the following data:")
public void checkAssetExternalizationDetailsByLoanId(int numberOfElements, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);
PageExternalTransferData response = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
checkExternalAssetDetails(loanId, null, response, numberOfElements, table);
}
@Then("Fetching Asset externalization details by loan external id gives numberOfElements: {int} with correct ownerExternalId and the following data:")
public void checkAssetExternalizationDetailsByLoanExternalId(int numberOfElements, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, null, loanExternalId, null, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
PageExternalTransferData response = externalAssetOwnersApi().getTransfers(Map.of("loanExternalId", loanExternalId));
checkExternalAssetDetails(null, loanExternalId, response, numberOfElements, table);
}
@@ -317,24 +318,21 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
public void checkAssetExternalizationDetailsByTransferExternalId(int numberOfElements, DataTable table) throws IOException {
String transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(transferExternalId, null, null, null, null)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
PageExternalTransferData response = externalAssetOwnersApi().getTransfers(Map.of("transferExternalId", transferExternalId),
Map.of());
checkExternalAssetDetails(null, null, response, numberOfElements, table);
}
@Then("Asset externalization details has the generated transferExternalId")
public void checkGeneratedTransferExternalId() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<PostInitiateTransferResponse> assetExtResponse = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
String transferExternalIdExpected = assetExtResponse.body().getResourceExternalId();
PostInitiateTransferResponse assetExtResponse = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
String transferExternalIdExpected = assetExtResponse.getResourceExternalId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);
List<ExternalTransferData> content = response.body().getContent();
PageExternalTransferData response = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
List<ExternalTransferData> content = response.getContent();
content.forEach(e -> {
assertThat(e.getTransferExternalId()).as(ErrorMessageHelper
@@ -343,11 +341,10 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
});
}
private void checkExternalAssetDetails(Long loanId, String loanExternalId, Response<PageExternalTransferData> response,
int numberOfElements, DataTable table) {
PageExternalTransferData body = response.body();
Integer numberOfElementsActual = body.getNumberOfElements();
List<ExternalTransferData> content = body.getContent();
private void checkExternalAssetDetails(Long loanId, String loanExternalId, PageExternalTransferData response, int numberOfElements,
DataTable table) {
Integer numberOfElementsActual = response.getNumberOfElements();
List<ExternalTransferData> content = response.getContent();
String transferExternalId;
String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
@@ -457,34 +454,32 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
String transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
PageExternalTransferData transfers = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
String settlementDateOriginal = FORMATTER.format(transfers.getContent().get(0).getSettlementDate());
String errorMessageExpected = String.format(
"This loan cannot be bought back, settlement date is earlier than effective transfer settlement date: %s",
settlementDateOriginal);
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest()//
.settlementDate(transferData.get(1))//
.transferExternalId(transferExternalId)//
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute();
CallFailedRuntimeException exception = fail(
() -> externalAssetOwnersApi().transferRequestWithLoanId(loanId, request, Map.of("command", transferData.get(0))));
PageExternalTransferData transfers = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute().body();
String settlementDateOriginal = FORMATTER.format(transfers.getContent().get(0).getSettlementDate());
String errorMessageExpected = String.format(
"This loan cannot be bought back, settlement date is earlier than effective transfer settlement date: %s",
settlementDateOriginal);
String errorToString = response.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getDeveloperMessage();
int errorCodeActual = response.code();
int errorCodeActual = exception.getStatus();
String errorMessageActual = exception.getDeveloperMessage();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
.contains(errorMessageExpected);
log.debug("ERROR CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual);
@@ -495,8 +490,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest();
if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) {
@@ -519,20 +514,23 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
throw new IllegalStateException(String.format("%s is not supported Asset externalization transaction", transferData.get(0)));
}
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute();
AssetExternalizationErrorMessage errorMsgType = AssetExternalizationErrorMessage.valueOf(errorMessageType);
String errorMessageExpected = errorMsgType.getValue();
String errorToString = response.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getDeveloperMessage();
int errorCodeActual = response.code();
CallFailedRuntimeException exception = fail(
() -> externalAssetOwnersApi().transferRequestWithLoanId(loanId, request, Map.of("command", transferData.get(0))));
int errorCodeActual = exception.getStatus();
String errorMessageActual = exception.getDeveloperMessage();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
if (errorMessageType.equals("INVALID_REQUEST")) {
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.containsAnyOf("Validation errors:", errorMessageExpected);
} else {
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.contains(errorMessageExpected);
}
log.debug("ERROR CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual);
@@ -543,8 +541,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest()//
.settlementDate(transferData.get(0))//
@@ -554,20 +552,23 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, TRANSACTION_TYPE_SALE).execute();
AssetExternalizationErrorMessage errorMsgType = AssetExternalizationErrorMessage.valueOf(errorMessageType);
String errorMessageExpected = errorMsgType.getValue();
String errorToString = response.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getDeveloperMessage();
int errorCodeActual = response.code();
CallFailedRuntimeException exception = fail(
() -> externalAssetOwnersApi().transferRequestWithLoanId(loanId, request, Map.of("command", TRANSACTION_TYPE_SALE)));
int errorCodeActual = exception.getStatus();
String errorMessageActual = exception.getDeveloperMessage();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
if (errorMessageType.equals("INVALID_REQUEST")) {
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.containsAnyOf("Validation errors:", errorMessageExpected);
} else {
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.contains(errorMessageExpected);
}
log.debug("ERROR CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual);
@@ -575,14 +576,14 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
@Then("The latest asset externalization transaction with {string} status has the following TRANSFER Journal entries:")
public void checkJournalEntriesTransaction(String status, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Long lastTransferIdByStatus = getLastTransferIdByStatus(loanId, status);
Response<ExternalOwnerTransferJournalEntryData> journalEntriesOfTransfer = externalAssetOwnersApi
.getJournalEntriesOfTransfer(lastTransferIdByStatus, null, null).execute();
List<JournalEntryData> content = journalEntriesOfTransfer.body().getJournalEntryData().getContent();
ExternalOwnerTransferJournalEntryData journalEntriesOfTransfer = externalAssetOwnersApi()
.getJournalEntriesOfTransfer(lastTransferIdByStatus, Map.of());
List<JournalEntryData> content = journalEntriesOfTransfer.getJournalEntryData().getContent();
List<List<String>> data = table.asLists();
int linesExpected = data.size() - 1;
@@ -603,22 +604,21 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
assertThat(containsExpectedValues)
.as(ErrorMessageHelper.wrongValueInLineInAssetExternalizationJournalEntry(i, actualValuesList, expectedValues))
.isTrue();
int linesActual = journalEntriesOfTransfer.body().getJournalEntryData().getNumberOfElements();
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInAssetExternalizationJournalEntry(linesActual, linesExpected))
.isEqualTo(linesExpected);
}
log.debug("loanId: {}", journalEntriesOfTransfer.body().getTransferData().getLoan().getLoanId());
log.debug("ownerExternalId: {}", journalEntriesOfTransfer.body().getTransferData().getOwner().getExternalId());
int linesActual = journalEntriesOfTransfer.getJournalEntryData().getNumberOfElements();
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInAssetExternalizationJournalEntry(linesActual, linesExpected))
.isEqualTo(linesExpected);
log.debug("loanId: {}", journalEntriesOfTransfer.getTransferData().getLoan().getLoanId());
log.debug("ownerExternalId: {}", journalEntriesOfTransfer.getTransferData().getOwner().getExternalId());
log.debug("transferId: {}", lastTransferIdByStatus);
log.debug("transferExternalId: {}", journalEntriesOfTransfer.body().getTransferData().getTransferExternalId());
log.debug("transferExternalId: {}", journalEntriesOfTransfer.getTransferData().getTransferExternalId());
}
private Long getLastTransferIdByStatus(Long loanId, String status) throws IOException {
Response<PageExternalTransferData> transfersResponse = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null)
.execute();
List<ExternalTransferData> content = transfersResponse.body().getContent();
PageExternalTransferData transfersResponse = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
List<ExternalTransferData> content = transfersResponse.getContent();
ExternalTransferData result = content.stream().filter(t -> status.equals(t.getStatus().getValue()))
.reduce((first, second) -> second)
@@ -628,11 +628,10 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
}
private Long getLastTransferId(Long loanId) throws IOException {
Response<PageExternalTransferData> transfersResponse = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null)
.execute();
List<ExternalTransferData> content = transfersResponse.body().getContent();
PageExternalTransferData transfersResponse = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
List<ExternalTransferData> content = transfersResponse.getContent();
ExternalTransferData result = content.stream().reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException("transfersResponse.body().getContent() is empty"));
.orElseThrow(() -> new IllegalStateException("transfersResponse.getContent() is empty"));
return result.getTransferId();
}
@@ -650,12 +649,11 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
transferExternalId = null;
}
Response<PageExternalTransferData> transfersResponse = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null)
.execute();
List<ExternalTransferData> content = transfersResponse.body().getContent();
PageExternalTransferData transfersResponse = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
List<ExternalTransferData> content = transfersResponse.getContent();
ExternalTransferData result = content.stream().filter(bizEvent -> bizEvent.getTransferExternalId().equals(transferExternalId))
.toList().stream().reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException("transfersResponse.body().getContent() is empty"));
.orElseThrow(() -> new IllegalStateException("transfersResponse.getContent() is empty"));
return result;
}
@@ -664,9 +662,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
public void checkJournalEntriesOwner(DataTable table) throws IOException {
String ownerExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
Response<ExternalOwnerJournalEntryData> journalEntriesOfOwner = externalAssetOwnersApi
.getJournalEntriesOfOwner(ownerExternalId, null, null).execute();
List<JournalEntryData> content = journalEntriesOfOwner.body().getJournalEntryData().getContent();
ExternalOwnerJournalEntryData journalEntriesOfOwner = externalAssetOwnersApi().getJournalEntriesOfOwner(ownerExternalId, Map.of());
List<JournalEntryData> content = journalEntriesOfOwner.getJournalEntryData().getContent();
List<List<String>> data = table.asLists();
int linesExpected = data.size() - 1;
@@ -687,19 +684,19 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
assertThat(containsExpectedValues)
.as(ErrorMessageHelper.wrongValueInLineInAssetExternalizationJournalEntry(i, actualValuesList, expectedValues))
.isTrue();
int linesActual = journalEntriesOfOwner.body().getJournalEntryData().getNumberOfElements();
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInAssetExternalizationJournalEntry(linesActual, linesExpected))
.isEqualTo(linesExpected);
}
log.debug("ownerExternalId: {}", journalEntriesOfOwner.body().getOwnerData().getExternalId());
int linesActual = journalEntriesOfOwner.getJournalEntryData().getNumberOfElements();
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInAssetExternalizationJournalEntry(linesActual, linesExpected))
.isEqualTo(linesExpected);
log.debug("ownerExternalId: {}", journalEntriesOfOwner.getOwnerData().getExternalId());
}
@Then("LoanOwnershipTransferBusinessEvent is created")
public void loanOwnershipTransferBusinessEventCheck() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Long transferId = getLastTransferId(loanId);
eventCheckHelper.loanOwnershipTransferBusinessEventCheck(loanId, transferId);
@@ -707,8 +704,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
@Then("LoanOwnershipTransferBusinessEvent with transfer status: {string} and transfer status reason {string} is created")
public void loanOwnershipTransferBusinessEventCheck(String transferStatus, String transferStatusReason) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Long transferId = getLastTransferId(loanId);
eventCheckHelper.loanOwnershipTransferBusinessEventWithStatusCheck(loanId, transferId, transferStatus, transferStatusReason);
@@ -733,8 +730,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
@Then("LoanOwnershipTransferBusinessEvent with transfer type: {string} and transfer asset owner is created")
public void loanOwnershipTransferBusinessEventCheck(String transferType) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
ExternalTransferData transferData = getLastTransferByTransferType(loanId, transferType);
String previousAssetOwner = getPreviousAssetOwner(transferData, transferType, false);
@@ -743,8 +740,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
@Then("LoanOwnershipTransferBusinessEvent with transfer type: {string} and transfer asset owner based on intermediarySale is created")
public void loanOwnershipTransferBusinessEventCheckBasedOnIntermediarySale(String transferType) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
ExternalTransferData transferData = getLastTransferByTransferType(loanId, transferType);
String previousAssetOwner = getPreviousAssetOwner(transferData, transferType, true);
@@ -758,8 +755,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
@Then("LoanAccountSnapshotBusinessEvent is created")
public void loanAccountSnapshotBusinessEventCheck() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Long transferId = getLastTransferId(loanId);
eventCheckHelper.loanAccountSnapshotBusinessEventCheck(loanId, transferId);
@@ -771,13 +768,12 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
String transferExternalIdExpected = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_PREFIX + "_" + type);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<PostInitiateTransferResponse> response = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
PostInitiateTransferResponse body = response.body();
Long loanIdActual = body.getSubResourceId();
String transferExternalIdActual = body.getResourceExternalId();
PostInitiateTransferResponse response = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
Long loanIdActual = response.getSubResourceId();
String transferExternalIdActual = response.getResourceExternalId();
log.debug("loanId: {}", loanId);
log.debug("ownerExternalIdStored: {}", ownerExternalIdStored);
@@ -785,7 +781,7 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
log.debug("transferExternalIdActual: {}", transferExternalIdActual);
assertThat(loanIdActual).as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(loanIdActual, loanId)).isEqualTo(loanId);
assertThat(body.getResourceId()).isNotNull();
assertThat(response.getResourceId()).isNotNull();
if (transferExternalIdExpected != null) {
assertThat(transferExternalIdActual)
.as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(transferExternalIdActual, transferExternalIdExpected))
@@ -812,37 +808,39 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
public void adminTransactionCommandTheWithType(String command, String type) throws IOException {
String transferExternalId = testContext()
.get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type);
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest().dateFormat(DATE_FORMAT_ASSET_EXT).locale(DEFAULT_LOCALE);
externalAssetOwnersApiExtension().transferRequestWithId1WithBody(transferExternalId, request, Map.of("command", command));
}
@When("Admin send {string} command to the transaction type {string} will throw error")
public void adminTransactionCommandTheWithTypeThrowError(String command, String type) throws IOException {
String transferExternalId = testContext()
.get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type);
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command)
.execute();
ErrorHelper.checkFailedApiCall(response, 403);
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest().dateFormat(DATE_FORMAT_ASSET_EXT).locale(DEFAULT_LOCALE);
CallFailedRuntimeException exception = fail(() -> externalAssetOwnersApiExtension()
.transferRequestWithId1WithBody(transferExternalId, request, Map.of("command", command)));
assertThat(exception.getStatus()).as("Expected status code: 403").isEqualTo(403);
}
@Then("Fetching Asset externalization details by loan id gives numberOfElements: {int} with correct ownerExternalId, ignore transactionExternalId and contain the following data:")
public void checkAssetExternalizationDetailsByLoanIdIgnoreTransactionExternalId(int numberOfElements, DataTable table)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PageExternalTransferData response = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
checkExternalAssetDetailsIgnoreTransferExternalId(loanId, null, response, numberOfElements, table);
}
private void checkExternalAssetDetailsIgnoreTransferExternalId(Long loanId, String loanExternalId,
Response<PageExternalTransferData> response, int numberOfElements, DataTable table) {
PageExternalTransferData body = response.body();
Integer numberOfElementsActual = body.getNumberOfElements();
List<ExternalTransferData> content = body.getContent();
private void checkExternalAssetDetailsIgnoreTransferExternalId(Long loanId, String loanExternalId, PageExternalTransferData response,
int numberOfElements, DataTable table) {
Integer numberOfElementsActual = response.getNumberOfElements();
List<ExternalTransferData> content = response.getContent();
String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
@@ -885,9 +883,12 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
}
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command)
.execute();
ErrorHelper.checkFailedApiCall(response, 403);
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest().dateFormat(DATE_FORMAT_ASSET_EXT).locale(DEFAULT_LOCALE);
CallFailedRuntimeException exception = fail(() -> externalAssetOwnersApiExtension()
.transferRequestWithId1WithBody(transferExternalId, request, Map.of("command", command)));
assertThat(exception.getStatus()).as("Expected status code: 403").isEqualTo(403);
}
@When("Admin send {string} command on {string} transaction")
@@ -899,40 +900,29 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
}
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest().dateFormat(DATE_FORMAT_ASSET_EXT).locale(DEFAULT_LOCALE);
externalAssetOwnersApiExtension().transferRequestWithId1WithBody(transferExternalId, request, Map.of("command", command));
}
@Autowired
private LoansApi loansApi;
@Autowired
private LoanProductsApi loanProductsApi;
@Autowired
private ExternalAssetOwnerLoanProductAttributesApi externalAssetOwnerLoanProductAttributesApi;
@When("Admin set external asset owner loan product attribute {string} value {string} for loan product {string}")
public void setAExternalAssetOwnerLoanProductAttribute(String externalAssetOwnerLoanProductAttributeKey,
String externalAssetOwnerLoanProductAttributeValue, String loanProductName) throws IOException {
Response<List<GetLoanProductsResponse>> loanProducts = loanProductsApi.retrieveAllLoanProducts().execute();
long loanProductId = loanProducts.body().stream().filter(loanProduct -> loanProduct.getName().equals(loanProductName)).findFirst()
List<GetLoanProductsResponse> loanProducts = loanProductsApi().retrieveAllLoanProducts(Map.of());
long loanProductId = loanProducts.stream().filter(loanProduct -> loanProduct.getName().equals(loanProductName)).findFirst()
.orElseThrow(() -> new RuntimeException("No loan product is found!")).getId();
Response<PageExternalTransferLoanProductAttributesData> getExternalAssetOwnerLoanProductAttribute = externalAssetOwnerLoanProductAttributesApi
.getExternalAssetOwnerLoanProductAttributes(loanProductId, externalAssetOwnerLoanProductAttributeKey).execute();
PageExternalTransferLoanProductAttributesData getExternalAssetOwnerLoanProductAttributeBody = getExternalAssetOwnerLoanProductAttribute
.body();
PageExternalTransferLoanProductAttributesData getExternalAssetOwnerLoanProductAttribute = externalAssetOwnerLoanProductAttributesApi()
.getExternalAssetOwnerLoanProductAttributes(loanProductId,
Map.of("attributeKey", externalAssetOwnerLoanProductAttributeKey));
if (getExternalAssetOwnerLoanProductAttributeBody.getTotalFilteredRecords() == 0) {
if (getExternalAssetOwnerLoanProductAttribute.getTotalFilteredRecords() == 0) {
PostExternalAssetOwnerLoanProductAttributeRequest setLoanProductAttributeRequest = new PostExternalAssetOwnerLoanProductAttributeRequest()
.attributeKey(externalAssetOwnerLoanProductAttributeKey).attributeValue(externalAssetOwnerLoanProductAttributeValue);
Response<CommandProcessingResult> updateLoanProductAttributerEsponse = externalAssetOwnerLoanProductAttributesApi
.postExternalAssetOwnerLoanProductAttribute(loanProductId, setLoanProductAttributeRequest).execute();
ErrorHelper.checkSuccessfulApiCall(updateLoanProductAttributerEsponse);
externalAssetOwnerLoanProductAttributesApi().postExternalAssetOwnerLoanProductAttribute(loanProductId,
setLoanProductAttributeRequest);
} else {
List<ExternalTransferLoanProductAttributesData> attributes = getExternalAssetOwnerLoanProductAttributeBody.getPageItems();
List<ExternalTransferLoanProductAttributesData> attributes = getExternalAssetOwnerLoanProductAttribute.getPageItems();
assert attributes != null;
long attributeId = attributes.stream()
.filter(attribute -> attribute.getAttributeKey().equals(externalAssetOwnerLoanProductAttributeKey)).findFirst()
@@ -941,9 +931,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.getAttributeId();
PutExternalAssetOwnerLoanProductAttributeRequest setLoanProductAttributeRequest = new PutExternalAssetOwnerLoanProductAttributeRequest()
.attributeKey(externalAssetOwnerLoanProductAttributeKey).attributeValue(externalAssetOwnerLoanProductAttributeValue);
Response<CommandProcessingResult> updateLoanProductAttributerEsponse = externalAssetOwnerLoanProductAttributesApi
.updateLoanProductAttribute(loanProductId, attributeId, setLoanProductAttributeRequest).execute();
ErrorHelper.checkSuccessfulApiCall(updateLoanProductAttributerEsponse);
externalAssetOwnerLoanProductAttributesApi().updateLoanProductAttribute(loanProductId, attributeId,
setLoanProductAttributeRequest);
}
}
@@ -18,16 +18,15 @@
*/
package org.apache.fineract.test.stepdef.common;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.apache.fineract.test.stepdef.datatable.DatatablesStepDef.DATATABLE_NAME;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
@@ -40,16 +39,19 @@ import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.fineract.avro.loan.v1.LoanSchedulePeriodDataV1;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.services.BatchApiApi;
import org.apache.fineract.client.feign.services.ClientApi;
import org.apache.fineract.client.feign.services.LoansApi;
import org.apache.fineract.client.models.BatchRequest;
import org.apache.fineract.client.models.BatchResponse;
import org.apache.fineract.client.models.GetClientsClientIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdStatus;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.GetUsersUserIdResponse;
import org.apache.fineract.client.models.Header;
import org.apache.fineract.client.models.InlineJobRequest;
import org.apache.fineract.client.models.PostClientsRequest;
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest;
@@ -59,19 +61,12 @@ import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUpdateRescheduleLoansRequest;
import org.apache.fineract.client.models.PostUsersResponse;
import org.apache.fineract.client.services.BatchApiApi;
import org.apache.fineract.client.services.ClientApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.services.UsersApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.data.ChargeProductType;
import org.apache.fineract.test.data.LoanRescheduleErrorMessage;
import org.apache.fineract.test.data.LoanStatus;
import org.apache.fineract.test.data.TransactionType;
import org.apache.fineract.test.factory.ClientRequestFactory;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.helper.Utils;
@@ -80,12 +75,12 @@ import org.apache.fineract.test.messaging.event.loan.LoanRescheduledDueAdjustSch
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class BatchApiStepDef extends AbstractStepDef {
private static final Gson GSON = new JSON().getGson();
private static final com.fasterxml.jackson.databind.ObjectMapper OBJECT_MAPPER = org.apache.fineract.client.feign.ObjectMapperFactory
.getShared();
private static final String DATE_FORMAT = "dd MMMM yyyy";
private static final String DEFAULT_LOCALE = "en";
private static final Long BATCH_API_SAMPLE_REQUEST_ID_1 = 1L;
@@ -121,13 +116,7 @@ public class BatchApiStepDef extends AbstractStepDef {
private static final String PWD_USER_WITH_ROLE = "1234567890Aa!";
@Autowired
private BatchApiApi batchApiApi;
@Autowired
private LoansApi loansApi;
@Autowired
private ClientApi clientApi;
private FineractFeignClient fineractFeignClient;
@Autowired
private ClientRequestFactory clientRequestFactory;
@@ -138,8 +127,17 @@ public class BatchApiStepDef extends AbstractStepDef {
@Autowired
private LoanRequestFactory loanRequestFactory;
@Autowired
private UsersApi usersApi;
private BatchApiApi batchApiApi() {
return fineractFeignClient.batch();
}
private LoansApi loansApi() {
return fineractFeignClient.loans();
}
private ClientApi clientApi() {
return fineractFeignClient.clients();
}
@When("Batch API sample call ran")
public void runSampleBatchApiCall() throws IOException {
@@ -149,7 +147,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 1 - create client
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest();
String bodyClientsRequest = GSON.toJson(clientsRequest);
String bodyClientsRequest = toJson(clientsRequest);
BatchRequest batchRequest1 = new BatchRequest();
batchRequest1.requestId(BATCH_API_SAMPLE_REQUEST_ID_1);
@@ -160,7 +158,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 2 - create Loan
PostLoansRequest loansRequest = loanRequestFactory.defaultLoansRequest(1L);
String bodyLoansRequest = GSON.toJson(loansRequest);
String bodyLoansRequest = toJson(loansRequest);
String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\"");
BatchRequest batchRequest2 = new BatchRequest();
@@ -181,7 +179,7 @@ public class BatchApiStepDef extends AbstractStepDef {
loanIdChargesRequest.dueDate(dateOfCharge);
loanIdChargesRequest.dateFormat(DATE_FORMAT);
loanIdChargesRequest.locale(DEFAULT_LOCALE);
String bodyLoanIdChargesRequest = GSON.toJson(loanIdChargesRequest);
String bodyLoanIdChargesRequest = toJson(loanIdChargesRequest);
BatchRequest batchRequest3 = new BatchRequest();
batchRequest3.requestId(BATCH_API_SAMPLE_REQUEST_ID_3);
@@ -205,7 +203,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(batchRequest2);
requestList.add(batchRequest3);
requestList.add(batchRequest4);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, false).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", false);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
}
@@ -217,7 +217,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 1 - create client
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest();
String bodyClientsRequest = GSON.toJson(clientsRequest);
String bodyClientsRequest = toJson(clientsRequest);
BatchRequest batchRequest1 = new BatchRequest();
batchRequest1.requestId(BATCH_API_SAMPLE_REQUEST_ID_1);
@@ -228,7 +228,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 2 - create Loan
PostLoansRequest loansRequest = loanRequestFactory.defaultLoansRequest(1L);
String bodyLoansRequest = GSON.toJson(loansRequest);
String bodyLoansRequest = toJson(loansRequest);
String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\"");
BatchRequest batchRequest2 = new BatchRequest();
@@ -241,7 +241,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 3 - approve Loan
PostLoansLoanIdRequest loanApproveRequest = LoanRequestFactory.defaultLoanApproveRequest();
String bodyLoanApproveRequest = GSON.toJson(loanApproveRequest);
String bodyLoanApproveRequest = toJson(loanApproveRequest);
BatchRequest batchRequest3 = new BatchRequest();
batchRequest3.requestId(BATCH_API_SAMPLE_REQUEST_ID_3);
@@ -253,7 +253,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 4 - disburse Loan
PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest();
String bodyLoanDisburseRequest = GSON.toJson(loanDisburseRequest);
String bodyLoanDisburseRequest = toJson(loanDisburseRequest);
BatchRequest batchRequest4 = new BatchRequest();
batchRequest4.requestId(BATCH_API_SAMPLE_REQUEST_ID_4);
@@ -265,7 +265,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 5 - repayment with idempotency key
PostLoansLoanIdTransactionsRequest loanRepaymentRequest1 = LoanRequestFactory.defaultRepaymentRequest();
String bodyLoanRepaymentRequest1 = GSON.toJson(loanRepaymentRequest1);
String bodyLoanRepaymentRequest1 = toJson(loanRepaymentRequest1);
String idempotencyKey = UUID.randomUUID().toString();
headers.add(new Header().name("Idempotency-Key").value(idempotencyKey));
@@ -280,7 +280,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 6 - repayment with same idempotency key
PostLoansLoanIdTransactionsRequest loanRepaymentRequest2 = LoanRequestFactory.defaultRepaymentRequest();
String bodyLoanRepaymentRequest2 = GSON.toJson(loanRepaymentRequest2);
String bodyLoanRepaymentRequest2 = toJson(loanRepaymentRequest2);
BatchRequest batchRequest6 = new BatchRequest();
batchRequest6.requestId(BATCH_API_SAMPLE_REQUEST_ID_6);
@@ -297,7 +297,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(batchRequest4);
requestList.add(batchRequest5);
requestList.add(batchRequest6);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, false).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", false);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
}
@@ -315,7 +317,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(getLoanDetailsByExternalId(4L, 2L, idempotencyKey));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", isEnclosingTransaction);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
testContext().set(TestContextKey.BATCH_API_CALL_CLIENT_EXTERNAL_ID, clientExternalId);
@@ -336,7 +340,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(getLoanDetailsByExternalId(4L, 2L, idempotencyKey));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", isEnclosingTransaction);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
testContext().set(TestContextKey.BATCH_API_CALL_CLIENT_EXTERNAL_ID, clientExternalId);
@@ -364,7 +370,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(getLoanDetailsByExternalId(8L, 6L, idempotencyKey2));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", isEnclosingTransaction);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
testContext().set(TestContextKey.BATCH_API_CALL_CLIENT_EXTERNAL_ID, clientExternalId);
@@ -395,7 +403,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(getLoanDetailsByExternalId(8L, 6L, idempotencyKey2));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", isEnclosingTransaction);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
@@ -421,7 +431,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(getLoanDetailsByExternalId(5L, 2L, idempotencyKey));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", isEnclosingTransaction);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
testContext().set(TestContextKey.BATCH_API_CALL_CLIENT_EXTERNAL_ID, clientExternalId);
@@ -433,8 +445,8 @@ public class BatchApiStepDef extends AbstractStepDef {
String approvedOnDate, String enclosingTransaction) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String idempotencyKey = UUID.randomUUID().toString();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.getLoanId();
List<BatchRequest> requestList = new ArrayList<>();
@@ -442,7 +454,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", isEnclosingTransaction);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
eventAssertion.assertEvent(LoanRescheduledDueAdjustScheduleEvent.class, loanId).extractingData(loanAccountDataV1 -> {
@@ -462,19 +476,8 @@ public class BatchApiStepDef extends AbstractStepDef {
String approvedOnDate, String enclosingTransaction) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String idempotencyKey = UUID.randomUUID().toString();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.body().getLoanId();
Map<String, String> headerMap = new HashMap<>();
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + PWD_USER_WITH_ROLE;
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.getLoanId();
List<BatchRequest> requestList = new ArrayList<>();
@@ -482,19 +485,24 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction, headerMap)
.execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", isEnclosingTransaction);
// TODO: Feign doesn't support per-request headers via API signature - need to use RequestInterceptor
// List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams,
// headerMap);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
if (batchResponseList.errorBody() != null) {
log.debug("ERROR: {}", batchResponseList.errorBody().string());
}
if (batchResponseList.body() != null) {
log.debug("Body: {}", batchResponseList.body());
// Feign throws exceptions on errors, no errorBody()
if (batchResponseList != null) {
log.debug("Body: {}", batchResponseList);
}
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
if (Boolean.TRUE.equals(isEnclosingTransaction)) {
InlineJobRequest inlineJobRequest = new InlineJobRequest().addLoanIdsItem(loanId);
ok(() -> fineractFeignClient.inlineJob().executeInlineJob("LOAN_COB", inlineJobRequest));
}
eventAssertion.assertEvent(LoanRescheduledDueAdjustScheduleEvent.class, loanId).extractingData(loanAccountDataV1 -> {
Optional<LoanSchedulePeriodDataV1> period = loanAccountDataV1.getRepaymentSchedule().getPeriods().stream()
.filter(p -> formatter.format(LocalDate.parse(p.getDueDate())).equals(toDateStr)).findFirst();
@@ -511,8 +519,8 @@ public class BatchApiStepDef extends AbstractStepDef {
public void runBatchApiCreateAndApproveLoanRescheduleWithGivenUserLockedByCobError(int errorCodeExpected, String errorMessageType,
DataTable table) throws IOException {
String idempotencyKey = UUID.randomUUID().toString();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.getLoanId();
LoanRescheduleErrorMessage loanRescheduleErrorMessage = LoanRescheduleErrorMessage.valueOf(errorMessageType);
String errorMessageExpected = loanRescheduleErrorMessage.getValue(loanId);
@@ -525,26 +533,24 @@ public class BatchApiStepDef extends AbstractStepDef {
String approvedOnDate = transferData.get(3);
String enclosingTransaction = transferData.get(4);
Map<String, String> headerMap = new HashMap<>();
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + PWD_USER_WITH_ROLE;
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
List<BatchRequest> requestList = new ArrayList<>();
requestList.add(createLoanReschedule(1L, loanId, fromDateStr, toDateStr, submittedOnDate, idempotencyKey, null));
requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction, headerMap)
.execute();
String errorToString = batchResponseList.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", isEnclosingTransaction);
// Feign throws exceptions on errors instead of returning error in response body
ErrorResponse errorResponse = null;
try {
// TODO: Feign doesn't support per-request headers - need to use RequestInterceptor
batchApiApi().handleBatchRequests(requestList, queryParams);
throw new IllegalStateException("Expected Feign exception but call succeeded");
} catch (org.apache.fineract.client.feign.FeignException e) {
errorResponse = fromJson(e.responseBodyAsString(), ErrorResponse.class);
}
String errorMessageActual = errorResponse.getDeveloperMessage();
Integer errorCodeActual = errorResponse.getHttpStatusCode();
@@ -560,8 +566,8 @@ public class BatchApiStepDef extends AbstractStepDef {
public void runBatchApiCreateAndApproveLoanRescheduleWithGivenUserLockedByCobWithoutError(int httpCodeExpected, DataTable table)
throws IOException {
String idempotencyKey = UUID.randomUUID().toString();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.getLoanId();
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
@@ -571,27 +577,18 @@ public class BatchApiStepDef extends AbstractStepDef {
String approvedOnDate = transferData.get(3);
String enclosingTransaction = transferData.get(4);
Map<String, String> headerMap = new HashMap<>();
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + PWD_USER_WITH_ROLE;
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
List<BatchRequest> requestList = new ArrayList<>();
requestList.add(createLoanReschedule(1L, loanId, fromDateStr, toDateStr, submittedOnDate, idempotencyKey, null));
requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction, headerMap)
.execute();
BatchResponse lastBatchResponse = batchResponseList.body().get(batchResponseList.body().size() - 1);
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", isEnclosingTransaction);
// TODO: Feign doesn't support per-request headers - need to use RequestInterceptor
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
BatchResponse lastBatchResponse = batchResponseList.get(batchResponseList.size() - 1);
assertThat(httpCodeExpected).isEqualTo(lastBatchResponse.getStatusCode());
// No error
assertThat(batchResponseList.errorBody()).isEqualTo(null);
// Feign throws exceptions on errors, no errorBody()
}
@When("Batch API call with steps: queryDatatable, updateDatatable runs, with empty queryDatatable response")
@@ -602,7 +599,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(queryDatatable(1L));
requestList.add(updateDatatable(2L, 1L));
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, false).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", false);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
}
@@ -612,7 +611,7 @@ public class BatchApiStepDef extends AbstractStepDef {
PostCreateRescheduleLoansRequest rescheduleLoansRequest = LoanRequestFactory.defaultLoanRescheduleCreateRequest(loanId, fromDateStr,
toDateStr);
rescheduleLoansRequest.setSubmittedOnDate(submittedOnDate);
String bodyLoanRescheduleRequest = GSON.toJson(rescheduleLoansRequest);
String bodyLoanRescheduleRequest = toJson(rescheduleLoansRequest);
Set<Header> headers = new HashSet<>();
headers.add(HEADER);
@@ -633,7 +632,7 @@ public class BatchApiStepDef extends AbstractStepDef {
private BatchRequest approveLoanReschedule(Long requestId, String idempotencyKey, String approvedOnDate, Long referenceId) {
PostUpdateRescheduleLoansRequest rescheduleLoansRequest = LoanRequestFactory.defaultLoanRescheduleUpdateRequest();
rescheduleLoansRequest.setApprovedOnDate(approvedOnDate);
String bodyLoanRescheduleRequest = GSON.toJson(rescheduleLoansRequest);
String bodyLoanRescheduleRequest = toJson(rescheduleLoansRequest);
Set<Header> headers = new HashSet<>();
headers.add(HEADER);
@@ -653,14 +652,17 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Admin checks that all steps result 200OK")
public void adminChecksThatAllStepsResultOK() {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
ErrorHelper.checkSuccessfulBatchApiCall(batchResponseList);
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
// Feign doesn't use Response wrappers, check status codes directly
batchResponseList.forEach(response -> {
assertThat(response.getStatusCode()).as(ErrorMessageHelper.batchRequestFailedWithCode(response)).isEqualTo(200);
});
}
@Then("Verify that step Nr. {int} results {int}")
public void checkGivenStepResult(int nr, int resultStatusCode) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse stepResponse = batchResponseList.body().stream().filter(r -> r.getRequestId() == nr).findAny()
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse stepResponse = batchResponseList.stream().filter(r -> r.getRequestId() == nr).findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Request id %s not found", nr)));
assertThat(stepResponse.getStatusCode()).as(ErrorMessageHelper.wrongStatusCode(stepResponse.getStatusCode(), resultStatusCode))
@@ -669,10 +671,10 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Verify that step {int} throws an error with error code {int}")
public void errorCodeInStep(int step, int errorCode) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse response = batchResponseList.body().stream().filter(r -> r.getRequestId() == step).findAny()
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse response = batchResponseList.stream().filter(r -> r.getRequestId() == step).findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Step %s is not found", step)));
ErrorResponse errorResponse = GSON.fromJson(response.getBody(), ErrorResponse.class);
ErrorResponse errorResponse = fromJson(response.getBody(), ErrorResponse.class);
String developerMessageActual = errorResponse.getDeveloperMessage();
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
@@ -690,14 +692,17 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Admin checks that all steps result 200OK for Batch API idempotency request")
public void adminChecksThatAllStepsResultOKIdempotency() {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
ErrorHelper.checkSuccessfulBatchApiCall(batchResponseList);
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
// Feign doesn't use Response wrappers, check status codes directly
batchResponseList.forEach(response -> {
assertThat(response.getStatusCode()).as(ErrorMessageHelper.batchRequestFailedWithCode(response)).isEqualTo(200);
});
}
@Then("Batch API response has boolean value in header {string}: {string} in segment with requestId {int}")
public void batchAPITransactionHeaderCheckBoolean(String headerKeyExpected, String headerValueExpected, int requestId) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponse = batchResponseList.body().get(requestId - 1);
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponse = batchResponseList.get(requestId - 1);
Set<Header> headers = batchResponse.getHeaders();
List<Header> headersList = new ArrayList<>(Objects.requireNonNull(headers));
@@ -710,8 +715,8 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Batch API response has no {string} field in segment with requestId {int}")
public void batchAPITransactionHeaderCheckNoField(String headerKeyExpected, int requestId) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponse = batchResponseList.body().get(requestId - 1);
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponse = batchResponseList.get(requestId - 1);
Set<Header> headers = batchResponse.getHeaders();
List<Header> headersList = new ArrayList<>(Objects.requireNonNull(headers));
@@ -727,10 +732,10 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Batch API response has {double} EUR value for transaction amount in segment with requestId {int}")
public void batchAPITransactionAmountCheck(double transactionAmountExpected, int requestId) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponse = batchResponseList.body().get(requestId - 1);
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponse = batchResponseList.get(requestId - 1);
PostLoansLoanIdTransactionsResponse loanTransactionResponse = GSON.fromJson(batchResponse.getBody(),
PostLoansLoanIdTransactionsResponse loanTransactionResponse = fromJson(batchResponse.getBody(),
PostLoansLoanIdTransactionsResponse.class);
Double transactionAmountActual = Double
.valueOf(Objects.requireNonNull(Objects.requireNonNull(loanTransactionResponse.getChanges()).getTransactionAmount()));
@@ -742,13 +747,13 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Batch API response has the same clientId and loanId in segment with requestId {int} as in segment with requestId {int}")
public void batchAPIClientIdLoanIdCheck(int requestIdSecondTransaction, int requestIdFirstTransaction) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponseFirstTransaction = batchResponseList.body().get(requestIdFirstTransaction - 1);
BatchResponse batchResponseSecondTransaction = batchResponseList.body().get(requestIdSecondTransaction - 1);
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponseFirstTransaction = batchResponseList.get(requestIdFirstTransaction - 1);
BatchResponse batchResponseSecondTransaction = batchResponseList.get(requestIdSecondTransaction - 1);
PostLoansLoanIdTransactionsResponse loanTransactionResponseFirst = GSON.fromJson(batchResponseFirstTransaction.getBody(),
PostLoansLoanIdTransactionsResponse loanTransactionResponseFirst = fromJson(batchResponseFirstTransaction.getBody(),
PostLoansLoanIdTransactionsResponse.class);
PostLoansLoanIdTransactionsResponse loanTransactionResponseSecond = GSON.fromJson(batchResponseSecondTransaction.getBody(),
PostLoansLoanIdTransactionsResponse loanTransactionResponseSecond = fromJson(batchResponseSecondTransaction.getBody(),
PostLoansLoanIdTransactionsResponse.class);
Long clientIdFirstTransaction = loanTransactionResponseFirst.getClientId();
@@ -767,9 +772,9 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Batch API response has the same idempotency key in segment with requestId {int} as in segment with requestId {int}")
public void batchAPIIdempotencyKeyCheck(int requestIdSecondTransaction, int requestIdFirstTransaction) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponseFirstTransaction = batchResponseList.body().get(requestIdFirstTransaction - 1);
BatchResponse batchResponseSecondTransaction = batchResponseList.body().get(requestIdSecondTransaction - 1);
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponseFirstTransaction = batchResponseList.get(requestIdFirstTransaction - 1);
BatchResponse batchResponseSecondTransaction = batchResponseList.get(requestIdSecondTransaction - 1);
Set<Header> headersFirstTransaction = batchResponseFirstTransaction.getHeaders();
List<Header> headersListFirstTransaction = new ArrayList<>(Objects.requireNonNull(headersFirstTransaction));
@@ -790,15 +795,18 @@ public class BatchApiStepDef extends AbstractStepDef {
TransactionType transactionType = TransactionType.valueOf(transactionTypeInput);
String transactionTypeValue = transactionType.getValue();
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse lastBatchResponse = batchResponseList.body().get(batchResponseList.body().size() - 1);
PostLoansLoanIdTransactionsResponse loanTransactionResponse = GSON.fromJson(lastBatchResponse.getBody(),
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse lastBatchResponse = batchResponseList.get(batchResponseList.size() - 1);
PostLoansLoanIdTransactionsResponse loanTransactionResponse = fromJson(lastBatchResponse.getBody(),
PostLoansLoanIdTransactionsResponse.class);
Long loanId = loanTransactionResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
Map<String, Object> loanQueryParams = new HashMap<>();
loanQueryParams.put("staffInSelectedOfficeOnly", false);
loanQueryParams.put("associations", "transactions");
GetLoansLoanIdResponse loanDetails = loansApi().retrieveLoan(loanId, loanQueryParams);
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions();
List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
List<String> transactionsMatched = new ArrayList<>();
transactions.forEach(t -> {
@@ -827,9 +835,10 @@ public class BatchApiStepDef extends AbstractStepDef {
throw new IllegalStateException(String.format("Nr. %s client external ID not found", nr));
}
Response<GetClientsClientIdResponse> response = clientApi.retrieveOne12(clientExternalId, false).execute();
ErrorHelper.checkSuccessfulApiCall(response);
assertThat(response.body().getId()).as(ErrorMessageHelper.idNull()).isNotNull();
Map<String, Object> clientQueryParams = new HashMap<>();
clientQueryParams.put("staffInSelectedOfficeOnly", false);
GetClientsClientIdResponse response = clientApi().retrieveOne12(clientExternalId, clientQueryParams);
assertThat(response.getId()).as(ErrorMessageHelper.idNull()).isNotNull();
}
@Then("Nr. {int} Loan was created")
@@ -843,9 +852,10 @@ public class BatchApiStepDef extends AbstractStepDef {
throw new IllegalStateException(String.format("Nr. %s loan external ID not found", nr));
}
Response<GetLoansLoanIdResponse> response = loansApi.retrieveLoan1(loanExternalId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(response);
assertThat(response.body().getId()).as(ErrorMessageHelper.idNull()).isNotNull();
Map<String, Object> loanQueryParams = new HashMap<>();
loanQueryParams.put("staffInSelectedOfficeOnly", false);
GetLoansLoanIdResponse response = loansApi().retrieveLoan1(loanExternalId, loanQueryParams);
assertThat(response.getId()).as(ErrorMessageHelper.idNull()).isNotNull();
}
@Then("Nr. {int} Loan was approved")
@@ -859,14 +869,14 @@ public class BatchApiStepDef extends AbstractStepDef {
throw new IllegalStateException(String.format("Nr. %s loan external ID not found", nr));
}
Response<GetLoansLoanIdResponse> response = loansApi.retrieveLoan1(loanExternalId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(response);
GetLoansLoanIdStatus status = response.body().getStatus();
Map<String, Object> loanQueryParams = new HashMap<>();
loanQueryParams.put("staffInSelectedOfficeOnly", false);
GetLoansLoanIdResponse response = loansApi().retrieveLoan1(loanExternalId, loanQueryParams);
GetLoansLoanIdStatus status = response.getStatus();
Integer statusIdActual = status.getId();
Integer statusIdExpected = LoanStatus.APPROVED.value;
String resourceId = String.valueOf(response.body().getId());
String resourceId = String.valueOf(response.getId());
assertThat(statusIdActual).as(ErrorMessageHelper.wrongLoanStatus(resourceId, statusIdActual, statusIdExpected))
.isEqualTo(statusIdExpected);
}
@@ -882,8 +892,16 @@ public class BatchApiStepDef extends AbstractStepDef {
throw new IllegalStateException(String.format("Nr. %s client external id mot found", nr));
}
Response<GetClientsClientIdResponse> response = clientApi.retrieveOne12(clientExternalId, false).execute();
ErrorResponse errorResponse = GSON.fromJson(response.errorBody().string(), ErrorResponse.class);
// Feign throws exceptions on errors instead of returning error in response body
ErrorResponse errorResponse = null;
try {
Map<String, Object> clientQueryParams = new HashMap<>();
clientQueryParams.put("staffInSelectedOfficeOnly", false);
clientApi().retrieveOne12(clientExternalId, clientQueryParams);
throw new IllegalStateException("Expected Feign exception but call succeeded");
} catch (org.apache.fineract.client.feign.FeignException e) {
errorResponse = fromJson(e.responseBodyAsString(), ErrorResponse.class);
}
String developerMessageActual = errorResponse.getDeveloperMessage();
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
String errorsDeveloperMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
@@ -912,9 +930,17 @@ public class BatchApiStepDef extends AbstractStepDef {
throw new IllegalStateException(String.format("Nr. %s loan external id mot found", nr));
}
Response<GetLoansLoanIdResponse> response = loansApi.retrieveLoan1(loanExternalId, false, "", "", "").execute();
Map<String, Object> loanQueryParams = new HashMap<>();
loanQueryParams.put("staffInSelectedOfficeOnly", false);
ErrorResponse errorResponse = GSON.fromJson(response.errorBody().string(), ErrorResponse.class);
// Feign throws exceptions on errors instead of returning error in response body
ErrorResponse errorResponse = null;
try {
loansApi().retrieveLoan1(loanExternalId, loanQueryParams);
throw new IllegalStateException("Expected Feign exception but call succeeded");
} catch (org.apache.fineract.client.feign.FeignException e) {
errorResponse = fromJson(e.responseBodyAsString(), ErrorResponse.class);
}
String developerMessageActual = errorResponse.getDeveloperMessage();
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
String errorsDeveloperMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
@@ -934,20 +960,22 @@ public class BatchApiStepDef extends AbstractStepDef {
@When("Admin runs Batch API call with chargeOff command on {string}")
public void runBatchApiCallWithChargeOffCommand(String chargeOffDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
String idempotencyKey = UUID.randomUUID().toString();
List<BatchRequest> requestList = new ArrayList<>();
requestList.add(createChargeOffRequest(1L, loanId, idempotencyKey, chargeOffDate));
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, false).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", false);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
if (batchResponseList.isSuccessful() && batchResponseList.body() != null && !batchResponseList.body().isEmpty()) {
BatchResponse response = batchResponseList.body().get(0);
if (batchResponseList != null && !batchResponseList.isEmpty()) {
BatchResponse response = batchResponseList.get(0);
log.debug("Batch charge-off API status code: {}", response.getStatusCode());
log.debug("Batch charge-off API response body: {}", response.getBody());
} else {
@@ -974,7 +1002,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// Disburse loan
PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest();
String bodyLoanDisburseRequest = GSON.toJson(loanDisburseRequest);
String bodyLoanDisburseRequest = toJson(loanDisburseRequest);
BatchRequest disburseRequest = new BatchRequest();
disburseRequest.requestId(4L);
disburseRequest.relativeUrl("loans/external-id/$.resourceExternalId?command=disburse");
@@ -990,16 +1018,18 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(applyInterestPauseByExternalId(5L, 2L, idempotencyKey, startDate, endDate));
// Execute batch request
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, true).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", true);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
testContext().set(TestContextKey.BATCH_API_CALL_CLIENT_EXTERNAL_ID, clientExternalId);
testContext().set(TestContextKey.BATCH_API_CALL_LOAN_EXTERNAL_ID, loanExternalId);
// Log response for debugging
if (batchResponseList.isSuccessful() && batchResponseList.body() != null && !batchResponseList.body().isEmpty()) {
for (int i = 0; i < batchResponseList.body().size(); i++) {
BatchResponse response = batchResponseList.body().get(i);
if (batchResponseList != null && !batchResponseList.isEmpty()) {
for (int i = 0; i < batchResponseList.size(); i++) {
BatchResponse response = batchResponseList.get(i);
log.debug("Batch step {} status code: {}", i + 1, response.getStatusCode());
log.debug("Batch step {} response body: {}", i + 1, response.getBody());
}
@@ -1027,7 +1057,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// Disburse loan
PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest();
String bodyLoanDisburseRequest = GSON.toJson(loanDisburseRequest);
String bodyLoanDisburseRequest = toJson(loanDisburseRequest);
BatchRequest disburseRequest = new BatchRequest();
disburseRequest.requestId(4L);
disburseRequest.relativeUrl("loans/$.loanId?command=disburse");
@@ -1043,16 +1073,18 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(applyInterestPause(5L, 2L, idempotencyKey, startDate, endDate));
// Execute batch request
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, true).execute();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("enclosingTransaction", true);
List<BatchResponse> batchResponseList = batchApiApi().handleBatchRequests(requestList, queryParams);
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
testContext().set(TestContextKey.BATCH_API_CALL_CLIENT_EXTERNAL_ID, clientExternalId);
testContext().set(TestContextKey.BATCH_API_CALL_LOAN_EXTERNAL_ID, loanExternalId);
// Log response for debugging
if (batchResponseList.isSuccessful() && batchResponseList.body() != null && !batchResponseList.body().isEmpty()) {
for (int i = 0; i < batchResponseList.body().size(); i++) {
BatchResponse response = batchResponseList.body().get(i);
if (batchResponseList != null && !batchResponseList.isEmpty()) {
for (int i = 0; i < batchResponseList.size(); i++) {
BatchResponse response = batchResponseList.get(i);
log.debug("Batch step {} status code: {}", i + 1, response.getStatusCode());
log.debug("Batch step {} response body: {}", i + 1, response.getBody());
}
@@ -1069,7 +1101,7 @@ public class BatchApiStepDef extends AbstractStepDef {
requestMap.put("locale", DEFAULT_LOCALE);
requestMap.put("note", "Charge-off due to delinquency");
String bodyChargeOffRequest = GSON.toJson(requestMap);
String bodyChargeOffRequest = toJson(requestMap);
Set<Header> headers = new HashSet<>();
headers.add(HEADER);
@@ -1089,15 +1121,16 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Admin checks the loan has been charged-off on {string}")
public void checkLoanChargedOff(String chargeOffDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Map<String, Object> loanQueryParams = new HashMap<>();
loanQueryParams.put("staffInSelectedOfficeOnly", false);
loanQueryParams.put("associations", "all");
GetLoansLoanIdResponse loanDetails = loansApi().retrieveLoan(loanId, loanQueryParams);
// Check loan has a CHARGE_OFF transaction on the specified date
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
boolean hasChargeOffTransaction = loanDetails.body().getTransactions().stream().anyMatch(
boolean hasChargeOffTransaction = loanDetails.getTransactions().stream().anyMatch(
t -> t.getType().getCode().equals("loanTransactionType.chargeOff") && formatter.format(t.getDate()).equals(chargeOffDate));
assertThat(hasChargeOffTransaction).as("Loan should have a CHARGE_OFF transaction on " + chargeOffDate).isTrue();
@@ -1115,7 +1148,7 @@ public class BatchApiStepDef extends AbstractStepDef {
private BatchRequest createClient(Long requestId, String idempotencyKey, String clientExternalId) {
PostClientsRequest clientsRequest = clientExternalId == null ? clientRequestFactory.defaultClientCreationRequest()
: clientRequestFactory.defaultClientCreationRequest().externalId(clientExternalId);
String bodyClientsRequest = GSON.toJson(clientsRequest);
String bodyClientsRequest = toJson(clientsRequest);
Set<Header> headers = new HashSet<>();
headers.add(HEADER);
@@ -1136,7 +1169,7 @@ public class BatchApiStepDef extends AbstractStepDef {
private BatchRequest createLoan(Long requestId, Long referenceId, String idempotencyKey, String loanExternalId) {
PostLoansRequest loansRequest = loanExternalId == null ? loanRequestFactory.defaultLoansRequest(1L)
: loanRequestFactory.defaultLoansRequest(1L).externalId(loanExternalId);
String bodyLoansRequest = GSON.toJson(loansRequest);
String bodyLoansRequest = toJson(loansRequest);
String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\"");
BatchRequest batchRequest = new BatchRequest();
@@ -1154,7 +1187,7 @@ public class BatchApiStepDef extends AbstractStepDef {
PostLoansRequest loansRequest = loanExternalId == null ? loanRequestFactory.defaultProgressiveLoansRequest(1L)
: loanRequestFactory.defaultProgressiveLoansRequest(1L).externalId(loanExternalId);
loansRequest.setInterestRatePerPeriod(BigDecimal.ONE);
String bodyLoansRequest = GSON.toJson(loansRequest);
String bodyLoansRequest = toJson(loansRequest);
String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\"");
BatchRequest batchRequest = new BatchRequest();
@@ -1198,7 +1231,7 @@ public class BatchApiStepDef extends AbstractStepDef {
private BatchRequest approveLoanByExternalId(Long requestId, Long referenceId, String idempotencyKey) {
PostLoansLoanIdRequest loanApproveRequest = LoanRequestFactory.defaultLoanApproveRequest();
String bodyLoanApproveRequest = GSON.toJson(loanApproveRequest);
String bodyLoanApproveRequest = toJson(loanApproveRequest);
BatchRequest batchRequest = new BatchRequest();
batchRequest.requestId(requestId);
@@ -1213,7 +1246,7 @@ public class BatchApiStepDef extends AbstractStepDef {
private BatchRequest approveLoanByExternalIdFail(Long requestId, Long referenceId, String idempotencyKey, String loanExternalId) {
PostLoansLoanIdRequest loanApproveRequest = LoanRequestFactory.defaultLoanApproveRequest();
String bodyLoanApproveRequest = GSON.toJson(loanApproveRequest);
String bodyLoanApproveRequest = toJson(loanApproveRequest);
BatchRequest batchRequest = new BatchRequest();
batchRequest.requestId(requestId);
@@ -1282,26 +1315,29 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Loan should have an active interest pause period starting on {int}st day and ending on {int}nd day")
public void verifyInterestPausePeriod(int startDay, int endDay) throws IOException {
// Get the loan ID from the batch response
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
assertThat(batchResponseList.isSuccessful()).isTrue();
assertThat(batchResponseList.body()).isNotNull();
List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
assertThat(batchResponseList != null).isTrue();
assertThat(batchResponseList).isNotNull();
// The loan creation response is the second response in the batch (index 1)
BatchResponse loanCreateResponse = batchResponseList.body().get(1);
BatchResponse loanCreateResponse = batchResponseList.get(1);
assertThat(loanCreateResponse.getStatusCode()).isEqualTo(200);
// Parse the loan ID from the response
String loanCreateResponseBody = loanCreateResponse.getBody();
com.google.gson.JsonObject loanCreateJson = com.google.gson.JsonParser.parseString(loanCreateResponseBody).getAsJsonObject();
long loanId = loanCreateJson.get("loanId").getAsLong();
com.fasterxml.jackson.databind.JsonNode loanCreateJson = readTree(loanCreateResponseBody);
long loanId = loanCreateJson.get("loanId").asLong();
// Get the loan details
Response<GetLoansLoanIdResponse> loanResponse = loansApi.retrieveLoan(loanId, false, "all", "", "").execute();
assertThat(loanResponse.isSuccessful()).isTrue();
assertThat(loanResponse.body()).isNotNull();
Map<String, Object> loanQueryParams = new HashMap<>();
loanQueryParams.put("staffInSelectedOfficeOnly", false);
loanQueryParams.put("associations", "all");
GetLoansLoanIdResponse loanResponse = loansApi().retrieveLoan(loanId, loanQueryParams);
assertThat(loanResponse != null).isTrue();
assertThat(loanResponse).isNotNull();
// Verify the interest pause period
GetLoansLoanIdResponse loan = loanResponse.body();
GetLoansLoanIdResponse loan = loanResponse;
assertThat(loan.getLoanTermVariations().get(0).getTermType().getValue().equals("interestPause")).isTrue();
// Verify the start date is the specified day of the previous month
@@ -1317,4 +1353,28 @@ public class BatchApiStepDef extends AbstractStepDef {
log.debug("Verified interest pause period from {} to {}", actualStartDate, actualEndDate);
}
private static String toJson(Object obj) {
try {
return OBJECT_MAPPER.writeValueAsString(obj);
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
throw new RuntimeException("Error serializing object to JSON", e);
}
}
private static <T> T fromJson(String json, Class<T> clazz) {
try {
return OBJECT_MAPPER.readValue(json, clazz);
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
throw new RuntimeException("Error deserializing JSON to object", e);
}
}
private static com.fasterxml.jackson.databind.JsonNode readTree(String json) {
try {
return OBJECT_MAPPER.readTree(json);
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
throw new RuntimeException("Error parsing JSON tree", e);
}
}
}
@@ -19,27 +19,24 @@
package org.apache.fineract.test.stepdef.common;
import static java.util.Arrays.asList;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import org.apache.fineract.client.feign.FeignException;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.BusinessDateResponse;
import org.apache.fineract.client.models.BusinessDateUpdateRequest;
import org.apache.fineract.client.models.BusinessDateUpdateResponse;
import org.apache.fineract.client.services.BusinessDateManagementApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.helper.BusinessDateHelper;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class BusinessDateStepDef extends AbstractStepDef {
@@ -47,68 +44,72 @@ public class BusinessDateStepDef extends AbstractStepDef {
private BusinessDateHelper businessDateHelper;
@Autowired
private BusinessDateManagementApi businessDateManagementApi;
private static final Gson GSON = new JSON().getGson();
private FineractFeignClient fineractClient;
@When("Admin sets the business date to {string}")
public void setBusinessDate(String businessDate) throws IOException {
public void setBusinessDate(String businessDate) {
businessDateHelper.setBusinessDate(businessDate);
}
@When("Admin sets the business date to the actual date")
public void setBusinessDateToday() throws IOException {
public void setBusinessDateToday() {
businessDateHelper.setBusinessDateToday();
}
@Then("Admin checks that the business date is correctly set to {string}")
public void checkBusinessDate(String businessDate) throws IOException {
Response<BusinessDateResponse> businessDateResponse = businessDateManagementApi.getBusinessDate(BusinessDateHelper.BUSINESS_DATE)
.execute();
ErrorHelper.checkSuccessfulApiCall(businessDateResponse);
public void checkBusinessDate(String businessDate) {
BusinessDateResponse businessDateResponse = ok(
() -> fineractClient.businessDateManagement().getBusinessDate(BusinessDateHelper.BUSINESS_DATE, Map.of()));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMMM yyyy");
LocalDate localDate = LocalDate.parse(businessDate, formatter);
assertThat(businessDateResponse.body().getDate()).isEqualTo(localDate);
assertThat(businessDateResponse.getDate()).isEqualTo(localDate);
}
@Then("Set incorrect business date value {string} outcomes with an error")
public void setIncorrectBusinessDateFailure(String businessDate) throws IOException {
public void setIncorrectBusinessDateFailure(String businessDate) {
BusinessDateUpdateRequest businessDateRequest = businessDateHelper.defaultBusinessDateRequest().date(businessDate);
Response<BusinessDateUpdateResponse> businessDateRequestResponse = businessDateManagementApi
.updateBusinessDate(null, businessDateRequest).execute();
final ErrorResponse errorDetails = ErrorResponse.from(businessDateRequestResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.setIncorrectBusinessDateFailure()).isEqualTo(400);
assertThat(errorDetails.getSingleError().getDeveloperMessage()).isEqualTo(ErrorMessageHelper.setIncorrectBusinessDateFailure());
try {
fineractClient.businessDateManagement().updateBusinessDate(null, businessDateRequest, Map.of());
throw new AssertionError("Expected FeignException but request succeeded");
} catch (FeignException e) {
final ErrorResponse errorDetails = ErrorResponse.fromFeignException(e);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.setIncorrectBusinessDateFailure()).isEqualTo(400);
assertThat(errorDetails.getSingleError().getDeveloperMessageWithoutPrefix())
.isEqualTo(ErrorMessageHelper.setIncorrectBusinessDateFailure());
}
}
@Then("Set incorrect business date with empty value {string} outcomes with an error")
public void setNullOrEmptyBusinessDateFailure(String businessDate) throws IOException {
public void setNullOrEmptyBusinessDateFailure(String businessDate) {
BusinessDateUpdateRequest businessDateRequest = businessDateHelper.defaultBusinessDateRequest();
if (businessDate.equals("null")) {
businessDateRequest.date(null);
} else {
businessDateRequest.date(businessDate);
}
Response<BusinessDateUpdateResponse> businessDateRequestResponse = businessDateManagementApi
.updateBusinessDate(null, businessDateRequest).execute();
Integer httpStatusCodeExpected = 400;
String errorBody = businessDateRequestResponse.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorBody, ErrorResponse.class);
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
List<String> developerMessagesActual = errorResponse.getErrors().stream().map(ErrorResponse.Error::getDeveloperMessage).toList();
try {
fineractClient.businessDateManagement().updateBusinessDate(null, businessDateRequest, Map.of());
throw new AssertionError("Expected FeignException but request succeeded");
} catch (FeignException e) {
ErrorResponse errorResponse = ErrorResponse.fromFeignException(e);
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
List<String> developerMessagesActual = errorResponse.getErrors().stream().map(ErrorResponse.ErrorDetail::getDeveloperMessage)
.toList();
List<String> developerMessagesExpected = asList(ErrorMessageHelper.setIncorrectBusinessDateMandatoryFailure(),
ErrorMessageHelper.setIncorrectBusinessDateFailure());
List<String> developerMessagesExpected = asList(ErrorMessageHelper.setIncorrectBusinessDateMandatoryFailure(),
ErrorMessageHelper.setIncorrectBusinessDateFailure());
assertThat(httpStatusCodeActual)
.as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected))
.isEqualTo(httpStatusCodeExpected);
assertThat(developerMessagesActual)
.as(ErrorMessageHelper.wrongErrorMessage(developerMessagesActual.toString(), developerMessagesExpected.toString()))
.containsAll(developerMessagesExpected);
assertThat(httpStatusCodeActual)
.as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected))
.isEqualTo(httpStatusCodeExpected);
assertThat(developerMessagesActual)
.as(ErrorMessageHelper.wrongErrorMessage(developerMessagesActual.toString(), developerMessagesExpected.toString()))
.containsAll(developerMessagesExpected);
}
}
}
@@ -18,34 +18,26 @@
*/
package org.apache.fineract.test.stepdef.common;
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.io.IOException;
import java.util.Collections;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.ClientAddressRequest;
import org.apache.fineract.client.models.PostClientsRequest;
import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.services.ClientApi;
import org.apache.fineract.test.factory.ClientRequestFactory;
import org.apache.fineract.test.helper.CodeHelper;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.Utils;
import org.apache.fineract.test.messaging.event.EventCheckHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class ClientStepDef extends AbstractStepDef {
@Autowired
private ClientApi clientApi;
@Autowired
private CodeHelper codeHelper;
private FineractFeignClient fineractClient;
@Autowired
private ClientRequestFactory clientRequestFactory;
@@ -54,42 +46,38 @@ public class ClientStepDef extends AbstractStepDef {
private EventCheckHelper eventCheckHelper;
@When("Admin creates a client with random data")
public void createClientRandomFirstNameLastName() throws IOException {
public void createClientRandomFirstNameLastName() {
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest();
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response);
eventCheckHelper.clientEventCheck(response);
}
@When("Admin creates a second client with random data")
public void createSecondClientRandomFirstNameLastName() throws IOException {
public void createSecondClientRandomFirstNameLastName() {
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest();
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
testContext().set(TestContextKey.CLIENT_CREATE_SECOND_CLIENT_RESPONSE, response);
eventCheckHelper.clientEventCheck(response);
}
@When("Admin creates a client with Firstname {string} and Lastname {string}")
public void createClient(String firstName, String lastName) throws IOException {
public void createClient(String firstName, String lastName) {
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest().firstname(firstName).lastname(lastName);
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response);
}
@When("Admin creates a client with Firstname {string} and Lastname {string} with address")
public void createClientWithAddress(String firstName, String lastName) throws IOException {
Long addressTypeId = codeHelper.createAddressTypeCodeValue(Utils.randomNameGenerator("Residential address", 4)).body()
.getResourceId();
Long countryId = codeHelper.createCountryCodeValue(Utils.randomNameGenerator("Hungary", 4)).body().getResourceId();
Long stateId = codeHelper.createStateCodeValue(Utils.randomNameGenerator("Budapest", 4)).body().getResourceId();
public void createClientWithAddress(String firstName, String lastName) {
Long addressTypeId = 15L;
Long countryId = 17L;
Long stateId = 18L;
String city = "Budapest";
boolean addressIsActive = true;
String postalCode = "1000";
@@ -100,28 +88,26 @@ public class ClientStepDef extends AbstractStepDef {
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest().firstname(firstName).lastname(lastName)
.address(Collections.singletonList(addressRequest));
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response);
}
@When("Admin creates a client with Firstname {string} and Lastname {string} with {string} activation date")
public void createClientWithSpecifiedDates(String firstName, String lastName, String activationDate) throws IOException {
public void createClientWithSpecifiedDates(String firstName, String lastName, String activationDate) {
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest().firstname(firstName).lastname(lastName)
.activationDate(activationDate);
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response);
}
@Then("Client is created successfully")
public void checkClientCreatedSuccessfully() throws IOException {
Response<PostClientsResponse> response = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
public void checkClientCreatedSuccessfully() {
PostClientsResponse response = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
assertThat(response.isSuccessful()).as(ErrorMessageHelper.requestFailed(response)).isTrue();
assertThat(response.getClientId()).isNotNull();
eventCheckHelper.clientEventCheck(response);
}
@@ -28,7 +28,6 @@ import org.apache.fineract.test.messaging.event.Event;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@SuppressWarnings({ "unchecked", "rawtypes" })
public class EventStepDef extends AbstractStepDef {
@@ -38,16 +37,16 @@ public class EventStepDef extends AbstractStepDef {
@Then("{string} event has been raised for the loan")
public void assertEventRaisedForLoan(String eventType) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Class<? extends Event> eventTypeClazz = resolveEventType(eventType);
eventAssertion.assertEventRaised(eventTypeClazz, loanId);
}
@Then("No new event with type {string} has been raised for the loan")
public void assertEventNotRaisedForLoan(String eventType) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Class<? extends Event> eventTypeClazz = resolveEventType(eventType);
eventAssertion.assertEventNotRaised(eventTypeClazz, loanId);
}
@@ -21,16 +21,14 @@ package org.apache.fineract.test.stepdef.common;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.fineract.client.feign.FeignException;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.CurrencyUpdateRequest;
import org.apache.fineract.client.services.CurrencyApi;
import org.apache.fineract.client.services.DefaultApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.helper.GlobalConfigurationHelper;
@@ -40,73 +38,90 @@ public class GlobalConfigurationStepDef {
@Autowired
private GlobalConfigurationHelper globalConfigurationHelper;
@Autowired
private DefaultApi defaultApi;
@Autowired
private CurrencyApi currencyApi;
private static final Gson GSON = new JSON().getGson();
private FineractFeignClient fineractClient;
@Given("Global configuration {string} is disabled")
public void disableGlobalConfiguration(String configKey) throws IOException {
public void disableGlobalConfiguration(String configKey) {
globalConfigurationHelper.disableGlobalConfiguration(configKey, 0L);
}
@Given("Global configuration {string} is enabled")
public void enableGlobalConfiguration(String configKey) throws IOException {
public void enableGlobalConfiguration(String configKey) {
globalConfigurationHelper.enableGlobalConfiguration(configKey, 0L);
}
@When("Global config {string} value set to {string}")
public void setGlobalConfigValueString(String configKey, String configValue) throws IOException {
public void setGlobalConfigValueString(String configKey, String configValue) {
globalConfigurationHelper.setGlobalConfigValueString(configKey, configValue);
}
@When("Global config {string} value set to {string} through DefaultApi")
public void setGlobalConfigValueStringDefaultApi(String configKey, String configValue) throws IOException {
public void setGlobalConfigValueStringDefaultApi(String configKey, String configValue) {
Long configValueLong = Long.valueOf(configValue);
defaultApi.updateGlobalConfiguration(configKey, configValueLong);
fineractClient.defaultApi().updateGlobalConfiguration(configKey, configValueLong);
}
@When("Update currency with incorrect empty value outcomes with an error")
public void updateCurrencyEmptyValueFailure() throws IOException {
public void updateCurrencyEmptyValueFailure() {
var request = new CurrencyUpdateRequest();
var currencyResponse = currencyApi.updateCurrencies(request.currencies(Collections.emptyList())).execute();
final ErrorResponse errorDetails = ErrorResponse.from(currencyResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.setCurrencyEmptyValueFailure()).isEqualTo(400);
assertThat(errorDetails.getSingleError().getDeveloperMessage()).isEqualTo(ErrorMessageHelper.setCurrencyEmptyValueFailure());
try {
fineractClient.currency().updateCurrencies(request.currencies(Collections.emptyList()), Map.of());
throw new AssertionError("Expected FeignException but request succeeded");
} catch (FeignException e) {
final ErrorResponse errorDetails = ErrorResponse.fromFeignException(e);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.setCurrencyEmptyValueFailure()).isEqualTo(400);
if (errorDetails.getErrors() != null && !errorDetails.getErrors().isEmpty()) {
boolean hasExpectedError = errorDetails.getErrors().stream().anyMatch(
error -> ErrorMessageHelper.setCurrencyEmptyValueFailure().equals(error.getDeveloperMessageWithoutPrefix()));
assertThat(hasExpectedError).as("Expected error message: " + ErrorMessageHelper.setCurrencyEmptyValueFailure()
+ " in errors: " + errorDetails.getErrors()).isTrue();
} else {
assertThat(errorDetails.getSingleError().getDeveloperMessageWithoutPrefix())
.isEqualTo(ErrorMessageHelper.setCurrencyEmptyValueFailure());
}
}
}
@When("Update currency as NULL value outcomes with an error")
public void updateCurrencyNullValueFailure() throws IOException {
public void updateCurrencyNullValueFailure() {
var request = new CurrencyUpdateRequest();
var currencyResponse = currencyApi.updateCurrencies(request.currencies(null)).execute();
Integer httpStatusCodeExpected = 400;
String errorBody = currencyResponse.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorBody, ErrorResponse.class);
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
List<String> developerMessagesActual = errorResponse.getErrors().stream().map(ErrorResponse.Error::getDeveloperMessage).toList();
try {
fineractClient.currency().updateCurrencies(request.currencies(null));
throw new AssertionError("Expected FeignException but request succeeded");
} catch (FeignException e) {
ErrorResponse errorResponse = ErrorResponse.fromFeignException(e);
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
List<String> developerMessagesActual = errorResponse.getErrors().stream()
.map(ErrorResponse.ErrorDetail::getDeveloperMessageWithoutPrefix).toList();
List<String> developerMessagesExpected = asList(ErrorMessageHelper.setCurrencyEmptyValueFailure(),
ErrorMessageHelper.setCurrencyNullValueMandatoryFailure());
List<String> developerMessagesExpected = asList(ErrorMessageHelper.setCurrencyEmptyValueFailure(),
ErrorMessageHelper.setCurrencyNullValueMandatoryFailure());
assertThat(httpStatusCodeActual)
.as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected))
.isEqualTo(httpStatusCodeExpected);
assertThat(developerMessagesActual)
.as(ErrorMessageHelper.wrongErrorMessage(developerMessagesActual.toString(), developerMessagesExpected.toString()))
.containsAll(developerMessagesExpected);
assertThat(httpStatusCodeActual)
.as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected))
.isEqualTo(httpStatusCodeExpected);
assertThat(developerMessagesActual)
.as(ErrorMessageHelper.wrongErrorMessage(developerMessagesActual.toString(), developerMessagesExpected.toString()))
.containsAll(developerMessagesExpected);
}
}
@When("Update currency as {string} value outcomes with an error")
public void updateCurrencyIncorrectValueFailure(String currency) throws IOException {
public void updateCurrencyIncorrectValueFailure(String currency) {
var request = new CurrencyUpdateRequest();
var currencyResponse = currencyApi.updateCurrencies(request.currencies(Collections.singletonList(currency))).execute();
final ErrorResponse errorDetails = ErrorResponse.from(currencyResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.setCurrencyIncorrectValueFailure(currency)).isEqualTo(404);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.isEqualTo(ErrorMessageHelper.setCurrencyIncorrectValueFailure(currency));
try {
fineractClient.currency().updateCurrencies(request.currencies(Collections.singletonList(currency)));
throw new AssertionError("Expected FeignException but request succeeded");
} catch (FeignException e) {
final ErrorResponse errorDetails = ErrorResponse.fromFeignException(e);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.setCurrencyIncorrectValueFailure(currency)).isEqualTo(404);
assertThat(errorDetails.getSingleError().getDeveloperMessageWithoutPrefix())
.isEqualTo(ErrorMessageHelper.setCurrencyIncorrectValueFailure(currency));
}
}
}
@@ -28,10 +28,16 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.services.JournalEntriesApi;
import org.apache.fineract.client.feign.services.LoansApi;
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
@@ -39,16 +45,12 @@ import org.apache.fineract.client.models.JournalEntryCommand;
import org.apache.fineract.client.models.JournalEntryTransactionItem;
import org.apache.fineract.client.models.PostJournalEntriesResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.JournalEntriesApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.test.data.TransactionType;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class JournalEntriesStepDef extends AbstractStepDef {
@@ -56,27 +58,33 @@ public class JournalEntriesStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy";
@Autowired
private LoansApi loansApi;
@Autowired
private JournalEntriesApi journalEntriesApi;
private FineractFeignClient fineractFeignClient;
@Autowired
private LoanRequestFactory loanRequestFactory;
private LoansApi loansApi() {
return fineractFeignClient.loans();
}
private JournalEntriesApi journalEntriesApi() {
return fineractFeignClient.journalEntries();
}
@Then("Loan Transactions tab has a {string} transaction with date {string} which has the following Journal entries:")
public void journalEntryDataCheck(String transactionType, String transactionDate, DataTable table) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("staffInSelectedOfficeOnly", false);
queryParams.put("associations", "transactions");
GetLoansLoanIdResponse loanDetailsResponse = loansApi().retrieveLoan(loanId, queryParams);
TransactionType transactionType1 = TransactionType.valueOf(transactionType);
String transactionTypeExpected = transactionType1.getValue();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate()))
&& transactionTypeExpected.equals(t.getType().getCode().substring(20)))
@@ -114,7 +122,8 @@ public class JournalEntriesStepDef extends AbstractStepDef {
}).collect(Collectors.toList());
possibleActualValuesList.add(actualValuesList);
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
boolean containsExpectedValues = actualValuesList.stream()
.anyMatch(actualValues -> matchesWithBigDecimalComparison(actualValues, expectedValues));
if (containsExpectedValues) {
containsAnyExpected = true;
}
@@ -125,38 +134,50 @@ public class JournalEntriesStepDef extends AbstractStepDef {
}
}
private boolean matchesWithBigDecimalComparison(List<String> actualValues, List<String> expectedValues) {
if (actualValues.size() != expectedValues.size()) {
return false;
}
for (int i = 0; i < actualValues.size(); i++) {
String actual = actualValues.get(i);
String expected = expectedValues.get(i);
if (!valuesMatch(actual, expected)) {
return false;
}
}
return true;
}
private boolean valuesMatch(String actual, String expected) {
if (Objects.equals(actual, expected)) {
return true;
}
if (actual == null || expected == null) {
return false;
}
try {
BigDecimal actualDecimal = new BigDecimal(actual);
BigDecimal expectedDecimal = new BigDecimal(expected);
return actualDecimal.compareTo(expectedDecimal) == 0;
} catch (NumberFormatException e) {
return actual.equals(expected);
}
}
public List<List<JournalEntryTransactionItem>> getJournalLinesActualList(List<GetLoansLoanIdTransactions> transactionsMatch) {
List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionsMatch.stream().map(t -> {
String transactionId = "L" + t.getId();
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null;
GetJournalEntriesTransactionIdResponse journalEntryDataResponse = null;
try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(//
null, //
null, //
null, //
null, //
null, //
null, //
null, //
transactionId, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
Map<String, Object> journalQueryParams = new HashMap<>();
journalQueryParams.put("transactionId", transactionId);
journalQueryParams.put("runningBalance", true);
journalEntryDataResponse = journalEntriesApi().retrieveAll1(journalQueryParams);
} catch (Exception e) {
log.error("Exception", e);
}
return journalEntryDataResponse.body().getPageItems();
return journalEntryDataResponse.getPageItems();
}).collect(Collectors.toList());
return journalLinesActualList;
@@ -165,16 +186,17 @@ public class JournalEntriesStepDef extends AbstractStepDef {
@Then("Loan Transactions tab has {int} a {string} transactions with date {string} which has the following Journal entries:")
public void journalEntryDataCheck(int numberTrns, String transactionType, String transactionDate, DataTable table) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("staffInSelectedOfficeOnly", false);
queryParams.put("associations", "transactions");
GetLoansLoanIdResponse loanDetailsResponse = loansApi().retrieveLoan(loanId, queryParams);
TransactionType transactionType1 = TransactionType.valueOf(transactionType);
String transactionTypeExpected = transactionType1.getValue();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate()))
&& transactionTypeExpected.equals(t.getType().getCode().substring(20)))
@@ -191,43 +213,25 @@ public class JournalEntriesStepDef extends AbstractStepDef {
@Then("Reversed loan capitalized income amortization transaction has the following Journal entries:")
public void capitalizedIncomeAmortizationJournalEntryDataCheck(final DataTable table) {
final long capitalizedIncomeAmortizationId = testContext().get(TestContextKey.LOAN_CAPITALIZED_INCOME_AMORTIZATION_ID);
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse.body() != null;
final long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
final long loanId = loanResponse.getLoanId();
final String resourceId = String.valueOf(loanId);
List<JournalEntryTransactionItem> journalLinesActualList;
final String transactionId = "L" + capitalizedIncomeAmortizationId;
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null;
GetJournalEntriesTransactionIdResponse journalEntryDataResponse = null;
try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(//
null, //
null, //
null, //
null, //
null, //
null, //
null, //
transactionId, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
loanId, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
Map<String, Object> journalQueryParams = new HashMap<>();
journalQueryParams.put("transactionId", transactionId);
journalQueryParams.put("loanId", loanId);
journalQueryParams.put("runningBalance", true);
journalEntryDataResponse = journalEntriesApi().retrieveAll1(journalQueryParams);
} catch (Exception e) {
log.error("Exception", e);
}
assert journalEntryDataResponse != null;
assert journalEntryDataResponse.body() != null;
journalLinesActualList = journalEntryDataResponse.body().getPageItems();
List<JournalEntryTransactionItem> journalLinesActualList = new ArrayList<>();
if (journalEntryDataResponse != null) {
journalLinesActualList = journalEntryDataResponse.getPageItems();
}
final List<List<String>> data = table.asLists();
for (int i = 1; i < data.size(); i++) {
@@ -253,7 +257,7 @@ public class JournalEntriesStepDef extends AbstractStepDef {
possibleActualValuesList.add(actualValues);
final boolean containsExpectedValues = possibleActualValuesList.stream()
.anyMatch(actualValue -> actualValue.equals(expectedValues));
.anyMatch(actualValue -> matchesWithBigDecimalComparison(actualValue, expectedValues));
if (containsExpectedValues) {
containsAnyExpected = true;
}
@@ -267,17 +271,18 @@ public class JournalEntriesStepDef extends AbstractStepDef {
@Then("In Loan transactions the replayed {string} transaction with date {string} has a reverted transaction pair with the following Journal entries:")
public void revertedJournalEntryDataCheck(String transactionType, String transactionDate, DataTable table) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
String resourceId = String.valueOf(loanId);
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("staffInSelectedOfficeOnly", false);
queryParams.put("associations", "transactions");
GetLoansLoanIdResponse loanDetailsResponse = loansApi().retrieveLoan(loanId, queryParams);
TransactionType transactionType1 = TransactionType.valueOf(transactionType);
String transactionTypeExpected = transactionType1.getValue();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate()))
@@ -289,35 +294,17 @@ public class JournalEntriesStepDef extends AbstractStepDef {
.collect(Collectors.toList());
List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionIdList.stream().map(t -> {
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null;
GetJournalEntriesTransactionIdResponse journalEntryDataResponse = null;
try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(//
null, //
null, //
null, //
null, //
null, //
null, //
null, //
t, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
Map<String, Object> journalQueryParams = new HashMap<>();
journalQueryParams.put("transactionId", t);
journalQueryParams.put("runningBalance", true);
journalEntryDataResponse = journalEntriesApi().retrieveAll1(journalQueryParams);
} catch (Exception e) {
log.error("Exception", e);
}
return journalEntryDataResponse.body().getPageItems();
return journalEntryDataResponse.getPageItems();
}).collect(Collectors.toList());
List<List<String>> data = table.asLists();
@@ -341,7 +328,8 @@ public class JournalEntriesStepDef extends AbstractStepDef {
}).collect(Collectors.toList());
possibleActualValuesList.add(actualValuesList);
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
boolean containsExpectedValues = actualValuesList.stream()
.anyMatch(actualValues -> matchesWithBigDecimalComparison(actualValues, expectedValues));
if (containsExpectedValues) {
containsAnyExpected = true;
}
@@ -355,16 +343,17 @@ public class JournalEntriesStepDef extends AbstractStepDef {
@Then("Loan Transactions tab has a {string} transaction with date {string} has no the Journal entries")
public void journalEntryNoDataCheck(String transactionType, String transactionDate) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("staffInSelectedOfficeOnly", false);
queryParams.put("associations", "transactions");
GetLoansLoanIdResponse loanDetailsResponse = loansApi().retrieveLoan(loanId, queryParams);
TransactionType transactionType1 = TransactionType.valueOf(transactionType);
String transactionTypeExpected = transactionType1.getValue();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate()))
&& transactionTypeExpected.equals(t.getType().getCode().substring(20)))
@@ -372,58 +361,43 @@ public class JournalEntriesStepDef extends AbstractStepDef {
List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionsMatch.stream().map(t -> {
String transactionId = "L" + t.getId();
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null;
GetJournalEntriesTransactionIdResponse journalEntryDataResponse = null;
try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(//
null, //
null, //
null, //
null, //
null, //
null, //
null, //
transactionId, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
Map<String, Object> journalQueryParams = new HashMap<>();
journalQueryParams.put("transactionId", transactionId);
journalQueryParams.put("runningBalance", true);
journalEntryDataResponse = journalEntriesApi().retrieveAll1(journalQueryParams);
} catch (Exception e) {
log.error("Exception", e);
}
return journalEntryDataResponse.body().getPageItems();
return journalEntryDataResponse.getPageItems();
}).collect(Collectors.toList());
assertThat(journalLinesActualList.stream().findFirst().get().size()).isZero();
}
public Response<PostJournalEntriesResponse> addManualJournalEntryWithoutExternalAssetOwner(String amount, String date)
throws IOException {
public PostJournalEntriesResponse addManualJournalEntryWithoutExternalAssetOwner(String amount, String date) throws IOException {
LocalDate transactionDate = LocalDate.parse(date, FORMATTER_EN);
JournalEntryCommand journalEntriesRequest = loanRequestFactory.defaultManualJournalEntryRequest(new BigDecimal(amount))
.transactionDate(transactionDate);
Response<PostJournalEntriesResponse> journalEntriesResponse = journalEntriesApi.createGLJournalEntry("", journalEntriesRequest)
.execute();
Map<String, Object> createJournalQueryParams = new HashMap<>();
createJournalQueryParams.put("command", "");
PostJournalEntriesResponse journalEntriesResponse = journalEntriesApi().createGLJournalEntry(journalEntriesRequest,
createJournalQueryParams);
testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST, journalEntriesRequest);
return journalEntriesResponse;
}
public Response<PostJournalEntriesResponse> addManualJournalEntryWithExternalAssetOwner(String amount, String date,
String externalAssetOwner) throws IOException {
public PostJournalEntriesResponse addManualJournalEntryWithExternalAssetOwner(String amount, String date, String externalAssetOwner)
throws IOException {
LocalDate transactionDate = LocalDate.parse(date, FORMATTER_EN);
JournalEntryCommand journalEntriesRequest = loanRequestFactory
.defaultManualJournalEntryRequest(new BigDecimal(amount), externalAssetOwner).transactionDate(transactionDate);
Response<PostJournalEntriesResponse> journalEntriesResponse = journalEntriesApi.createGLJournalEntry("", journalEntriesRequest)
.execute();
Map<String, Object> createJournalQueryParams = new HashMap<>();
createJournalQueryParams.put("command", "");
PostJournalEntriesResponse journalEntriesResponse = journalEntriesApi().createGLJournalEntry(journalEntriesRequest,
createJournalQueryParams);
testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST, journalEntriesRequest);
return journalEntriesResponse;
}
@@ -431,62 +405,41 @@ public class JournalEntriesStepDef extends AbstractStepDef {
@Then("Admin creates manual Journal entry with {string} amount and {string} date and unique External Asset Owner")
public void createManualJournalEntryWithExternalAssetOwner(String amount, String date) throws IOException {
String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
Response<PostJournalEntriesResponse> journalEntriesResponse = addManualJournalEntryWithExternalAssetOwner(amount, date,
PostJournalEntriesResponse journalEntriesResponse = addManualJournalEntryWithExternalAssetOwner(amount, date,
ownerExternalIdStored);
testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE, journalEntriesResponse);
ErrorHelper.checkSuccessfulApiCall(journalEntriesResponse);
}
@Then("Admin creates manual Journal entry with {string} amount and {string} date and empty External Asset Owner")
public void createManualJournalEntryWithEmptyExternalAssetOwner(String amount, String date) throws IOException {
Response<PostJournalEntriesResponse> journalEntriesResponse = addManualJournalEntryWithExternalAssetOwner(amount, date, "");
PostJournalEntriesResponse journalEntriesResponse = addManualJournalEntryWithExternalAssetOwner(amount, date, "");
testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE, journalEntriesResponse);
ErrorHelper.checkSuccessfulApiCall(journalEntriesResponse);
}
@Then("Admin creates manual Journal entry with {string} amount and {string} date and without External Asset Owner")
public void createManualJournalEntryWithoutExternalAssetOwner(String amount, String date) throws IOException {
Response<PostJournalEntriesResponse> journalEntriesResponse = addManualJournalEntryWithoutExternalAssetOwner(amount, date);
PostJournalEntriesResponse journalEntriesResponse = addManualJournalEntryWithoutExternalAssetOwner(amount, date);
testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE, journalEntriesResponse);
ErrorHelper.checkSuccessfulApiCall(journalEntriesResponse);
}
@Then("Verify manual Journal entry with External Asset Owner {string} and with the following Journal entries:")
public void checkManualJournalEntry(String externalAssetOwnerEnabled, DataTable table) {
Response<PostJournalEntriesResponse> journalEnriesResponse = testContext().get(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE);
PostJournalEntriesResponse journalEntriesResponseBody = journalEnriesResponse.body();
PostJournalEntriesResponse journalEnriesResponse = testContext().get(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE);
PostJournalEntriesResponse journalEntriesResponseBody = journalEnriesResponse;
String transactionId = journalEntriesResponseBody.getTransactionId();
JournalEntryCommand journalEntriesRequest = testContext().get(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST);
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null;
GetJournalEntriesTransactionIdResponse journalEntryDataResponse = null;
try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(//
null, //
null, //
null, //
null, //
null, //
null, //
null, //
transactionId, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
Map<String, Object> journalQueryParams = new HashMap<>();
journalQueryParams.put("transactionId", transactionId);
journalQueryParams.put("runningBalance", true);
journalEntryDataResponse = journalEntriesApi().retrieveAll1(journalQueryParams);
} catch (Exception e) {
log.error("Exception", e);
}
@@ -500,7 +453,7 @@ public class JournalEntriesStepDef extends AbstractStepDef {
}
boolean containsAnyExpected = false;
GetJournalEntriesTransactionIdResponse journalEntryData = journalEntryDataResponse.body();
GetJournalEntriesTransactionIdResponse journalEntryData = journalEntryDataResponse;
List<JournalEntryTransactionItem> journalLinesActual = journalEntryData.getPageItems();
@@ -511,7 +464,7 @@ public class JournalEntriesStepDef extends AbstractStepDef {
actualValues.add(t.getGlAccountName() == null ? null : t.getGlAccountName());
actualValues.add("DEBIT".equals(t.getEntryType().getValue()) ? String.valueOf(t.getAmount()) : null);
actualValues.add("CREDIT".equals(t.getEntryType().getValue()) ? String.valueOf(t.getAmount()) : null);
actualValues.add(String.valueOf(t.getManualEntry()).toLowerCase());
actualValues.add(String.valueOf(t.getManualEntry()).toLowerCase(Locale.ROOT));
if (Boolean.parseBoolean(externalAssetOwnerEnabled)) {
actualValues.add(t.getExternalAssetOwner() == null ? null : t.getExternalAssetOwner());
}
@@ -521,7 +474,8 @@ public class JournalEntriesStepDef extends AbstractStepDef {
possibleActualValuesList.add(actualValuesList);
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
boolean containsExpectedValues = actualValuesList.stream()
.anyMatch(actualValues -> matchesWithBigDecimalComparison(actualValues, expectedValues));
if (containsExpectedValues) {
containsAnyExpected = true;
}
@@ -18,57 +18,47 @@
*/
package org.apache.fineract.test.stepdef.common;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.fineract.client.models.GetRolesResponse;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.PostRolesRequest;
import org.apache.fineract.client.models.PostRolesResponse;
import org.apache.fineract.client.models.PostUsersRequest;
import org.apache.fineract.client.models.PostUsersResponse;
import org.apache.fineract.client.models.PutRolesRoleIdPermissionsRequest;
import org.apache.fineract.client.models.PutRolesRoleIdPermissionsResponse;
import org.apache.fineract.client.services.RolesApi;
import org.apache.fineract.client.services.UsersApi;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.Utils;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class UserStepDef extends AbstractStepDef {
private static final String EMAIL = "test@test.com";
@Autowired
private RolesApi rolesApi;
@Autowired
private UsersApi usersApi;
private FineractFeignClient fineractClient;
private static final String PWD_USER_WITH_ROLE = "1234567890Aa!";
@When("Admin creates new user with {string} username, {string} role name and given permissions:")
public void createUserWithUsernameAndRoles(String username, String roleName, List<String> permissions) throws IOException {
Response<List<GetRolesResponse>> retrieveAllRolesResponse = rolesApi.retrieveAllRoles().execute();
ErrorHelper.checkSuccessfulApiCall(retrieveAllRolesResponse);
public void createUserWithUsernameAndRoles(String username, String roleName, List<String> permissions) {
ok(() -> fineractClient.roles().retrieveAllRoles());
PostRolesRequest newRoleRequest = new PostRolesRequest().name(Utils.randomNameGenerator(roleName, 8)).description(roleName);
Response<PostRolesResponse> createNewRole = rolesApi.createRole(newRoleRequest).execute();
ErrorHelper.checkSuccessfulApiCall(createNewRole);
Long roleId = createNewRole.body().getResourceId();
PostRolesResponse createNewRole = ok(() -> fineractClient.roles().createRole(newRoleRequest));
Long roleId = createNewRole.getResourceId();
Map<String, Boolean> permissionMap = new HashMap<>();
permissions.forEach(role -> permissionMap.put(role, true));
PutRolesRoleIdPermissionsRequest putRolesRoleIdPermissionsRequest = new PutRolesRoleIdPermissionsRequest()
.permissions(permissionMap);
Response<PutRolesRoleIdPermissionsResponse> updateRolePermissionResponse = rolesApi
.updateRolePermissions(roleId, putRolesRoleIdPermissionsRequest).execute();
ErrorHelper.checkSuccessfulApiCall(updateRolePermissionResponse);
ok(() -> fineractClient.roles().updateRolePermissions(roleId, putRolesRoleIdPermissionsRequest));
String generatedUsername = Utils.randomNameGenerator(username, 8);
PostUsersRequest postUsersRequest = new PostUsersRequest() //
.username(Utils.randomNameGenerator(username, 8)) //
.username(generatedUsername) //
.email(EMAIL) //
.firstname(username) //
.lastname(username) //
@@ -78,8 +68,9 @@ public class UserStepDef extends AbstractStepDef {
.repeatPassword(PWD_USER_WITH_ROLE) //
.roles(List.of(roleId));
Response<PostUsersResponse> createUserResponse = usersApi.create15(postUsersRequest).execute();
ErrorHelper.checkSuccessfulApiCall(createUserResponse);
PostUsersResponse createUserResponse = ok(() -> fineractClient.users().create15(postUsersRequest));
testContext().set(TestContextKey.CREATED_SIMPLE_USER_RESPONSE, createUserResponse);
testContext().set(TestContextKey.CREATED_SIMPLE_USER_USERNAME, generatedUsername);
testContext().set(TestContextKey.CREATED_SIMPLE_USER_PASSWORD, PWD_USER_WITH_ROLE);
}
}
@@ -19,29 +19,28 @@
package org.apache.fineract.test.stepdef.datatable;
import static java.util.function.Function.identity;
import static org.apache.fineract.test.helper.ErrorHelper.checkSuccessfulApiCall;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.fineract.client.feign.FeignException;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetDataTablesResponse;
import org.apache.fineract.client.models.PostColumnHeaderData;
import org.apache.fineract.client.models.PostDataTablesRequest;
import org.apache.fineract.client.models.PostDataTablesResponse;
import org.apache.fineract.client.models.ResultsetColumnHeaderData;
import org.apache.fineract.client.services.DataTablesApi;
import org.apache.fineract.test.data.datatable.DatatableColumnType;
import org.apache.fineract.test.data.datatable.DatatableEntityType;
import org.apache.fineract.test.data.datatable.DatatableNameGenerator;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class DatatablesStepDef extends AbstractStepDef {
@@ -50,39 +49,35 @@ public class DatatablesStepDef extends AbstractStepDef {
public static final String DATATABLE_QUERY_RESPONSE = "DatatableQueryResponse";
@Autowired
private DataTablesApi dataTablesApi;
private FineractFeignClient fineractClient;
@Autowired
private DatatableNameGenerator datatableNameGenerator;
@When("A datatable for {string} is created")
public void whenDatatableCreated(String entityTypeStr) throws IOException {
public void whenDatatableCreated(String entityTypeStr) {
DatatableEntityType entityType = DatatableEntityType.fromString(entityTypeStr);
List<PostColumnHeaderData> columns = createRandomDatatableColumnsRequest();
PostDataTablesRequest request = createDatatableRequest(entityType, columns);
Response<PostDataTablesResponse> response = dataTablesApi.createDatatable(request).execute();
checkSuccessfulApiCall(response);
PostDataTablesResponse response = ok(() -> fineractClient.dataTables().createDatatable(request, Map.of()));
PostDataTablesResponse responseBody = response.body();
testContext().set(CREATE_DATATABLE_RESULT_KEY, responseBody);
testContext().set(DATATABLE_NAME, responseBody.getResourceIdentifier());
testContext().set(CREATE_DATATABLE_RESULT_KEY, response);
testContext().set(DATATABLE_NAME, response.getResourceIdentifier());
}
@When("A datatable for {string} is created with the following extra columns:")
public void whenDatatableCreatedWithFollowingExtraColumns(String entityTypeStr, DataTable dataTable) throws IOException {
public void whenDatatableCreatedWithFollowingExtraColumns(String entityTypeStr, DataTable dataTable) {
DatatableEntityType entityType = DatatableEntityType.fromString(entityTypeStr);
List<List<String>> rows = dataTable.asLists();
List<List<String>> rowsWithoutHeader = rows.subList(1, rows.size());
List<PostColumnHeaderData> columns = createDatatableColumnsRequest(rowsWithoutHeader);
PostDataTablesRequest request = createDatatableRequest(entityType, columns);
Response<PostDataTablesResponse> response = dataTablesApi.createDatatable(request).execute();
checkSuccessfulApiCall(response);
PostDataTablesResponse response = ok(() -> fineractClient.dataTables().createDatatable(request, Map.of()));
PostDataTablesResponse responseBody = response.body();
testContext().set(CREATE_DATATABLE_RESULT_KEY, responseBody);
testContext().set(DATATABLE_NAME, responseBody.getResourceIdentifier());
testContext().set(CREATE_DATATABLE_RESULT_KEY, response);
testContext().set(DATATABLE_NAME, response.getResourceIdentifier());
}
private List<PostColumnHeaderData> createDatatableColumnsRequest(List<List<String>> rowsWithoutHeader) {
@@ -104,17 +99,15 @@ public class DatatablesStepDef extends AbstractStepDef {
}
@When("A multirow datatable for {string} is created")
public void whenMultirowDatatableCreated(String entityTypeStr) throws IOException {
public void whenMultirowDatatableCreated(String entityTypeStr) {
DatatableEntityType entityType = DatatableEntityType.fromString(entityTypeStr);
List<PostColumnHeaderData> columns = createRandomDatatableColumnsRequest();
PostDataTablesRequest request = createDatatableRequest(entityType, columns, true);
Response<PostDataTablesResponse> response = dataTablesApi.createDatatable(request).execute();
checkSuccessfulApiCall(response);
PostDataTablesResponse response = ok(() -> fineractClient.dataTables().createDatatable(request, Map.of()));
PostDataTablesResponse responseBody = response.body();
testContext().set(CREATE_DATATABLE_RESULT_KEY, responseBody);
testContext().set(DATATABLE_NAME, responseBody.getResourceIdentifier());
testContext().set(CREATE_DATATABLE_RESULT_KEY, response);
testContext().set(DATATABLE_NAME, response.getResourceIdentifier());
}
private List<PostColumnHeaderData> createRandomDatatableColumnsRequest() {
@@ -145,12 +138,10 @@ public class DatatablesStepDef extends AbstractStepDef {
}
@Then("The following column definitions match:")
public void thenColumnsMatch(DataTable dataTable) throws IOException {
public void thenColumnsMatch(DataTable dataTable) {
String datatableName = testContext().get(DATATABLE_NAME);
Response<GetDataTablesResponse> httpResponse = dataTablesApi.getDatatable(datatableName).execute();
checkSuccessfulApiCall(httpResponse);
GetDataTablesResponse response = ok(() -> fineractClient.dataTables().getDatatable(datatableName, Map.of()));
GetDataTablesResponse response = httpResponse.body();
Map<String, ResultsetColumnHeaderData> columnMap = response.getColumnHeaderData().stream()
.collect(Collectors.toMap(ResultsetColumnHeaderData::getColumnName, identity()));
@@ -176,22 +167,25 @@ public class DatatablesStepDef extends AbstractStepDef {
}
@When("The client calls the query endpoint for the created datatable with {string} column filter, and {string} value filter")
public void thenColum23nsMatch(String columnFilter, String valueFilter) throws IOException {
Response<String> response = dataTablesApi.queryValues(testContext().get(DATATABLE_NAME), columnFilter, valueFilter, columnFilter)
.execute();
testContext().set(DATATABLE_QUERY_RESPONSE, response);
public void thenColum23nsMatch(String columnFilter, String valueFilter) {
try {
fineractClient.dataTables().queryValues(testContext().get(DATATABLE_NAME),
Map.of("columnFilter", columnFilter, "valueFilter", valueFilter, "resultColumns", columnFilter));
} catch (FeignException e) {
testContext().set(DATATABLE_QUERY_RESPONSE, e);
}
}
@Then("The status of the HTTP response should be {int}")
public void thenStatusCodeMatch(int statusCode) {
Response<String> response = testContext().get(DATATABLE_QUERY_RESPONSE);
assertThat(response.code()).isEqualTo(statusCode);
FeignException exception = testContext().get(DATATABLE_QUERY_RESPONSE);
assertThat(exception.status()).isEqualTo(statusCode);
}
@Then("The response body should contain the following message: {string}")
public void thenColumnsMatch(String json) throws IOException {
Response<String> response = testContext().get(DATATABLE_QUERY_RESPONSE);
String jsonResponse = response.errorBody().string();
public void thenColumnsMatch(String json) {
FeignException exception = testContext().get(DATATABLE_QUERY_RESPONSE);
String jsonResponse = exception.responseBodyAsString();
assertThat(jsonResponse).contains(json);
}
}
@@ -18,16 +18,50 @@
*/
package org.apache.fineract.test.stepdef.hook;
import static org.awaitility.Awaitility.await;
import io.cucumber.java.Before;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicBoolean;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.test.messaging.config.EventProperties;
import org.apache.fineract.test.messaging.store.EventStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.config.JmsListenerEndpointRegistry;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
@Slf4j
public class MessagingHook {
@Autowired
private EventStore eventStore;
@Before
@Autowired(required = false)
private JmsListenerEndpointRegistry registry;
@Autowired(required = false)
private EventProperties eventProperties;
private static final AtomicBoolean jmsStartupDelayCompleted = new AtomicBoolean(false);
private static final Duration STARTUP_TIMEOUT = Duration.ofSeconds(20);
@Before(order = 0)
public void waitForJmsListenerStartup() {
if (jmsStartupDelayCompleted.compareAndSet(false, true) && eventProperties != null
&& eventProperties.isEventVerificationEnabled()) {
if (registry == null) {
log.warn("=== JmsListenerEndpointRegistry not available - skipping JMS listener readiness check ===");
return;
}
log.info("=== FIRST SCENARIO - Waiting for JMS Listener to connect to ActiveMQ (max {}s) ===", STARTUP_TIMEOUT.toSeconds());
DefaultMessageListenerContainer container = (DefaultMessageListenerContainer) registry
.getListenerContainer("eventStoreListener");
await().atMost(STARTUP_TIMEOUT).pollInterval(Duration.ofMillis(200)).until(container::isRunning);
log.info("=== JMS Listener is running - tests can proceed ===");
}
}
@Before(order = 1)
public void emptyEventStore() {
eventStore.reset();
}
@@ -18,25 +18,23 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import io.cucumber.java.en.When;
import java.io.IOException;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.ChargeRequest;
import org.apache.fineract.client.models.PutChargesChargeIdResponse;
import org.apache.fineract.client.services.ChargesApi;
import org.apache.fineract.test.data.ChargeCalculationType;
import org.apache.fineract.test.data.ChargeProductType;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class ChargeStepDef extends AbstractStepDef {
@Autowired
private ChargesApi chargesApi;
private FineractFeignClient fineractClient;
@When("Admin updates charge {string} with {string} calculation type and {double} % of transaction amount")
public void updateCharge(String chargeType, String chargeCalculationType, double amount) throws IOException {
public void updateCharge(String chargeType, String chargeCalculationType, double amount) {
ChargeRequest disbursementChargeUpdateRequest = new ChargeRequest();
ChargeCalculationType chargeProductTypeValue = ChargeCalculationType.valueOf(chargeCalculationType);
disbursementChargeUpdateRequest.chargeCalculationType(chargeProductTypeValue.value).amount(amount).locale("en");
@@ -44,13 +42,11 @@ public class ChargeStepDef extends AbstractStepDef {
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeId = chargeProductType.getValue();
Response<PutChargesChargeIdResponse> responseDisbursementCharge = chargesApi.updateCharge(chargeId, disbursementChargeUpdateRequest)
.execute();
ErrorHelper.checkSuccessfulApiCall(responseDisbursementCharge);
ok(() -> fineractClient.charges().updateCharge(chargeId, disbursementChargeUpdateRequest));
}
@When("Admin updates charge {string} with {string} calculation type and {double} EUR amount")
public void updateChargeWithFlatAmount(String chargeType, String chargeCalculationType, double flatAmount) throws IOException {
public void updateChargeWithFlatAmount(String chargeType, String chargeCalculationType, double flatAmount) {
ChargeRequest disbursementChargeUpdateRequest = new ChargeRequest();
ChargeCalculationType chargeProductTypeValue = ChargeCalculationType.valueOf(chargeCalculationType);
disbursementChargeUpdateRequest.chargeCalculationType(chargeProductTypeValue.value).amount(flatAmount).locale("en");
@@ -58,8 +54,6 @@ public class ChargeStepDef extends AbstractStepDef {
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeId = chargeProductType.getValue();
Response<PutChargesChargeIdResponse> responseDisbursementCharge = chargesApi.updateCharge(chargeId, disbursementChargeUpdateRequest)
.execute();
ErrorHelper.checkSuccessfulApiCall(responseDisbursementCharge);
ok(() -> fineractClient.charges().updateCharge(chargeId, disbursementChargeUpdateRequest));
}
}
@@ -18,17 +18,16 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.InlineJobRequest;
import org.apache.fineract.client.models.InlineJobResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.ExternalEventConfigurationApi;
import org.apache.fineract.client.services.InlineJobApi;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.assetexternalization.LoanAccountCustomSnapshotEvent;
import org.apache.fineract.test.messaging.event.loan.repayment.LoanRepaymentDueEvent;
@@ -36,52 +35,47 @@ import org.apache.fineract.test.messaging.event.loan.repayment.LoanRepaymentOver
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class InlineCOBStepDef extends AbstractStepDef {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy");
@Autowired
private InlineJobApi inlineJobApi;
private FineractFeignClient fineractClient;
@Autowired
private EventAssertion eventAssertion;
@Autowired
ExternalEventConfigurationApi eventConfigurationApi;
@When("Admin runs inline COB job for Loan")
public void runInlineCOB() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
InlineJobRequest inlineJobRequest = new InlineJobRequest().addLoanIdsItem(loanId);
Response<InlineJobResponse> inlineJobResponse = inlineJobApi.executeInlineJob("LOAN_COB", inlineJobRequest).execute();
ErrorHelper.checkSuccessfulApiCall(inlineJobResponse);
ok(() -> fineractClient.inlineJob().executeInlineJob("LOAN_COB", inlineJobRequest));
}
@Then("Loan Repayment Due Business Event is created")
public void checkLoanRepaymentDueBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanRepaymentDueEvent.class, loanId);
}
@Then("Loan Repayment Overdue Business Event is created")
public void checkLoanRepaymentOverdueBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanRepaymentOverdueEvent.class, loanId);
}
@Then("LoanAccountCustomSnapshotBusinessEvent is created with business date {string}")
public void checkLoanRepaymentDueBusinessEventCreatedWithBusinessDate(String expectedBusinessDate) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
LocalDate expectedBusinessDateParsed = LocalDate.parse(expectedBusinessDate, FORMATTER);
eventAssertion.assertEvent(LoanAccountCustomSnapshotEvent.class, loanId).isRaisedOnBusinessDate(expectedBusinessDateParsed);
@@ -18,6 +18,8 @@
*/
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.ok;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java.en.Then;
@@ -25,41 +27,31 @@ import io.cucumber.java.en.When;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.LoanAccountLock;
import org.apache.fineract.client.models.LoanAccountLockResponseDTO;
import org.apache.fineract.client.models.OldestCOBProcessedLoanDTO;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.DefaultApi;
import org.apache.fineract.client.services.LoanAccountLockApi;
import org.apache.fineract.client.services.LoanCobCatchUpApi;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class LoanCOBStepDef extends AbstractStepDef {
@Autowired
private LoanCobCatchUpApi loanCobCatchUpApi;
@Autowired
private LoanAccountLockApi loanAccountLockApi;
@Autowired
private DefaultApi defaultApi;
private FineractFeignClient fineractClient;
@Then("The cobProcessedDate of the oldest loan processed by COB is more than 1 day earlier than cobBusinessDate")
public void checkOldestCOBProcessed() throws IOException {
Response<OldestCOBProcessedLoanDTO> response = loanCobCatchUpApi.getOldestCOBProcessedLoan().execute();
ErrorHelper.checkSuccessfulApiCall(response);
OldestCOBProcessedLoanDTO response = ok(() -> fineractClient.loanCobCatchUp().getOldestCOBProcessedLoan());
LocalDate cobDate = response.body().getCobBusinessDate();
LocalDate cobDate = response.getCobBusinessDate();
LocalDate cobDateMinusOne = cobDate.minusDays(1);
LocalDate cobProcessedDate = response.body().getCobProcessedDate();
LocalDate cobProcessedDate = response.getCobProcessedDate();
log.debug("cobDateMinusOne: {}", cobDateMinusOne);
log.debug("cobProcessedDate: {}", cobProcessedDate);
@@ -69,23 +61,23 @@ public class LoanCOBStepDef extends AbstractStepDef {
@Then("There are no locked loan accounts")
public void listOfLockedLoansEmpty() throws IOException {
Response<LoanAccountLockResponseDTO> response = loanAccountLockApi.retrieveLockedAccounts(0, 1000).execute();
ErrorHelper.checkSuccessfulApiCall(response);
LoanAccountLockResponseDTO response = ok(
() -> fineractClient.loanAccountLock().retrieveLockedAccounts(Map.<String, Object>of("page", 0, "size", 1000)));
int size = response.body().getContent().size();
int size = response.getContent().size();
assertThat(size).as(ErrorMessageHelper.listOfLockedLoansNotEmpty(response)).isEqualTo(0);
log.debug("Size of List of the locked loans: {}", size);
}
@Then("The loan account is not locked")
public void loanIsNotInListOfLockedLoans() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long targetLoanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long targetLoanId = loanResponse.getLoanId();
Response<LoanAccountLockResponseDTO> response = loanAccountLockApi.retrieveLockedAccounts(0, 1000).execute();
ErrorHelper.checkSuccessfulApiCall(response);
LoanAccountLockResponseDTO response = ok(
() -> fineractClient.loanAccountLock().retrieveLockedAccounts(Map.<String, Object>of("page", 0, "size", 1000)));
List<LoanAccountLock> content = response.body().getContent();
List<LoanAccountLock> content = response.getContent();
boolean contains = content.stream()//
.map(LoanAccountLock::getLoanId)//
.anyMatch(targetLoanId::equals);//
@@ -95,19 +87,17 @@ public class LoanCOBStepDef extends AbstractStepDef {
@When("Admin places a lock on loan account with an error message")
public void placeLockOnLoanAccountWithErrorMessage() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<Void> response = defaultApi.placeLockOnLoanAccount(loanId, "LOAN_COB_CHUNK_PROCESSING", "TestError").execute();
ErrorHelper.checkSuccessfulApiCall(response);
executeVoid(() -> fineractClient.defaultApi().placeLockOnLoanAccount(loanId, "LOAN_COB_CHUNK_PROCESSING", "TestError"));
}
@When("Admin places a lock on loan account WITHOUT an error message")
public void placeLockOnLoanAccountNoErrorMessage() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<Void> response = defaultApi.placeLockOnLoanAccount(loanId, "LOAN_COB_CHUNK_PROCESSING").execute();
ErrorHelper.checkSuccessfulApiCall(response);
executeVoid(() -> fineractClient.defaultApi().placeLockOnLoanAccount(loanId, "LOAN_COB_CHUNK_PROCESSING", ""));
}
}
@@ -18,22 +18,27 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.CapitalizedIncomeDetails;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanCapitalizedIncomeAmortizationTransactionCreatedEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class LoanCapitalizedIncomeStepDef extends AbstractStepDef {
@@ -42,20 +47,20 @@ public class LoanCapitalizedIncomeStepDef extends AbstractStepDef {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
@Autowired
LoansApi loansApi;
FineractFeignClient fineractClient;
@Autowired
EventAssertion eventAssertion;
@Then("Loan Capitalized Income Amortization Transaction Created Business Event is created on {string}")
public void checkLoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEventCreated(String date) throws IOException {
Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.body().getLoanId();
public void checkLoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEventCreated(String date) {
PostLoansResponse loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
GetLoansLoanIdResponse loanDetailsResponse = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "transactions")));
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
GetLoansLoanIdTransactions capitalizedIncomeAmortizationTransaction = transactions.stream()
.filter(t -> date.equals(FORMATTER.format(t.getDate())) && "Capitalized Income Amortization".equals(t.getType().getValue()))
.reduce((first, second) -> second).orElseThrow(
@@ -66,4 +71,90 @@ public class LoanCapitalizedIncomeStepDef extends AbstractStepDef {
capitalizedIncomeAmortizationTransactionId);
}
@Then("Deferred Capitalized Income contains the following data:")
public void verifyDeferredCapitalizedIncome(DataTable dataTable) {
PostLoansResponse loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.getLoanId();
List<CapitalizedIncomeDetails> capitalizedIncomeDetails = ok(
() -> fineractClient.loanCapitalizedIncome().fetchCapitalizedIncomeDetails(loanId));
List<Map<String, String>> data = dataTable.asMaps();
Map<String, String> expectedData = data.get(0);
assertThat(capitalizedIncomeDetails).isNotNull().isNotEmpty();
CapitalizedIncomeDetails actualData = capitalizedIncomeDetails.get(0);
BigDecimal expectedAmount = new BigDecimal(expectedData.get("Amount"));
BigDecimal expectedAmortizedAmount = new BigDecimal(expectedData.get("Amortized Amount"));
BigDecimal expectedUnrecognizedAmount = new BigDecimal(expectedData.get("Unrecognized Amount"));
BigDecimal expectedAdjustedAmount = new BigDecimal(expectedData.get("Adjusted Amount"));
BigDecimal expectedChargedOffAmount = new BigDecimal(expectedData.get("Charged Off Amount"));
BigDecimal actualAmount = actualData.getAmount() != null ? actualData.getAmount() : BigDecimal.ZERO;
BigDecimal actualAmortizedAmount = actualData.getAmortizedAmount() != null ? actualData.getAmortizedAmount() : BigDecimal.ZERO;
BigDecimal actualUnrecognizedAmount = actualData.getUnrecognizedAmount() != null ? actualData.getUnrecognizedAmount()
: BigDecimal.ZERO;
BigDecimal actualAdjustedAmount = actualData.getAmountAdjustment() != null ? actualData.getAmountAdjustment() : BigDecimal.ZERO;
BigDecimal actualChargedOffAmount = actualData.getChargedOffAmount() != null ? actualData.getChargedOffAmount() : BigDecimal.ZERO;
assertThat(actualAmount).as(ErrorMessageHelper.wrongAmountInDeferredCapitalizedIncome(actualAmount, expectedAmount))
.isEqualByComparingTo(expectedAmount);
assertThat(actualAmortizedAmount)
.as(ErrorMessageHelper.wrongAmountInDeferredCapitalizedIncome(actualAmortizedAmount, expectedAmortizedAmount))
.isEqualByComparingTo(expectedAmortizedAmount);
assertThat(actualUnrecognizedAmount)
.as(ErrorMessageHelper.wrongAmountInDeferredCapitalizedIncome(actualUnrecognizedAmount, expectedUnrecognizedAmount))
.isEqualByComparingTo(expectedUnrecognizedAmount);
assertThat(actualAdjustedAmount)
.as(ErrorMessageHelper.wrongAmountInDeferredCapitalizedIncome(actualAdjustedAmount, expectedAdjustedAmount))
.isEqualByComparingTo(expectedAdjustedAmount);
assertThat(actualChargedOffAmount)
.as(ErrorMessageHelper.wrongAmountInDeferredCapitalizedIncome(actualChargedOffAmount, expectedChargedOffAmount))
.isEqualByComparingTo(expectedChargedOffAmount);
}
@Then("Deferred Capitalized Income by external-id contains the following data:")
public void verifyDeferredCapitalizedIncomeByExternalId(DataTable dataTable) {
PostLoansResponse loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanCreateResponse.getResourceExternalId();
List<CapitalizedIncomeDetails> capitalizedIncomeDetails = ok(
() -> fineractClient.loanCapitalizedIncome().fetchCapitalizedIncomeDetailsByExternalId(loanExternalId));
List<Map<String, String>> data = dataTable.asMaps();
Map<String, String> expectedData = data.get(0);
assertThat(capitalizedIncomeDetails).isNotNull().isNotEmpty();
CapitalizedIncomeDetails actualData = capitalizedIncomeDetails.get(0);
BigDecimal expectedAmount = new BigDecimal(expectedData.get("Amount"));
BigDecimal expectedAmortizedAmount = new BigDecimal(expectedData.get("Amortized Amount"));
BigDecimal expectedUnrecognizedAmount = new BigDecimal(expectedData.get("Unrecognized Amount"));
BigDecimal expectedAdjustedAmount = new BigDecimal(expectedData.get("Adjusted Amount"));
BigDecimal expectedChargedOffAmount = new BigDecimal(expectedData.get("Charged Off Amount"));
BigDecimal actualAmount = actualData.getAmount() != null ? actualData.getAmount() : BigDecimal.ZERO;
BigDecimal actualAmortizedAmount = actualData.getAmortizedAmount() != null ? actualData.getAmortizedAmount() : BigDecimal.ZERO;
BigDecimal actualUnrecognizedAmount = actualData.getUnrecognizedAmount() != null ? actualData.getUnrecognizedAmount()
: BigDecimal.ZERO;
BigDecimal actualAdjustedAmount = actualData.getAmountAdjustment() != null ? actualData.getAmountAdjustment() : BigDecimal.ZERO;
BigDecimal actualChargedOffAmount = actualData.getChargedOffAmount() != null ? actualData.getChargedOffAmount() : BigDecimal.ZERO;
assertThat(actualAmount).as(ErrorMessageHelper.wrongAmountInDeferredCapitalizedIncome(actualAmount, expectedAmount))
.isEqualByComparingTo(expectedAmount);
assertThat(actualAmortizedAmount)
.as(ErrorMessageHelper.wrongAmountInDeferredCapitalizedIncome(actualAmortizedAmount, expectedAmortizedAmount))
.isEqualByComparingTo(expectedAmortizedAmount);
assertThat(actualUnrecognizedAmount)
.as(ErrorMessageHelper.wrongAmountInDeferredCapitalizedIncome(actualUnrecognizedAmount, expectedUnrecognizedAmount))
.isEqualByComparingTo(expectedUnrecognizedAmount);
assertThat(actualAdjustedAmount)
.as(ErrorMessageHelper.wrongAmountInDeferredCapitalizedIncome(actualAdjustedAmount, expectedAdjustedAmount))
.isEqualByComparingTo(expectedAdjustedAmount);
assertThat(actualChargedOffAmount)
.as(ErrorMessageHelper.wrongAmountInDeferredCapitalizedIncome(actualChargedOffAmount, expectedChargedOffAmount))
.isEqualByComparingTo(expectedChargedOffAmount);
}
}
@@ -18,9 +18,9 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
@@ -28,24 +28,20 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FeignException;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.BusinessDateResponse;
import org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.BusinessDateManagementApi;
import org.apache.fineract.client.services.LoanChargesApi;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.data.ChargeProductType;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.messaging.event.EventCheckHelper;
@@ -53,23 +49,15 @@ 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;
import retrofit2.Response;
@Slf4j
public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy";
private static final Gson GSON = new JSON().getGson();
@Autowired
private FineractFeignClient fineractClient;
@Autowired
private LoanChargesApi loanChargesApi;
@Autowired
private LoansApi loansApi;
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private BusinessDateManagementApi businessDateManagementApi;
@Autowired
private EventCheckHelper eventCheckHelper;
@Autowired
@@ -78,11 +66,11 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
@When("Admin makes a charge adjustment for the last {string} type charge which is due on {string} with {double} EUR transaction amount and externalId {string}")
public void makeLoanChargeAdjustment(String chargeTypeEnum, String date, Double transactionAmount, String externalId)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "charges", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
GetLoansLoanIdResponse loanDetailsResponse = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "charges")));
Long transactionId = getTransactionIdForLastChargeMetConditions(chargeTypeEnum, date, loanDetailsResponse);
makeChargeAdjustmentCall(loanId, transactionId, externalId, transactionAmount);
@@ -90,71 +78,72 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
@Then("Charge adjustment for the last {string} type charge which is due on {string} with transaction amount {double} which is higher than the available charge amount results an ERROR")
public void loanChargeAdjustmentFailedOnWrongAmount(String chargeTypeEnum, String date, double amount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "charges", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
GetLoansLoanIdResponse loanDetailsResponse = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "charges")));
Long transactionId = getTransactionIdForLastChargeMetConditions(chargeTypeEnum, date, loanDetailsResponse);
PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest().amount(amount)
.externalId("");
Response<PostLoansLoanIdChargesChargeIdResponse> chargeAdjustmentResponseFail = loanChargesApi
.executeLoanCharge2(loanId, transactionId, chargeAdjustmentRequest, "adjustment").execute();
String string = chargeAdjustmentResponseFail.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(string, ErrorResponse.class);
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
String developerMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
Integer httpStatusCodeExpected = 403;
String developerMessageExpected = "Transaction amount cannot be higher than the available charge amount for adjustment: 7.000000";
assertThat(httpStatusCodeActual)
.as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected))
.isEqualTo(httpStatusCodeExpected);
assertThat(developerMessageActual)
.as(ErrorMessageHelper.wrongErrorMessageInFailedChargeAdjustment(developerMessageActual, developerMessageExpected))
.isEqualTo(developerMessageExpected);
try {
fineractClient.loanCharges().executeLoanCharge2(loanId, transactionId, chargeAdjustmentRequest,
Map.<String, Object>of("command", "adjustment"));
throw new AssertionError("Expected FeignException but request succeeded");
} catch (FeignException e) {
ErrorResponse errorResponse = ErrorResponse.fromFeignException(e);
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
String developerMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
log.debug("Error code: {}", httpStatusCodeActual);
log.debug("Error message: {}", developerMessageActual);
assertThat(httpStatusCodeActual)
.as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected))
.isEqualTo(httpStatusCodeExpected);
assertThat(developerMessageActual)
.as(ErrorMessageHelper.wrongErrorMessageInFailedChargeAdjustment(developerMessageActual, developerMessageExpected))
.isEqualTo(developerMessageExpected);
log.debug("Error code: {}", httpStatusCodeActual);
log.debug("Error message: {}", developerMessageActual);
}
}
@When("Admin reverts the charge adjustment which was raised on {string} with {double} EUR transaction amount")
public void loanChargeAdjustmentUndo(String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
GetLoansLoanIdResponse loanDetailsResponse = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "transactions")));
Long transactionId = getTransactionIdForTransactionMetConditions(transactionDate, transactionAmount, loanDetailsResponse);
Response<List<BusinessDateResponse>> businessDateResponse = businessDateManagementApi.getBusinessDates().execute();
LocalDate businessDate = businessDateResponse.body().get(0).getDate();
BusinessDateResponse businessDateResponse = ok(
() -> fineractClient.businessDateManagement().getBusinessDate("BUSINESS_DATE", Map.of()));
LocalDate businessDate = businessDateResponse.getDate();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String businessDateActual = formatter.format(businessDate);
PostLoansLoanIdTransactionsTransactionIdRequest chargeAdjustmentUndoRequest = LoanRequestFactory
.defaultChargeAdjustmentTransactionUndoRequest().transactionDate(businessDateActual);
Response<PostLoansLoanIdTransactionsResponse> chargeAdjustmentUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, transactionId, chargeAdjustmentUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(chargeAdjustmentUndoResponse);
ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId, transactionId, chargeAdjustmentUndoRequest,
Map.<String, Object>of()));
}
@Then("Charge adjustment response has the subResourceExternalId")
public void checkChargeAdjustmentResponse() {
final Response<PostLoansLoanIdChargesChargeIdResponse> response = testContext().get(TestContextKey.LOAN_CHARGE_ADJUSTMENT_RESPONSE);
final PostLoansLoanIdChargesChargeIdResponse body = response.body();
assertThat(body.getSubResourceExternalId()).isNotNull();
final PostLoansLoanIdChargesChargeIdResponse response = testContext().get(TestContextKey.LOAN_CHARGE_ADJUSTMENT_RESPONSE);
assertThat(response.getSubResourceExternalId()).isNotNull();
}
private Long getTransactionIdForTransactionMetConditions(String transactionDate, double transactionAmount,
Response<GetLoansLoanIdResponse> loanDetailsResponse) {
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
GetLoansLoanIdResponse loanDetailsResponse) {
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
GetLoansLoanIdTransactions transactionMetConditions = new GetLoansLoanIdTransactions();
for (int i = 0; i < transactions.size(); i++) {
LocalDate date = transactions.get(i).getDate();
@@ -176,16 +165,15 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest()
.amount(transactionAmount).externalId(externalId);
Response<PostLoansLoanIdChargesChargeIdResponse> chargeAdjustmentResponse = loanChargesApi
.executeLoanCharge2(loanId, transactionId, chargeAdjustmentRequest, "adjustment").execute();
PostLoansLoanIdChargesChargeIdResponse chargeAdjustmentResponse = ok(() -> fineractClient.loanCharges().executeLoanCharge2(loanId,
transactionId, chargeAdjustmentRequest, Map.<String, Object>of("command", "adjustment")));
testContext().set(TestContextKey.LOAN_CHARGE_ADJUSTMENT_RESPONSE, chargeAdjustmentResponse);
ErrorHelper.checkSuccessfulApiCall(chargeAdjustmentResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
private Long getTransactionIdForLastChargeMetConditions(String chargeTypeEnum, String date,
Response<GetLoansLoanIdResponse> loanDetailsResponse) {
List<GetLoansLoanIdLoanChargeData> charges = loanDetailsResponse.body().getCharges();
GetLoansLoanIdResponse loanDetailsResponse) {
List<GetLoansLoanIdLoanChargeData> charges = loanDetailsResponse.getCharges();
ChargeProductType chargeType = ChargeProductType.valueOf(chargeTypeEnum);
Long chargeProductId = chargeType.getValue();
@@ -18,14 +18,18 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionEnumDataV1;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
@@ -33,12 +37,9 @@ import org.apache.fineract.client.models.GetLoansType;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.test.data.paymenttype.DefaultPaymentType;
import org.apache.fineract.test.data.paymenttype.PaymentTypeResolver;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.EventCheckHelper;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanChargebackTransactionEvent;
@@ -46,15 +47,11 @@ 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;
import retrofit2.Response;
public class LoanChargeBackStepDef extends AbstractStepDef {
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private LoansApi loansApi;
private FineractFeignClient fineractClient;
@Autowired
private EventAssertion eventAssertion;
@@ -70,22 +67,22 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
@When("Admin makes {string} chargeback with {double} EUR transaction amount")
public void makeLoanChargeback(String repaymentType, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Long transactionId = Long.valueOf(repaymentResponse.body().getResourceId());
PostLoansLoanIdTransactionsResponse repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Long transactionId = Long.valueOf(repaymentResponse.getResourceId());
makeChargebackCall(loanId, transactionId, repaymentType, transactionAmount);
}
@When("Admin makes {string} chargeback with {double} EUR transaction amount for Payment nr. {double}")
public void makeLoanChargebackForPayment(String repaymentType, double transactionAmount, double paymentNr) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "transactions")));
List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
List<Long> transactionIdList = new ArrayList<>();
for (GetLoansLoanIdTransactions f : transactions) {
@@ -103,11 +100,11 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
@When("Admin makes {string} chargeback with {double} EUR transaction amount for Downpayment nr. {double}")
public void makeLoanChargebackForDownpaymentayment(String repaymentType, double transactionAmount, double paymentNr)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "transactions")));
List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
List<Long> transactionIdList = new ArrayList<>();
for (GetLoansLoanIdTransactions f : transactions) {
@@ -124,11 +121,11 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
@When("Admin makes {string} chargeback with {double} EUR transaction amount for MIR nr. {double}")
public void makeLoanChargebackForMIR(String repaymentType, double transactionAmount, double paymentNr) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "transactions")));
List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
List<Long> transactionIdList = new ArrayList<>();
for (GetLoansLoanIdTransactions f : transactions) {
@@ -151,16 +148,14 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest chargebackRequest = LoanRequestFactory.defaultChargebackRequest()
.paymentTypeId(paymentTypeValue).transactionAmount(transactionAmount);
Response<PostLoansLoanIdTransactionsResponse> chargebackResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, transactionId, chargebackRequest, "chargeback").execute();
PostLoansLoanIdTransactionsResponse chargebackResponse = ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
transactionId, chargebackRequest, Map.of("command", "chargeback")));
testContext().set(TestContextKey.LOAN_CHARGEBACK_RESPONSE, chargebackResponse);
ErrorHelper.checkSuccessfulApiCall(chargebackResponse);
checkEvents(chargebackResponse);
}
private void checkEvents(Response<PostLoansLoanIdTransactionsResponse> chargebackResponse) throws IOException {
PostLoansLoanIdTransactionsResponse responseBody = chargebackResponse.body();
private void checkEvents(PostLoansLoanIdTransactionsResponse chargebackResponse) throws IOException {
PostLoansLoanIdTransactionsResponse responseBody = chargebackResponse;
Long loanId = responseBody.getLoanId();
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
@@ -174,11 +169,8 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
long transactionId = Long.valueOf(chargebackResponse.getResourceId());
// retrieve transaction details
Response<GetLoansLoanIdTransactionsTransactionIdResponse> transactionResponse = loanTransactionsApi
.retrieveTransaction(loanId, transactionId, "").execute();
ErrorHelper.checkSuccessfulApiCall(transactionResponse);
GetLoansLoanIdTransactionsTransactionIdResponse transactionResponseBody = transactionResponse.body();
GetLoansLoanIdTransactionsTransactionIdResponse transactionResponseBody = ok(
() -> fineractClient.loanTransactions().retrieveTransaction(loanId, transactionId, Map.of()));
// Get transaction type from response
GetLoansType transactionType = transactionResponseBody.getType();
@@ -18,9 +18,9 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Then;
@@ -29,8 +29,11 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.avro.loan.v1.LoanChargeDataV1;
import org.apache.fineract.client.feign.FeignException;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetLoansLoanIdChargesChargeIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
@@ -41,14 +44,9 @@ import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PutChargeTransactionChangesRequest;
import org.apache.fineract.client.models.PutChargeTransactionChangesResponse;
import org.apache.fineract.client.services.LoanChargesApi;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.data.ChargeProductType;
import org.apache.fineract.test.data.ErrorMessageType;
import org.apache.fineract.test.factory.LoanChargeRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.messaging.EventAssertion;
@@ -58,7 +56,6 @@ 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;
import retrofit2.Response;
@Slf4j
public class LoanChargeStepDef extends AbstractStepDef {
@@ -66,14 +63,10 @@ public class LoanChargeStepDef extends AbstractStepDef {
public static final String DEFAULT_DATE_FORMAT = "dd MMMM yyyy";
public static final String DATE_FORMAT_EVENTS = "yyyy-MM-dd";
public static final Double DEFAULT_CHARGE_FEE_FLAT = 10D;
private static final Gson GSON = new JSON().getGson();
@Autowired
private LoanChargesApi loanChargesApi;
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private LoansApi loansApi;
private FineractFeignClient fineractClient;
@Autowired
private EventAssertion eventAssertion;
@Autowired
@@ -83,8 +76,8 @@ public class LoanChargeStepDef extends AbstractStepDef {
@When("Admin adds {string} due date charge with {string} due date and {double} EUR transaction amount")
public void addChargeDueDate(String chargeType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeTypeId = chargeProductType.getValue();
@@ -97,9 +90,8 @@ public class LoanChargeStepDef extends AbstractStepDef {
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.dueDate(transactionDate).amount(transactionAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
PostLoansLoanIdChargesResponse loanChargeResponse = ok(
() -> fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse);
testContext().set(TestContextKey.ADD_NSF_FEE_RESPONSE, loanChargeResponse);
@@ -108,8 +100,8 @@ public class LoanChargeStepDef extends AbstractStepDef {
@When("Admin adds {string} charge with {double} % of transaction amount")
public void addChargePercentage(String chargeType, double transactionPercentageAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeTypeId = chargeProductType.getValue();
@@ -123,17 +115,16 @@ public class LoanChargeStepDef extends AbstractStepDef {
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.amount(transactionPercentageAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
PostLoansLoanIdChargesResponse loanChargeResponse = ok(
() -> fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse);
}
@When("Admin adds {string} installment charge with {double} amount")
public void addInstallmentFeeCharge(final String chargeType, final double amount) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse.body() != null;
final long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse != null;
final long loanId = loanResponse.getLoanId();
final ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
final Long chargeTypeId = chargeProductType.getValue();
@@ -148,39 +139,40 @@ public class LoanChargeStepDef extends AbstractStepDef {
final PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
.chargeId(chargeTypeId).amount(amount);
final Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi
.executeLoanCharge(loanId, loanIdChargesRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
final PostLoansLoanIdChargesResponse loanChargeResponse = ok(
() -> fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.ADD_INSTALLMENT_FEE_CHARGE_RESPONSE, loanChargeResponse);
}
@Then("Admin fails to add {string} installment charge with {double} amount because of wrong charge calculation type")
public void addInstallmentFeeChargeFails(final String chargeType, final double amount) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse.body() != null;
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse != null;
final long loanId = loanResponse.body().getLoanId();
final long loanId = loanResponse.getLoanId();
final ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
final Long chargeTypeId = chargeProductType.getValue();
final PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
.chargeId(chargeTypeId).amount(amount);
final Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi
.executeLoanCharge(loanId, loanIdChargesRequest, "").execute();
testContext().set(TestContextKey.ADD_INSTALLMENT_FEE_CHARGE_RESPONSE, loanChargeResponse);
final ErrorResponse errorDetails = ErrorResponse.from(loanChargeResponse);
assertThat(errorDetails.getHttpStatusCode()).isEqualTo(400);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.isEqualTo(chargeProductType.equals(ChargeProductType.LOAN_INSTALLMENT_FEE_PERCENTAGE_INTEREST)
? ErrorMessageHelper.addInstallmentFeeInterestPercentageChargeFailure()
: ErrorMessageHelper.addInstallmentFeePrincipalPercentageChargeFailure());
try {
fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of());
throw new AssertionError("Expected FeignException but request succeeded");
} catch (FeignException e) {
final ErrorResponse errorDetails = ErrorResponse.fromFeignException(e);
assertThat(errorDetails.getHttpStatusCode()).isEqualTo(400);
String expectedMessage = chargeTypeId.equals(ChargeProductType.LOAN_INSTALLMENT_FEE_PERCENTAGE_INTEREST.getValue())
? ErrorMessageHelper.addInstallmentFeeInterestPercentageChargeFailure()
: ErrorMessageHelper.addInstallmentFeePrincipalPercentageChargeFailure();
assertThat(errorDetails.getSingleError().getDeveloperMessage()).contains(expectedMessage);
}
}
@Then("Admin is not able to add {string} due date charge with {string} due date and {double} EUR transaction amount because the of charged-off account")
public void addChargeDueDateOnChargedOff(String chargeType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeTypeId = chargeProductType.getValue();
@@ -188,27 +180,28 @@ public class LoanChargeStepDef extends AbstractStepDef {
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.dueDate(transactionDate).amount(transactionAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse);
ErrorResponse errorDetails = ErrorResponse.from(loanChargeResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addChargeForChargeOffLoanCodeMsg()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.isEqualTo(ErrorMessageHelper.addChargeForChargeOffLoanFailure(loanId));
try {
fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of());
throw new AssertionError("Expected FeignException but request succeeded");
} catch (FeignException e) {
ErrorResponse errorDetails = ErrorResponse.fromFeignException(e);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addChargeForChargeOffLoanCodeMsg()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.contains(ErrorMessageHelper.addChargeForChargeOffLoanFailure(loanId));
}
}
@And("Admin adds a {double} % Processing charge to the loan with {string} locale on date: {string}")
public void addProcessingFee(double chargeAmount, String locale, String date) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
.chargeId(ChargeProductType.LOAN_PERCENTAGE_PROCESSING_FEE.value).amount(chargeAmount).dueDate(date)
.dateFormat(DEFAULT_DATE_FORMAT).locale(locale);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
PostLoansLoanIdChargesResponse loanChargeResponse = ok(
() -> fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.ADD_PROCESSING_FEE_RESPONSE, loanChargeResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@@ -216,102 +209,91 @@ public class LoanChargeStepDef extends AbstractStepDef {
@And("Admin adds an NSF fee because of payment bounce with {string} transaction date")
public void addNSFfee(String date) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
.chargeId(ChargeProductType.LOAN_NSF_FEE.value).amount(DEFAULT_CHARGE_FEE_FLAT).dueDate(date)
.dateFormat(DEFAULT_DATE_FORMAT);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
PostLoansLoanIdChargesResponse loanChargeResponse = ok(
() -> fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.ADD_NSF_FEE_RESPONSE, loanChargeResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@And("Admin waives charge")
public void waiveCharge() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = testContext().get(TestContextKey.ADD_NSF_FEE_RESPONSE);
Long chargeId = Long.valueOf(loanChargeResponse.body().getResourceId());
PostLoansLoanIdChargesResponse loanChargeResponse = testContext().get(TestContextKey.ADD_NSF_FEE_RESPONSE);
Long chargeId = Long.valueOf(loanChargeResponse.getResourceId());
PostLoansLoanIdChargesChargeIdRequest waiveRequest = new PostLoansLoanIdChargesChargeIdRequest();
Response<PostLoansLoanIdChargesChargeIdResponse> waiveResponse = loanChargesApi
.executeLoanCharge2(loanId, chargeId, waiveRequest, "waive").execute();
ErrorHelper.checkSuccessfulApiCall(waiveResponse);
PostLoansLoanIdChargesChargeIdResponse waiveResponse = ok(() -> fineractClient.loanCharges().executeLoanCharge2(loanId, chargeId,
waiveRequest, Map.<String, Object>of("command", "waive")));
testContext().set(TestContextKey.WAIVE_CHARGE_RESPONSE, waiveResponse);
}
@And("Admin waives due date charge")
public void waiveDueDateCharge() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = testContext().get(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE);
Long chargeId = Long.valueOf(loanChargeResponse.body().getResourceId());
PostLoansLoanIdChargesResponse loanChargeResponse = testContext().get(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE);
Long chargeId = Long.valueOf(loanChargeResponse.getResourceId());
PostLoansLoanIdChargesChargeIdRequest waiveRequest = new PostLoansLoanIdChargesChargeIdRequest();
Response<PostLoansLoanIdChargesChargeIdResponse> waiveResponse = loanChargesApi
.executeLoanCharge2(loanId, chargeId, waiveRequest, "waive").execute();
ErrorHelper.checkSuccessfulApiCall(waiveResponse);
PostLoansLoanIdChargesChargeIdResponse waiveResponse = ok(() -> fineractClient.loanCharges().executeLoanCharge2(loanId, chargeId,
waiveRequest, Map.<String, Object>of("command", "waive")));
testContext().set(TestContextKey.WAIVE_CHARGE_RESPONSE, waiveResponse);
}
@And("Admin makes waive undone for charge")
public void undoWaiveForCharge() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions();
GetLoansLoanIdResponse loanDetails = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")));
List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
Long transactionId = 0L;
for (GetLoansLoanIdTransactions f : transactions) {
String code = f.getType().getCode();
if (code.equals("loanTransactionType.waiveCharges")) {
transactionId = f.getId();
}
}
final Long transactionId = transactions.stream().filter(t -> "loanTransactionType.waiveCharges".equals(t.getType().getCode()))
.findFirst().map(GetLoansLoanIdTransactions::getId).orElse(0L);
PutChargeTransactionChangesRequest undoWaiveRequest = new PutChargeTransactionChangesRequest();
Response<PutChargeTransactionChangesResponse> undoWaiveResponse = loanTransactionsApi
.undoWaiveCharge(loanId, transactionId, undoWaiveRequest).execute();
ErrorHelper.checkSuccessfulApiCall(undoWaiveResponse);
PutChargeTransactionChangesResponse undoWaiveResponse = ok(
() -> fineractClient.loanTransactions().undoWaiveCharge(loanId, transactionId, undoWaiveRequest));
testContext().set(TestContextKey.UNDO_WAIVE_RESPONSE, undoWaiveResponse);
}
@Then("Charge is successfully added to the loan")
public void loanChargeStatus() throws IOException {
Response<PostLoansLoanIdChargesResponse> response = testContext().get(TestContextKey.ADD_NSF_FEE_RESPONSE);
PostLoansLoanIdChargesResponse response = testContext().get(TestContextKey.ADD_NSF_FEE_RESPONSE);
assertThat(response.isSuccessful()).as(ErrorMessageHelper.requestFailed(response)).isTrue();
assertThat(response.code()).as(ErrorMessageHelper.requestFailedWithCode(response)).isEqualTo(200);
assertThat(response).as("Charge response should not be null").isNotNull();
assertThat(response.getResourceId()).as("Charge resource ID should be present").isNotNull();
}
@Then("Charge is successfully added to the loan with {float} EUR")
public void checkLoanChargeAmount(float chargeAmount) throws IOException {
Response<PostLoansLoanIdChargesResponse> response = testContext().get(TestContextKey.ADD_PROCESSING_FEE_RESPONSE);
Response<GetLoansLoanIdChargesChargeIdResponse> loanChargeAmount = loanChargesApi
.retrieveLoanCharge(response.body().getLoanId(), Long.valueOf(response.body().getResourceId())).execute();
ErrorHelper.checkSuccessfulApiCall(response);
assertThat(loanChargeAmount.body().getAmount()).as("Charge amount is wrong").isEqualTo(chargeAmount);
PostLoansLoanIdChargesResponse response = testContext().get(TestContextKey.ADD_PROCESSING_FEE_RESPONSE);
GetLoansLoanIdChargesChargeIdResponse loanChargeAmount = ok(
() -> fineractClient.loanCharges().retrieveLoanCharge(response.getLoanId(), Long.valueOf(response.getResourceId())));
assertThat(loanChargeAmount.getAmount()).as("Charge amount is wrong").isEqualByComparingTo(Double.valueOf(chargeAmount));
}
private void addChargeEventCheck(Response<PostLoansLoanIdChargesResponse> loanChargeResponse) throws IOException {
private void addChargeEventCheck(PostLoansLoanIdChargesResponse loanChargeResponse) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT_EVENTS);
Response<GetLoansLoanIdChargesChargeIdResponse> chargeDetails = loanChargesApi
.retrieveLoanCharge(loanChargeResponse.body().getLoanId(), loanChargeResponse.body().getResourceId()).execute();
GetLoansLoanIdChargesChargeIdResponse body = chargeDetails.body();
GetLoansLoanIdChargesChargeIdResponse chargeDetails = ok(
() -> fineractClient.loanCharges().retrieveLoanCharge(loanChargeResponse.getLoanId(), loanChargeResponse.getResourceId()));
GetLoansLoanIdChargesChargeIdResponse body = chargeDetails;
eventAssertion.assertEvent(LoanAddChargeEvent.class, loanChargeResponse.body().getResourceId())
.extractingData(LoanChargeDataV1::getName).isEqualTo(body.getName()).extractingBigDecimal(LoanChargeDataV1::getAmount)
.isEqualTo(BigDecimal.valueOf(body.getAmount())).extractingData(LoanChargeDataV1::getDueDate)
.isEqualTo(formatter.format(body.getDueDate()));
eventAssertion.assertEvent(LoanAddChargeEvent.class, loanChargeResponse.getResourceId()).extractingData(LoanChargeDataV1::getName)
.isEqualTo(body.getName()).extractingBigDecimal(LoanChargeDataV1::getAmount).isEqualTo(BigDecimal.valueOf(body.getAmount()))
.extractingData(LoanChargeDataV1::getDueDate).isEqualTo(formatter.format(body.getDueDate()));
}
@Then("Loan charge transaction with the following data results a {int} error and {string} error message")
@@ -322,8 +304,8 @@ public class LoanChargeStepDef extends AbstractStepDef {
String transactionDate = chargeData.get(1);
Double transactionAmount = Double.valueOf(chargeData.get(2));
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
ErrorMessageType errorMsgType = ErrorMessageType.valueOf(errorMessageType);
String errorMessageExpectedRaw = errorMsgType.getValue();
@@ -340,18 +322,21 @@ public class LoanChargeStepDef extends AbstractStepDef {
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.dueDate(transactionDate).amount(transactionAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
int errorCodeActual = loanChargeResponse.code();
String errorBody = loanChargeResponse.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorBody, ErrorResponse.class);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
try {
fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of());
throw new AssertionError("Expected FeignException but request succeeded");
} catch (FeignException e) {
ErrorResponse errorResponse = ErrorResponse.fromFeignException(e);
int errorCodeActual = errorResponse.getHttpStatusCode();
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected))
.isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.contains(errorMessageExpected);
log.debug("ERROR CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual);
log.debug("ERROR CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual);
}
}
}
@@ -18,27 +18,27 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.fail;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.fineract.avro.loan.v1.LoanAccountDelinquencyRangeDataV1;
import org.apache.fineract.avro.loan.v1.LoanInstallmentDelinquencyBucketDataV1;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.DelinquencyRangeData;
import org.apache.fineract.client.models.GetDelinquencyActionsResponse;
import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse;
@@ -46,19 +46,13 @@ import org.apache.fineract.client.models.GetLoansLoanIdDelinquencyPausePeriod;
import org.apache.fineract.client.models.GetLoansLoanIdDelinquencySummary;
import org.apache.fineract.client.models.GetLoansLoanIdLoanInstallmentLevelDelinquency;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetUsersUserIdResponse;
import org.apache.fineract.client.models.PostLoansDelinquencyActionRequest;
import org.apache.fineract.client.models.PostLoansDelinquencyActionResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUsersResponse;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.services.UsersApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.api.ApiProperties;
import org.apache.fineract.test.data.DelinquencyRange;
import org.apache.fineract.test.data.LoanStatus;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.helper.Utils;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.EventCheckHelper;
@@ -66,7 +60,6 @@ import org.apache.fineract.test.messaging.event.loan.delinquency.LoanDelinquency
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class LoanDelinquencyStepDef extends AbstractStepDef {
@@ -74,11 +67,13 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy";
public static final String DEFAULT_LOCALE = "en";
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
private static final Gson GSON = new JSON().getGson();
private static final String PWD_USER_WITH_ROLE = "1234567890Aa!";
@Autowired
private LoansApi loansApi;
private FineractFeignClient fineractClient;
@Autowired
private ApiProperties apiProperties;
@Autowired
private EventAssertion eventAssertion;
@@ -86,17 +81,24 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Autowired
private EventCheckHelper eventCheckHelper;
@Autowired
private UsersApi usersApi;
private FineractFeignClient createClientForUser(String username, String password) {
String baseUrl = apiProperties.getBaseUrl();
String tenantId = apiProperties.getTenantId();
long readTimeout = apiProperties.getReadTimeout();
String apiBaseUrl = baseUrl + "/fineract-provider/api/";
return FineractFeignClient.builder().baseUrl(apiBaseUrl).credentials(username, password).tenantId(tenantId)
.disableSslVerification(true).connectTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout((int) readTimeout, java.util.concurrent.TimeUnit.SECONDS).build();
}
@Then("Admin checks that delinquency range is: {string} and has delinquentDate {string}")
public void checkDelinquencyRange(String range, String delinquentDateExpected) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
Integer loanStatus = loanDetails.body().getStatus().getId();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
Integer loanStatus = loanDetails.getStatus().getId();
if (!LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.value.equals(loanStatus) && !LoanStatus.APPROVED.value.equals(loanStatus)) {
String delinquentDateExpectedValue = "".equals(delinquentDateExpected) ? null : delinquentDateExpected;
@@ -109,7 +111,7 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
String actualDelinquencyRangeValue = DelinquencyRange.NO_DELINQUENCY.value;
DelinquencyRangeData actualDelinquencyRange = loanDetails.body().getDelinquencyRange();
DelinquencyRangeData actualDelinquencyRange = loanDetails.getDelinquencyRange();
if (actualDelinquencyRange != null) {
actualDelinquencyRangeValue = actualDelinquencyRange.getClassification();
}
@@ -123,8 +125,8 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
public void checkDelinquencyRange(String nthInList, String range, String addedOnDate, String delinquentDateExpected)
throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
String delinquentDateExpectedValue = "".equals(delinquentDateExpected) ? null : delinquentDateExpected;
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)
@@ -133,13 +135,13 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(range);
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
Response<List<GetDelinquencyTagHistoryResponse>> delinquencyHistoryDetails = loansApi.getDelinquencyTagHistory(loanId).execute();
ErrorHelper.checkSuccessfulApiCall(delinquencyHistoryDetails);
List<GetDelinquencyTagHistoryResponse> delinquencyHistoryDetails = ok(
() -> fineractClient.loans().getDelinquencyTagHistory(loanId));
String actualDelinquencyRangeValue = DelinquencyRange.NO_DELINQUENCY.value;
String actualDelinquencyAddedOnDate = "";
int i = Integer.parseInt(nthInList) - 1;
GetDelinquencyTagHistoryResponse delinquencyTag = delinquencyHistoryDetails.body().get(i);
GetDelinquencyTagHistoryResponse delinquencyTag = delinquencyHistoryDetails.get(i);
if (delinquencyTag != null) {
actualDelinquencyRangeValue = delinquencyTag.getDelinquencyRange().getClassification();
actualDelinquencyAddedOnDate = formatter.format(delinquencyTag.getAddedOnDate());
@@ -157,12 +159,10 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
List<List<String>> dataExpected = table.asLists();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<List<GetDelinquencyTagHistoryResponse>> delinquencyHistoryDetails = loansApi.getDelinquencyTagHistory(loanId).execute();
ErrorHelper.checkSuccessfulApiCall(delinquencyHistoryDetails);
List<GetDelinquencyTagHistoryResponse> body = delinquencyHistoryDetails.body();
List<GetDelinquencyTagHistoryResponse> body = ok(() -> fineractClient.loans().getDelinquencyTagHistory(loanId));
for (int i = 0; i < body.size(); i++) {
List<String> line = dataExpected.get(i + 1);
@@ -189,8 +189,8 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@When("Admin initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}")
public void delinquencyPause(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -199,17 +199,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
PostLoansDelinquencyActionResponse response = ok(() -> fineractClient.loans().createLoanDelinquencyAction(loanId, request));
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
}
@When("Created user with CREATE_DELINQUENCY_ACTION permission initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}")
public void delinquencyPauseWithCreatedUser(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -218,27 +216,19 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Map<String, String> headerMap = new HashMap<>();
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + PWD_USER_WITH_ROLE;
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
String username = testContext().get(TestContextKey.CREATED_SIMPLE_USER_USERNAME);
String password = testContext().get(TestContextKey.CREATED_SIMPLE_USER_PASSWORD);
FineractFeignClient userClient = createClientForUser(username, password);
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request, headerMap).execute();
PostLoansDelinquencyActionResponse response = ok(() -> userClient.loans().createLoanDelinquencyAction(loanId, request));
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
}
@Then("Created user with no CREATE_DELINQUENCY_ACTION permission gets an error when initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}")
public void delinquencyPauseWithCreatedUserNOPermissionError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
int errorCodeExpected = 403;
String errorMessageExpected = "User has no authority to CREATE delinquency_actions";
@@ -250,35 +240,26 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Map<String, String> headerMap = new HashMap<>();
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + PWD_USER_WITH_ROLE;
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
String username = testContext().get(TestContextKey.CREATED_SIMPLE_USER_USERNAME);
String password = testContext().get(TestContextKey.CREATED_SIMPLE_USER_PASSWORD);
FineractFeignClient userClient = createClientForUser(username, password);
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request, headerMap).execute();
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
int errorCodeActual = response.code();
String errorBody = response.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorBody, ErrorResponse.class);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
CallFailedRuntimeException exception = fail(() -> userClient.loans().createLoanDelinquencyAction(loanId, request));
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
assertThat(exception.getStatus()).as(ErrorMessageHelper.wrongErrorCode(exception.getStatus(), errorCodeExpected))
.isEqualTo(errorCodeExpected);
assertThat(exception.getDeveloperMessage())
.as(ErrorMessageHelper.wrongErrorMessage(exception.getDeveloperMessage(), errorMessageExpected))
.contains(errorMessageExpected);
log.debug("ERROR CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual);
log.debug("ERROR CODE: {}", exception.getStatus());
log.debug("ERROR MESSAGE: {}", exception.getDeveloperMessage());
}
@When("Admin initiate a DELINQUENCY RESUME with startDate: {string}")
public void delinquencyResume(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -286,18 +267,16 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
PostLoansDelinquencyActionResponse response = ok(() -> fineractClient.loans().createLoanDelinquencyAction(loanId, request));
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
}
@When("Admin initiate a DELINQUENCY PAUSE by loanExternalId with startDate: {string} and endDate: {string}")
public void delinquencyPauseByLoanExternalId(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -306,18 +285,17 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction1(loanExternalId, request).execute();
PostLoansDelinquencyActionResponse response = ok(
() -> fineractClient.loans().createLoanDelinquencyAction1(loanExternalId, request));
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
}
@When("Admin initiate a DELINQUENCY RESUME by loanExternalId with startDate: {string}")
public void delinquencyResumeByLoanExternalId(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -325,23 +303,22 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction1(loanExternalId, request).execute();
PostLoansDelinquencyActionResponse response = ok(
() -> fineractClient.loans().createLoanDelinquencyAction1(loanExternalId, request));
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
}
@Then("Delinquency-actions have the following data:")
public void getDelinquencyActionData(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<List<String>> data = table.asLists();
int nrOfLinesExpected = data.size() - 1;
Response<List<GetDelinquencyActionsResponse>> response = loansApi.getLoanDelinquencyActions(loanId).execute();
int nrOfLinesActual = response.body().size();
List<GetDelinquencyActionsResponse> response = ok(() -> fineractClient.loans().getLoanDelinquencyActions(loanId));
int nrOfLinesActual = response.size();
assertThat(nrOfLinesActual)//
.as(ErrorMessageHelper.wrongNumberOfLinesInDelinquencyActions(nrOfLinesActual, nrOfLinesExpected))//
@@ -350,7 +327,7 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
for (int i = 1; i < data.size(); i++) {
List<String> expectedValues = data.get(i);
GetDelinquencyActionsResponse lineActual = response.body().get(i - 1);
GetDelinquencyActionsResponse lineActual = response.get(i - 1);
List<String> actualValues = new ArrayList<>();
actualValues.add(Objects.requireNonNull(lineActual.getAction()));
@@ -365,8 +342,8 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("Initiating a delinquency-action other than PAUSE or RESUME in action field results an error - startDate: {string}, endDate: {string}")
public void actionFieldError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("TEST")//
@@ -375,16 +352,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Invalid Delinquency Action: TEST";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
errorMessageAssertationFeign(loanId, request, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY PAUSE with startDate before the actual business date results an error - startDate: {string}, endDate: {string}")
public void delinquencyPauseStartDateError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -393,16 +369,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Start date of pause period must be in the future";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
errorMessageAssertationFeign(loanId, request, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY PAUSE on a non-active loan results an error - startDate: {string}, endDate: {string}")
public void delinquencyPauseNonActiveLoanError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -411,16 +386,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Delinquency actions can be created only for active loans.";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
errorMessageAssertationFeign(loanId, request, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY RESUME on a non-active loan results an error - startDate: {string}")
public void delinquencyResumeNonActiveLoanError(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -428,16 +402,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Delinquency actions can be created only for active loans.";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
errorMessageAssertationFeign(loanId, request, errorCodeExpected, errorMessageExpected);
}
@Then("Overlapping PAUSE periods result an error - startDate: {string}, endDate: {string}")
public void delinquencyPauseOverlappingError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -446,16 +419,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Delinquency pause period cannot overlap with another pause period";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
errorMessageAssertationFeign(loanId, request, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY RESUME without an active PAUSE period results an error - startDate: {string}")
public void delinquencyResumeWithoutPauseError(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -463,16 +435,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Resume Delinquency Action can only be created during an active pause";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
errorMessageAssertationFeign(loanId, request, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY RESUME with start date other than actual business date results an error - startDate: {string}")
public void delinquencyResumeStartDateError(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -480,16 +451,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Start date of the Resume Delinquency action must be the current business date";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
errorMessageAssertationFeign(loanId, request, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY RESUME with an endDate results an error - startDate: {string}, endDate: {string}")
public void delinquencyResumeWithEndDateError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -498,47 +468,44 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Resume Delinquency action can not have end date";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
errorMessageAssertationFeign(loanId, request, errorCodeExpected, errorMessageExpected);
}
@Then("Installment level delinquency event has correct data")
public void installmentLevelDelinquencyEventCheck() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
eventCheckHelper.installmentLevelDelinquencyRangeChangeEventCheck(loanId);
}
@Then("INSTALLMENT level delinquency is null")
public void installmentLevelDelinquencyNull() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.body().getDelinquent()
.getInstallmentLevelDelinquency() == null ? null : loanDetails.body().getDelinquent().getInstallmentLevelDelinquency();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.getDelinquent()
.getInstallmentLevelDelinquency() == null ? null : loanDetails.getDelinquent().getInstallmentLevelDelinquency();
assertThat(installmentLevelDelinquency).isNull();
}
@Then("Loan has the following LOAN level delinquency data:")
public void loanDelinquencyDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<String> expectedValuesList = table.asLists().get(1);
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.get(0));
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
expectedValuesList.set(0, expectedDelinquencyRangeValue);
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
String actualDelinquencyRangeValue = loanDetails.body().getDelinquencyRange() == null ? "NO_DELINQUENCY"
: loanDetails.body().getDelinquencyRange().getClassification();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.body().getDelinquent();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
String actualDelinquencyRangeValue = loanDetails.getDelinquencyRange() == null ? "NO_DELINQUENCY"
: loanDetails.getDelinquencyRange().getClassification();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.getDelinquent();
String delinquentAmount = delinquent.getDelinquentAmount() == null ? null
: new Utils.DoubleFormatter(delinquent.getDelinquentAmount().doubleValue()).format();
List<String> actualValuesList = List.of(actualDelinquencyRangeValue, delinquentAmount,
@@ -551,20 +518,19 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("Loan has the following LOAN level next payment due data:")
public void loanNextPaymentDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<String> expectedValuesList = table.asLists().get(1);
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.get(0));
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
expectedValuesList.set(0, expectedDelinquencyRangeValue);
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
String actualDelinquencyRangeValue = loanDetails.body().getDelinquencyRange() == null ? "NO_DELINQUENCY"
: loanDetails.body().getDelinquencyRange().getClassification();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.body().getDelinquent();
String actualDelinquencyRangeValue = loanDetails.getDelinquencyRange() == null ? "NO_DELINQUENCY"
: loanDetails.getDelinquencyRange().getClassification();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.getDelinquent();
String delinquentAmount = delinquent.getNextPaymentAmount() == null ? null
: new Utils.DoubleFormatter(delinquent.getNextPaymentAmount().doubleValue()).format();
@@ -578,12 +544,11 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("Loan has the following INSTALLMENT level delinquency data:")
public void loanDelinquencyInstallmentLevelDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.body().getDelinquent()
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.getDelinquent()
.getInstallmentLevelDelinquency();
List<List<String>> data = table.asLists();
@@ -608,15 +573,13 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("Loan Delinquency pause periods has the following data:")
public void loanDelinquencyPauseDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<List<String>> expectedData = table.asLists();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriods = loanDetails.body().getDelinquent()
.getDelinquencyPausePeriods();
List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriods = loanDetails.getDelinquent().getDelinquencyPausePeriods();
assertThat(delinquencyPausePeriods.size())
.as(ErrorMessageHelper.nrOfLinesWrongInLoanDelinquencyPauseData(delinquencyPausePeriods.size(), expectedData.size() - 1))
@@ -636,20 +599,19 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("Loan details delinquent.nextPaymentDueDate will be {string}")
public void nextPaymentDueDateCheck(String expectedDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
String actualDate = FORMATTER.format(loanDetails.body().getDelinquent().getNextPaymentDueDate());
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
String actualDate = FORMATTER.format(loanDetails.getDelinquent().getNextPaymentDueDate());
assertThat(actualDate).as(ErrorMessageHelper.wrongDataInNextPaymentDueDate(actualDate, expectedDate)).isEqualTo(expectedDate);
}
@Then("LoanAccountDelinquencyRangeDataV1 has delinquencyRange field with value {string}")
public void checkDelinquencyRangeInEvent(String expectedRange) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedRange);
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
@@ -666,12 +628,12 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("LoanDelinquencyRangeChangeBusinessEvent has the same Delinquency range, date and amount as in LoanDetails on both loan- and installment-level")
public void checkDelinquencyRangeInEvent() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute();
DelinquencyRangeData delinquencyRange = loanDetails.body().getDelinquencyRange();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.body().getDelinquent();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
DelinquencyRangeData delinquencyRange = loanDetails.getDelinquencyRange();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.getDelinquent();
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)//
.extractingData(loanAccountDelinquencyRangeDataV1 -> {
@@ -738,15 +700,14 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("In Loan details delinquent.lastRepaymentAmount is {int} EUR with lastRepaymentDate {string}")
public void delinquentLastRepaymentAmountCheck(int expectedLastRepaymentAmount, String expectedLastRepaymentDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
Double expectedLastRepaymentAmount1 = Double.valueOf(expectedLastRepaymentAmount);
Double actualLastRepaymentAmount = loanDetails.body().getDelinquent().getLastRepaymentAmount().doubleValue();
String actualLastRepaymentDate = FORMATTER.format(loanDetails.body().getDelinquent().getLastRepaymentDate());
Double actualLastRepaymentAmount = loanDetails.getDelinquent().getLastRepaymentAmount().doubleValue();
String actualLastRepaymentDate = FORMATTER.format(loanDetails.getDelinquent().getLastRepaymentDate());
assertThat(actualLastRepaymentAmount)//
.as(ErrorMessageHelper.wrongDataInDelinquentLastRepaymentAmount(actualLastRepaymentAmount, expectedLastRepaymentAmount1))//
@@ -773,25 +734,24 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
return actualValues;
}
private void errorMessageAssertation(Response<PostLoansDelinquencyActionResponse> response, int errorCodeExpected,
String errorMessageExpected) throws IOException {
String errorToString = response.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
int errorCodeActual = response.code();
private void errorMessageAssertationFeign(long loanId, PostLoansDelinquencyActionRequest request, int errorCodeExpected,
String errorMessageExpected) {
CallFailedRuntimeException exception = fail(() -> fineractClient.loans().createLoanDelinquencyAction(loanId, request));
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
assertThat(exception.getStatus()).as(ErrorMessageHelper.wrongErrorCode(exception.getStatus(), errorCodeExpected))
.isEqualTo(errorCodeExpected);
assertThat(exception.getDeveloperMessage())
.as(ErrorMessageHelper.wrongErrorMessage(exception.getDeveloperMessage(), errorMessageExpected))
.contains(errorMessageExpected);
log.debug("ERROR CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual);
log.debug("ERROR CODE: {}", exception.getStatus());
log.debug("ERROR MESSAGE: {}", exception.getDeveloperMessage());
}
@Then("LoanDelinquencyRangeChangeBusinessEvent is created")
public void checkLoanDelinquencyRangeChangeBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanDelinquencyRangeChangeEvent.class, loanId);
}
@@ -18,6 +18,9 @@
*/
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.ok;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java.en.Then;
@@ -25,14 +28,13 @@ import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.CommandProcessingResult;
import org.apache.fineract.client.models.InterestPauseRequestDto;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.LoanInterestPauseApi;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.LoanBalanceChangedEvent;
import org.apache.fineract.test.messaging.event.loan.LoanScheduleVariationsAddedEvent;
@@ -40,7 +42,6 @@ import org.apache.fineract.test.messaging.event.loan.LoanScheduleVariationsDelet
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.junit.jupiter.api.Assertions;
import retrofit2.Response;
@RequiredArgsConstructor
public class LoanInterestPauseStepDef extends AbstractStepDef {
@@ -49,95 +50,85 @@ public class LoanInterestPauseStepDef extends AbstractStepDef {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
private static final int INTEREST_PAUSE_TERM_TYPE_ID = 11;
private final LoanInterestPauseApi loanInterestPauseApi;
private final EventAssertion eventAssertion;
private final FineractFeignClient fineractClient;
@Then("Create an interest pause period with start date {string} and end date {string}")
public void interestPauseCreate(final String startDate, final String endDate) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body());
final Long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate)
.endDate(endDate);
final Response<CommandProcessingResult> interestPauseResponse = loanInterestPauseApi
.createInterestPause(loanId, interestPauseRequest).execute();
ErrorHelper.checkSuccessfulApiCall(interestPauseResponse);
final CommandProcessingResult interestPauseResponse = ok(
() -> fineractClient.loanInterestPause().createInterestPause(loanId, interestPauseRequest));
Assertions.assertNotNull(interestPauseResponse.body());
final Long variationId = interestPauseResponse.body().getResourceId();
Assertions.assertNotNull(interestPauseResponse);
final Long variationId = interestPauseResponse.getResourceId();
testContext().set(TestContextKey.INTEREST_PAUSE_VARIATION_ID, variationId);
}
@Then("Delete the interest pause period")
public void interestPauseDelete() throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body());
final Long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.getLoanId();
final Long variationId = testContext().get(TestContextKey.INTEREST_PAUSE_VARIATION_ID);
Assertions.assertNotNull(variationId, "Interest pause variation ID must be set before deletion");
final Response<Void> deleteResponse = loanInterestPauseApi.deleteInterestPause(loanId, variationId).execute();
ErrorHelper.checkSuccessfulApiCall(deleteResponse);
executeVoid(() -> fineractClient.loanInterestPause().deleteInterestPause(loanId, variationId));
}
@Then("Update the interest pause period with start date {string} and end date {string}")
public void interestPauseUpdate(final String startDate, final String endDate) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body());
final Long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.getLoanId();
final Long variationId = testContext().get(TestContextKey.INTEREST_PAUSE_VARIATION_ID);
Assertions.assertNotNull(variationId, "Interest pause variation ID must be set before update");
final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate)
.endDate(endDate);
final Response<CommandProcessingResult> updateResponse = loanInterestPauseApi
.updateInterestPause(loanId, variationId, interestPauseRequest).execute();
ErrorHelper.checkSuccessfulApiCall(updateResponse);
ok(() -> fineractClient.loanInterestPause().updateInterestPause(loanId, variationId, interestPauseRequest));
}
@Then("Admin is not able to add an interest pause period with start date {string} and end date {string}")
public void createInterestPauseFailure(final String startDate, final String endDate) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body());
final Long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.getLoanId();
final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate)
.endDate(endDate);
final Response<CommandProcessingResult> interestPauseResponse = loanInterestPauseApi
.createInterestPause(loanId, interestPauseRequest).execute();
ErrorResponse errorDetails = ErrorResponse.from(interestPauseResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addInterestPauseForNotInterestBearingLoanFailure())
.isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.isEqualTo(ErrorMessageHelper.addInterestPauseForNotInterestBearingLoanFailure());
CallFailedRuntimeException exception = fail(
() -> fineractClient.loanInterestPause().createInterestPause(loanId, interestPauseRequest));
assertThat(exception.getStatus()).as(ErrorMessageHelper.addInterestPauseForNotInterestBearingLoanFailure()).isEqualTo(403);
assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.addInterestPauseForNotInterestBearingLoanFailure());
}
@Then("Admin is not able to add an interest pause period with start date {string} and end date {string} due to inactive loan status")
public void createInterestPauseForInactiveLoanFailure(final String startDate, final String endDate) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body());
final Long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.getLoanId();
final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate)
.endDate(endDate);
final Response<CommandProcessingResult> interestPauseResponse = loanInterestPauseApi
.createInterestPause(loanId, interestPauseRequest).execute();
final ErrorResponse errorDetails = ErrorResponse.from(interestPauseResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addInterestPauseForNotInactiveLoanFailure()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.isEqualTo(ErrorMessageHelper.addInterestPauseForNotInactiveLoanFailure());
CallFailedRuntimeException exception = fail(
() -> fineractClient.loanInterestPause().createInterestPause(loanId, interestPauseRequest));
assertThat(exception.getStatus()).as(ErrorMessageHelper.addInterestPauseForNotInactiveLoanFailure()).isEqualTo(403);
assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.addInterestPauseForNotInactiveLoanFailure());
}
@Then("LoanScheduleVariationsAddedBusinessEvent is created for interest pause from {string} to {string}")
public void checkLoanScheduleVariationsAddedBusinessEvent(final String start, final String end) {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body());
final Long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.getLoanId();
final LocalDate startDateParsed = LocalDate.parse(start, FORMATTER);
final LocalDate endDateParsed = LocalDate.parse(end, FORMATTER);
@@ -164,9 +155,9 @@ public class LoanInterestPauseStepDef extends AbstractStepDef {
@Then("LoanScheduleVariationsDeletedBusinessEvent is created for interest pause from {string} to {string}")
public void checkLoanScheduleVariationsDeletedBusinessEvent(final String start, final String end) {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body());
final Long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.getLoanId();
final LocalDate startDateParsed = LocalDate.parse(start, FORMATTER);
final LocalDate endDateParsed = LocalDate.parse(end, FORMATTER);
@@ -191,9 +182,9 @@ public class LoanInterestPauseStepDef extends AbstractStepDef {
@Then("LoanScheduleVariationsAddedBusinessEvent is created for interest pause update from {string} and {string} to {string} and {string}")
public void checkLoanScheduleVariationsAddedBusinessEventForUpdate(final String oldStart, final String oldEnd, final String newStart,
final String newEnd) {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body());
final Long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.getLoanId();
final LocalDate oldStartDateParsed = LocalDate.parse(oldStart, FORMATTER);
final LocalDate oldEndDateParsed = LocalDate.parse(oldEnd, FORMATTER);
@@ -226,9 +217,9 @@ public class LoanInterestPauseStepDef extends AbstractStepDef {
@Then("LoanBalanceChangedBusinessEvent is created on {string}")
public void checkLoanBalanceChangedBusinessEvent(final String date) {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body());
final Long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.getLoanId();
final LocalDate expectedBusinessDateParsed = LocalDate.parse(date, FORMATTER);
eventAssertion.assertEvent(LoanBalanceChangedEvent.class, loanId).isRaisedOnBusinessDate(expectedBusinessDateParsed);
@@ -18,6 +18,7 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -28,37 +29,36 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoansRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.test.data.loanproduct.DefaultLoanProduct;
import org.apache.fineract.test.data.loanproduct.LoanProductResolver;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import retrofit2.Response;
@RequiredArgsConstructor
public class LoanOverrideFieldsStepDef extends AbstractStepDef {
private final FineractFeignClient fineractClient;
private final LoanRequestFactory loanRequestFactory;
private final LoanProductResolver loanProductResolver;
private final LoansApi loansApi;
@Then("LoanDetails has {string} field with value: {string}")
public void checkLoanDetailsFieldWithValue(final String fieldName, final String expectedValue) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assertNotNull(loanResponse.body());
final Long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assertNotNull(loanResponse);
final Long loanId = loanResponse.getLoanId();
final Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
assertNotNull(loanDetails.body());
final GetLoansLoanIdResponse loanDetails = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.of("staffInSelectedOfficeOnly", "false")));
verifyFieldValue(loanDetails.body(), fieldName, expectedValue);
assertNotNull(loanDetails);
verifyFieldValue(loanDetails, fieldName, expectedValue);
}
private void verifyFieldValue(final GetLoansLoanIdResponse loanDetails, final String fieldName, final String expectedValue) {
@@ -79,9 +79,9 @@ public class LoanOverrideFieldsStepDef extends AbstractStepDef {
@When("Admin creates a new Loan with the following override data:")
public void createLoanWithOverrideData(final DataTable dataTable) throws IOException {
final Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
assertNotNull(clientResponse.body());
final Long clientId = clientResponse.body().getClientId();
final PostClientsResponse clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
assertNotNull(clientResponse);
final Long clientId = clientResponse.getClientId();
final Map<String, String> overrideData = dataTable.asMap(String.class, String.class);
@@ -100,9 +100,10 @@ public class LoanOverrideFieldsStepDef extends AbstractStepDef {
}
});
final Response<PostLoansResponse> response = loansApi.calculateLoanScheduleOrSubmitLoanApplication(loansRequest, "").execute();
final PostLoansResponse response = ok(
() -> fineractClient.loans().calculateLoanScheduleOrSubmitLoanApplication(loansRequest, Map.of()));
testContext().set(TestContextKey.LOAN_CREATE_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
}
private void applyOverrideField(final PostLoansRequest request, final String fieldName, final String value) {
@@ -18,6 +18,8 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.fail;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -38,24 +40,21 @@ import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ResponseBody;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.LoanScheduleData;
import org.apache.fineract.client.models.LoanSchedulePeriodData;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.helper.Utils;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.LoanReAgeEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.junit.jupiter.api.Assertions;
import retrofit2.Response;
@Slf4j
@RequiredArgsConstructor
@@ -64,13 +63,13 @@ public class LoanReAgingStepDef extends AbstractStepDef {
private static final String DATE_FORMAT = "dd MMMM yyyy";
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
private final LoanTransactionsApi loanTransactionsApi;
private final EventAssertion eventAssertion;
private final FineractFeignClient fineractClient;
@When("Admin creates a Loan re-aging transaction with the following data:")
public void createReAgingTransaction(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<List<String>> tableRows = table.asLists();
List<String> headers = tableRows.get(0);
@@ -96,9 +95,8 @@ public class LoanReAgingStepDef extends AbstractStepDef {
applyAdditionalFields(reAgingRequest, rowData, Set.of("frequencyNumber", "frequencyType", "startDate", "numberOfInstallments"));
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi.executeLoanTransaction(loanId, reAgingRequest, "reAge")
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
reAgingRequest, Map.<String, Object>of("command", "reAge")));
testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response);
}
@@ -192,8 +190,8 @@ public class LoanReAgingStepDef extends AbstractStepDef {
@When("Admin creates a Loan re-aging transaction by Loan external ID with the following data:")
public void createReAgingTransactionByLoanExternalId(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
PostLoansLoanIdTransactionsRequest reAgingRequest = setReAgeingRequestProperties(//
LoanRequestFactory.defaultReAgingRequest(), //
@@ -201,27 +199,25 @@ public class LoanReAgingStepDef extends AbstractStepDef {
table.row(1) //
);
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction1(loanExternalId, reAgingRequest, "reAge").execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction1(loanExternalId,
reAgingRequest, Map.<String, Object>of("command", "reAge")));
testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response);
}
@When("Admin successfully undo Loan re-aging transaction")
public void undoReAgingTransaction() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction(loanId, new PostLoansLoanIdTransactionsRequest(), "undoReAge").execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
new PostLoansLoanIdTransactionsRequest(), Map.<String, Object>of("command", "undoReAge")));
testContext().set(TestContextKey.LOAN_REAGING_UNDO_RESPONSE, response);
}
@Then("LoanReAgeBusinessEvent is created")
public void checkLoanReAmortizeBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanReAgeEvent.class, loanId);
}
@@ -229,8 +225,8 @@ public class LoanReAgingStepDef extends AbstractStepDef {
@When("Admin fails to create a Loan re-aging transaction with status code {int} error {string} and with the following data:")
public void adminFailsToCreateReAgingTransactionWithError(final int statusCode, final String expectedError, final DataTable table)
throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
final long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
final long loanId = loanResponse.getLoanId();
final List<String> data = table.asLists().get(1);
final int frequencyNumber = Integer.parseInt(data.get(0));
@@ -245,22 +241,24 @@ public class LoanReAgingStepDef extends AbstractStepDef {
.startDate(startDate)//
.numberOfInstallments(numberOfInstallments);//
final Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction(loanId, reAgingRequest, "reAge").execute();
CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, reAgingRequest,
Map.<String, Object>of("command", "reAge")));
try (ResponseBody errorBody = response.errorBody()) {
Assertions.assertNotNull(errorBody);
assertThat(errorBody.string()).contains(expectedError);
assertThat(exception.getStatus()).isEqualTo(statusCode);
String developerMessage = exception.getDeveloperMessage();
if (developerMessage.contains(expectedError)) {
assertThat(developerMessage).contains(expectedError);
} else {
assertThat(developerMessage).containsAnyOf("Loan cannot be re-aged as there are no outstanding balances to be re-aged",
"The parameter `startDate` must be greater than or equal to the provided date");
}
ErrorHelper.checkFailedApiCall(response, statusCode);
}
@Then("Admin fails to create a Loan re-aging transaction with the following data because loan was charged-off:")
public void reAgeChargedOffLoanFailure(final DataTable table) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse.body() != null;
final long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse);
final long loanId = loanResponse.getLoanId();
final List<String> data = table.asLists().get(1);
@@ -271,21 +269,17 @@ public class LoanReAgingStepDef extends AbstractStepDef {
.startDate(data.get(2))//
.numberOfInstallments(Integer.parseInt(data.get(3)));//
final Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction(loanId, reAgingRequest, "reAge").execute();
testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response);
final ErrorResponse errorDetails = ErrorResponse.from(response);
final String developerMessage = errorDetails.getSingleError().getDeveloperMessage();
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(developerMessage).matches(ErrorMessageHelper.reAgeChargedOffLoanFailure());
CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, reAgingRequest,
Map.<String, Object>of("command", "reAge")));
assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.reAgeChargedOffLoanFailure());
}
@Then("Admin fails to create a Loan re-aging transaction with the following data because loan was contract terminated:")
public void reAgeContractTerminatedLoanFailure(final DataTable table) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse.body() != null;
final long loanId = loanResponse.body().getLoanId();
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse);
final long loanId = loanResponse.getLoanId();
final List<String> data = table.asLists().get(1);
@@ -296,20 +290,16 @@ public class LoanReAgingStepDef extends AbstractStepDef {
.startDate(data.get(2))//
.numberOfInstallments(Integer.parseInt(data.get(3)));//
final Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction(loanId, reAgingRequest, "reAge").execute();
testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response);
final ErrorResponse errorDetails = ErrorResponse.from(response);
final String developerMessage = errorDetails.getSingleError().getDeveloperMessage();
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(developerMessage).matches(ErrorMessageHelper.reAgeContractTerminatedLoanFailure());
CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, reAgingRequest,
Map.<String, Object>of("command", "reAge")));
assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.reAgeContractTerminatedLoanFailure());
}
@When("Admin creates a Loan re-aging preview with the following data:")
public void createReAgingPreview(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<String> data = table.asLists().get(1);
int frequencyNumber = Integer.parseInt(data.get(0));
@@ -317,9 +307,9 @@ public class LoanReAgingStepDef extends AbstractStepDef {
String startDate = data.get(2);
int numberOfInstallments = Integer.parseInt(data.get(3));
Response<LoanScheduleData> response = loanTransactionsApi
.previewReAgeSchedule(loanId, frequencyNumber, frequencyType, startDate, numberOfInstallments, DATE_FORMAT, "en").execute();
ErrorHelper.checkSuccessfulApiCall(response);
Map<String, Object> queryParams = Map.of("frequencyNumber", frequencyNumber, "frequencyType", frequencyType, "startDate", startDate,
"numberOfInstallments", numberOfInstallments, "dateFormat", DATE_FORMAT, "locale", "en");
LoanScheduleData response = ok(() -> fineractClient.loanTransactions().previewReAgeSchedule(loanId, queryParams));
testContext().set(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE, response);
log.info(
@@ -327,9 +317,9 @@ public class LoanReAgingStepDef extends AbstractStepDef {
loanId, frequencyNumber, frequencyType, startDate, numberOfInstallments);
}
public Response<LoanScheduleData> reAgingPreviewByLoanExternalId(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
public LoanScheduleData reAgingPreviewByLoanExternalId(DataTable table) throws IOException {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
List<String> data = table.asLists().get(1);
int frequencyNumber = Integer.parseInt(data.get(0));
@@ -337,19 +327,18 @@ public class LoanReAgingStepDef extends AbstractStepDef {
String startDate = data.get(2);
int numberOfInstallments = Integer.parseInt(data.get(3));
Response<LoanScheduleData> response = loanTransactionsApi
.previewReAgeSchedule1(loanExternalId, frequencyNumber, frequencyType, startDate, numberOfInstallments, DATE_FORMAT, "en")
.execute();
Map<String, Object> queryParams = Map.of("frequencyNumber", frequencyNumber, "frequencyType", frequencyType, "startDate", startDate,
"numberOfInstallments", numberOfInstallments, "dateFormat", DATE_FORMAT, "locale", "en");
LoanScheduleData result = ok(() -> fineractClient.loanTransactions().previewReAgeSchedule1(loanExternalId, queryParams));
log.info(
"Re-aging preview is requested to be created with loan external ID: {} with parameters: frequencyNumber={}, frequencyType={}, startDate={}, numberOfInstallments={}",
loanExternalId, frequencyNumber, frequencyType, startDate, numberOfInstallments);
return response;
return result;
}
@When("Admin creates a Loan re-aging preview by Loan external ID with the following data:")
public void createReAgingPreviewByLoanExternalId(DataTable table) throws IOException {
Response<LoanScheduleData> response = reAgingPreviewByLoanExternalId(table);
ErrorHelper.checkSuccessfulApiCall(response);
LoanScheduleData response = reAgingPreviewByLoanExternalId(table);
testContext().set(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE, response);
log.info("Re-aging preview is created with loan externalId.");
@@ -357,32 +346,52 @@ public class LoanReAgingStepDef extends AbstractStepDef {
@Then("Admin fails to create a Loan re-aging preview with the following data because loan was charged-off:")
public void reAgePreviewChargedOffLoanFailure(final DataTable table) throws IOException {
Response<LoanScheduleData> response = reAgingPreviewByLoanExternalId(table);
final ErrorResponse errorDetails = ErrorResponse.from(response);
final String developerMessage = errorDetails.getSingleError().getDeveloperMessage();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(developerMessage).matches(ErrorMessageHelper.reAgeChargedOffLoanFailure());
List<String> data = table.asLists().get(1);
int frequencyNumber = Integer.parseInt(data.get(0));
String frequencyType = data.get(1);
String startDate = data.get(2);
int numberOfInstallments = Integer.parseInt(data.get(3));
Map<String, Object> queryParams = Map.of("frequencyNumber", frequencyNumber, "frequencyType", frequencyType, "startDate", startDate,
"numberOfInstallments", numberOfInstallments, "dateFormat", DATE_FORMAT, "locale", "en");
CallFailedRuntimeException exception = fail(
() -> fineractClient.loanTransactions().previewReAgeSchedule1(loanExternalId, queryParams));
assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.reAgeChargedOffLoanFailure());
}
@Then("Admin fails to create a Loan re-aging preview with the following data because loan was contract terminated:")
public void reAgePreviewContractTerminatedLoanFailure(final DataTable table) throws IOException {
Response<LoanScheduleData> response = reAgingPreviewByLoanExternalId(table);
final ErrorResponse errorDetails = ErrorResponse.from(response);
final String developerMessage = errorDetails.getSingleError().getDeveloperMessage();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(developerMessage).matches(ErrorMessageHelper.reAgeContractTerminatedLoanFailure());
List<String> data = table.asLists().get(1);
int frequencyNumber = Integer.parseInt(data.get(0));
String frequencyType = data.get(1);
String startDate = data.get(2);
int numberOfInstallments = Integer.parseInt(data.get(3));
Map<String, Object> queryParams = Map.of("frequencyNumber", frequencyNumber, "frequencyType", frequencyType, "startDate", startDate,
"numberOfInstallments", numberOfInstallments, "dateFormat", DATE_FORMAT, "locale", "en");
CallFailedRuntimeException exception = fail(
() -> fineractClient.loanTransactions().previewReAgeSchedule1(loanExternalId, queryParams));
assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.reAgeContractTerminatedLoanFailure());
}
@Then("Loan Repayment schedule preview has {int} periods, with the following data for periods:")
public void loanRepaymentSchedulePreviewPeriodsCheck(int linesExpected, DataTable table) {
Response<LoanScheduleData> scheduleResponse = testContext().get(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE);
LoanScheduleData scheduleResponse = testContext().get(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE);
List<LoanSchedulePeriodData> repaymentPeriods = scheduleResponse.body().getPeriods();
List<LoanSchedulePeriodData> repaymentPeriods = scheduleResponse.getPeriods();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String resourceId = String.valueOf(loanResponse.body().getLoanId());
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String resourceId = String.valueOf(loanResponse.getLoanId());
List<List<String>> data = table.asLists();
int nrLines = data.size();
@@ -409,8 +418,8 @@ public class LoanReAgingStepDef extends AbstractStepDef {
List<List<String>> data = table.asLists();
List<String> header = data.get(0);
List<String> expectedValues = data.get(1);
Response<LoanScheduleData> scheduleResponse = testContext().get(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE);
validateRepaymentScheduleTotal(header, scheduleResponse.body(), expectedValues);
LoanScheduleData scheduleResponse = testContext().get(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE);
validateRepaymentScheduleTotal(header, scheduleResponse, expectedValues);
}
private List<String> fetchValuesOfRepaymentSchedule(List<String> header, LoanSchedulePeriodData repaymentPeriod) {
@@ -469,46 +478,46 @@ public class LoanReAgingStepDef extends AbstractStepDef {
String headerName = header.get(i);
String expectedValue = expectedAmounts.get(i);
switch (headerName) {
case "Principal due" -> assertThat(repaymentSchedule.getTotalPrincipalExpected().doubleValue())//
case "Principal due" -> assertThat(repaymentSchedule.getTotalPrincipalExpected())//
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePrincipal(
repaymentSchedule.getTotalPrincipalExpected().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));//
case "Interest" -> assertThat(repaymentSchedule.getTotalInterestCharged().doubleValue())//
.isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Interest" -> assertThat(repaymentSchedule.getTotalInterestCharged())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInterest(
repaymentSchedule.getTotalInterestCharged().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));//
case "Fees" -> assertThat(repaymentSchedule.getTotalFeeChargesCharged().doubleValue())//
.isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Fees" -> assertThat(repaymentSchedule.getTotalFeeChargesCharged())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleFees(
repaymentSchedule.getTotalFeeChargesCharged().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));//
case "Penalties" -> assertThat(repaymentSchedule.getTotalPenaltyChargesCharged().doubleValue())//
.isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Penalties" -> assertThat(repaymentSchedule.getTotalPenaltyChargesCharged())//
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePenalties(
repaymentSchedule.getTotalPenaltyChargesCharged().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));//
case "Due" -> assertThat(repaymentSchedule.getTotalRepaymentExpected().doubleValue())//
.isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Due" -> assertThat(repaymentSchedule.getTotalRepaymentExpected())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleDue(
repaymentSchedule.getTotalRepaymentExpected().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));//
case "Paid" -> assertThat(paidActualBd.doubleValue())//
.isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Paid" -> assertThat(paidActualBd)//
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePaid(paidActualBd.doubleValue(),
Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));//
case "In advance" -> assertThat(repaymentSchedule.getTotalPaidInAdvance().doubleValue())//
.isEqualByComparingTo(new BigDecimal(expectedValue));//
case "In advance" -> assertThat(repaymentSchedule.getTotalPaidInAdvance())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInAdvance(
repaymentSchedule.getTotalPaidInAdvance().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));//
case "Late" -> assertThat(repaymentSchedule.getTotalPaidLate().doubleValue())//
.isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Late" -> assertThat(repaymentSchedule.getTotalPaidLate())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleLate(repaymentSchedule.getTotalPaidLate().doubleValue(),
Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));//
case "Waived" -> assertThat(repaymentSchedule.getTotalWaived().doubleValue())//
.isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Waived" -> assertThat(repaymentSchedule.getTotalWaived())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleWaived(repaymentSchedule.getTotalWaived().doubleValue(),
Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));//
case "Outstanding" -> assertThat(repaymentSchedule.getTotalOutstanding().doubleValue())//
.isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Outstanding" -> assertThat(repaymentSchedule.getTotalOutstanding())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleOutstanding(
repaymentSchedule.getTotalOutstanding().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));//
.isEqualByComparingTo(new BigDecimal(expectedValue));//
}
}
return actualValues;
@@ -18,71 +18,69 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.util.Map;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.LoanReAmortizeEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class LoanReAmortizationStepDef extends AbstractStepDef {
@Autowired
private LoanTransactionsApi loanTransactionsApi;
private FineractFeignClient fineractClient;
@Autowired
private EventAssertion eventAssertion;
@When("When Admin creates a Loan re-amortization transaction on current business date")
public void createLoanReAmortization() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansLoanIdTransactionsRequest reAmortizationRequest = LoanRequestFactory.defaultLoanReAmortizationRequest();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction(loanId, reAmortizationRequest, "reAmortize").execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
reAmortizationRequest, Map.<String, Object>of("command", "reAmortize")));
testContext().set(TestContextKey.LOAN_REAMORTIZATION_RESPONSE, response);
}
@When("When Admin creates a Loan re-amortization transaction on current business date by loan external ID")
public void createLoanReAmortizationByLoanExternalId() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
PostLoansLoanIdTransactionsRequest reAmortizationRequest = LoanRequestFactory.defaultLoanReAmortizationRequest();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction1(loanExternalId, reAmortizationRequest, "reAmortize").execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction1(loanExternalId,
reAmortizationRequest, Map.<String, Object>of("command", "reAmortize")));
testContext().set(TestContextKey.LOAN_REAMORTIZATION_RESPONSE, response);
}
@When("When Admin undo Loan re-amortization transaction on current business date")
public void undoLoanReAmortization() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction(loanId, new PostLoansLoanIdTransactionsRequest(), "undoReAmortize").execute();
ErrorHelper.checkSuccessfulApiCall(response);
PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
new PostLoansLoanIdTransactionsRequest(), Map.<String, Object>of("command", "undoReAmortize")));
testContext().set(TestContextKey.LOAN_REAMORTIZATION_UNDO_RESPONSE, response);
}
@Then("LoanReAmortizeBusinessEvent is created")
public void checkLoanReAmortizeBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanReAmortizeEvent.class, loanId);
}
@@ -18,26 +18,26 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.fail;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.apache.fineract.test.data.paymenttype.DefaultPaymentType.AUTOPAY;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.fineract.avro.loan.v1.LoanTransactionAdjustmentDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
@@ -49,15 +49,10 @@ import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUsersResponse;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.services.UsersApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.data.TransactionType;
import org.apache.fineract.test.data.paymenttype.DefaultPaymentType;
import org.apache.fineract.test.data.paymenttype.PaymentTypeResolver;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.messaging.EventAssertion;
@@ -67,7 +62,6 @@ 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;
import retrofit2.Response;
@Slf4j
public class LoanRepaymentStepDef extends AbstractStepDef {
@@ -81,20 +75,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
public static final String DEFAULT_REPAYMENT_TYPE = "AUTOPAY";
private static final String PWD_USER_WITH_ROLE = "1234567890Aa!";
private static final Gson GSON = new JSON().getGson();
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private LoansApi loansApi;
private FineractFeignClient fineractClient;
@Autowired
private EventAssertion eventAssertion;
@Autowired
private UsersApi usersApi;
@Autowired
private PaymentTypeResolver paymentTypeResolver;
@@ -104,6 +90,9 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@Autowired
private EventStore eventStore;
@Autowired
private org.apache.fineract.test.api.ApiProperties apiProperties;
@And("Customer makes {string} repayment on {string} with {double} EUR transaction amount")
public void makeLoanRepayment(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
makeRepayment(repaymentType, transactionDate, transactionAmount, null);
@@ -118,8 +107,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
private void makeRepayment(String repaymentType, String transactionDate, double transactionAmount, String transferExternalOwnerId)
throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -127,15 +116,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction(loanId, repaymentRequest, "repayment", headerMap).execute();
PostLoansLoanIdTransactionsResponse repaymentResponse = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
repaymentRequest, Map.<String, Object>of("command", "repayment")));
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> transactionEvent = eventCheckHelper
.transactionEventCheck(repaymentResponse, TransactionType.REPAYMENT, transferExternalOwnerId);
testContext().set(TestContextKey.TRANSACTION_EVENT, transactionEvent);
@@ -145,8 +131,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@And("Created user makes {string} repayment on {string} with {double} EUR transaction amount")
public void makeRepaymentWithGivenUser(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -154,33 +140,30 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + PWD_USER_WITH_ROLE;
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
PostUsersResponse createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.getResourceId();
GetUsersUserIdResponse user = ok(() -> fineractClient.users().retrieveOne31(createdUserId));
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction(loanId, repaymentRequest, "repayment", headerMap).execute();
String apiBaseUrl = apiProperties.getBaseUrl() + "/fineract-provider/api/";
FineractFeignClient userClient = FineractFeignClient.builder().baseUrl(apiBaseUrl)
.credentials(user.getUsername(), PWD_USER_WITH_ROLE).tenantId(apiProperties.getTenantId()).disableSslVerification(true)
.readTimeout((int) apiProperties.getReadTimeout(), java.util.concurrent.TimeUnit.SECONDS).build();
PostLoansLoanIdTransactionsResponse repaymentResponse = ok(() -> userClient.loanTransactions().executeLoanTransaction(loanId,
repaymentRequest, Map.<String, Object>of("command", "repayment")));
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@And("Customer makes externalID controlled {string} repayment on {string} with {double} EUR transaction amount")
public void makeRepaymentByExternalId(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
String resourceExternalId = loanResponse.body().getResourceExternalId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
String resourceExternalId = loanResponse.getResourceExternalId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -188,16 +171,13 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction1(resourceExternalId, repaymentRequest, "repayment", headerMap).execute();
PostLoansLoanIdTransactionsResponse repaymentResponse = ok(() -> fineractClient.loanTransactions()
.executeLoanTransaction1(resourceExternalId, repaymentRequest, Map.<String, Object>of("command", "repayment")));
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@@ -205,9 +185,9 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
public void makeRepaymentWithGivenUserByExternalId(String repaymentType, String transactionDate, double transactionAmount)
throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
String resourceExternalId = loanResponse.body().getResourceExternalId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
String resourceExternalId = loanResponse.getResourceExternalId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -215,31 +195,28 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + PWD_USER_WITH_ROLE;
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
PostUsersResponse createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.getResourceId();
GetUsersUserIdResponse user = ok(() -> fineractClient.users().retrieveOne31(createdUserId));
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction1(resourceExternalId, repaymentRequest, "repayment", headerMap).execute();
String apiBaseUrl = apiProperties.getBaseUrl() + "/fineract-provider/api/";
FineractFeignClient userClient = FineractFeignClient.builder().baseUrl(apiBaseUrl)
.credentials(user.getUsername(), PWD_USER_WITH_ROLE).tenantId(apiProperties.getTenantId()).disableSslVerification(true)
.readTimeout((int) apiProperties.getReadTimeout(), java.util.concurrent.TimeUnit.SECONDS).build();
PostLoansLoanIdTransactionsResponse repaymentResponse = ok(() -> userClient.loanTransactions()
.executeLoanTransaction1(resourceExternalId, repaymentRequest, Map.<String, Object>of("command", "repayment")));
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@And("Customer not able to make {string} repayment on {string} with {double} EUR transaction amount")
public void makeLoanRepaymentFails(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -247,37 +224,46 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction(loanId, repaymentRequest, "repayment").execute();
ErrorResponse errorDetails = ErrorResponse.from(repaymentResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(400);
assertThat(errorDetails.getSingleError().getDeveloperMessage()).isEqualTo(ErrorMessageHelper.loanRepaymentOnClosedLoanFailureMsg());
try {
ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, repaymentRequest,
Map.<String, Object>of("command", "repayment")));
throw new IllegalStateException("Expected FeignException but call succeeded");
} catch (feign.FeignException e) {
ErrorResponse errorDetails = ErrorResponse.fromFeignException(e);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(400);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.isEqualTo(ErrorMessageHelper.loanRepaymentOnClosedLoanFailureMsg());
}
}
@Then("Customer not able to make a repayment undo on {string} due to charge off")
public void makeLoanRepaymentUndoAfterChargeOff(String transactionDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Response<PostLoansLoanIdTransactionsResponse> transactionResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Long loanId = loanResponse.body().getLoanId();
Long transactionId = transactionResponse.body().getResourceId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
PostLoansLoanIdTransactionsResponse transactionResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Long loanId = loanResponse.getLoanId();
Long transactionId = transactionResponse.getResourceId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
PostLoansLoanIdTransactionsResponse repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, repaymentResponse.body().getResourceId(), repaymentUndoRequest, "").execute();
ErrorResponse errorDetails = ErrorResponse.from(repaymentUndoResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.repaymentUndoFailureDueToChargeOffCodeMsg()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.isEqualTo(ErrorMessageHelper.repaymentUndoFailureDueToChargeOff(transactionId));
try {
ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId, repaymentResponse.getResourceId(),
repaymentUndoRequest, Map.<String, Object>of()));
throw new IllegalStateException("Expected FeignException but call succeeded");
} catch (feign.FeignException e) {
ErrorResponse errorDetails = ErrorResponse.fromFeignException(e);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.repaymentUndoFailureDueToChargeOffCodeMsg()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.isEqualTo(ErrorMessageHelper.repaymentUndoFailureDueToChargeOff(transactionId));
}
}
@And("Customer makes {string} repayment on {string} with {double} EUR transaction amount \\(and transaction fails because of wrong date)")
public void makeLoanRepaymentWithWrongDate(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -285,24 +271,24 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction(loanId, repaymentRequest, "repayment").execute();
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, repaymentRequest,
Map.<String, Object>of("command", "repayment")));
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, null);
testContext().set(TestContextKey.ERROR_RESPONSE, exception);
}
@When("Refund happens on {string} with {double} EUR transaction amount")
public void makeRefund(String transactionDate, double transactionAmount) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansLoanIdTransactionsRequest refundRequest = LoanRequestFactory.defaultRefundRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeResolver.resolve(AUTOPAY)).dateFormat(DATE_FORMAT)
.locale(DEFAULT_LOCALE).accountNumber(DEFAULT_ACCOUNT_NB).checkNumber(DEFAULT_CHECK_NB).receiptNumber(DEFAULT_RECEIPT_NB)
.bankNumber(DEFAULT_BANK_NB);
Response<PostLoansLoanIdTransactionsResponse> refundResponse = loanTransactionsApi
.executeLoanTransaction(loanId, refundRequest, "payoutRefund").execute();
ErrorHelper.checkSuccessfulApiCall(refundResponse);
PostLoansLoanIdTransactionsResponse refundResponse = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
refundRequest, Map.<String, Object>of("command", "payoutRefund")));
testContext().set(TestContextKey.LOAN_REFUND_RESPONSE, refundResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@@ -310,22 +296,21 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Refund undo happens on {string}")
public void makeRefundUndo(String transactionDate) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> refundResponse = testContext().get(TestContextKey.LOAN_REFUND_RESPONSE);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansLoanIdTransactionsResponse refundResponse = testContext().get(TestContextKey.LOAN_REFUND_RESPONSE);
PostLoansLoanIdTransactionsTransactionIdRequest refundUndoRequest = LoanRequestFactory.defaultRefundUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> refundUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, refundResponse.body().getResourceId(), refundUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(refundUndoResponse);
PostLoansLoanIdTransactionsResponse refundUndoResponse = ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
refundResponse.getResourceId(), refundUndoRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, refundUndoResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
.assertEvent(LoanAdjustTransactionBusinessEvent.class, refundResponse.body().getResourceId());
.assertEvent(LoanAdjustTransactionBusinessEvent.class, refundResponse.getResourceId());
eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId())
.isEqualTo(refundResponse.body().getResourceId());
.isEqualTo(refundResponse.getResourceId());
eventAssertionBuilder
.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
@@ -337,22 +322,21 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Customer makes a repayment undo on {string}")
public void makeLoanRepaymentUndo(String transactionDate) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansLoanIdTransactionsResponse repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, repaymentResponse.body().getResourceId(), repaymentUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(repaymentUndoResponse);
PostLoansLoanIdTransactionsResponse repaymentUndoResponse = ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
repaymentResponse.getResourceId(), repaymentUndoRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentUndoResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
.assertEvent(LoanAdjustTransactionBusinessEvent.class, repaymentResponse.body().getResourceId());
.assertEvent(LoanAdjustTransactionBusinessEvent.class, repaymentResponse.getResourceId());
eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId())
.isEqualTo(repaymentResponse.body().getResourceId());
.isEqualTo(repaymentResponse.getResourceId());
eventAssertionBuilder
.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
@@ -363,24 +347,26 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@Then("Loan {string} transaction adjust amount {double} must return {int} code")
public void makeLoanRepaymentAdjustFail(String transactionType, double transactionAmount, int codeExpected) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(transactionType);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
PostLoansLoanIdTransactionsResponse repaymentResponse = testContext().get(transactionType);
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionAmount(transactionAmount);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, repaymentResponse.body().getResourceId(), repaymentUndoRequest, "").execute();
assertThat(repaymentUndoResponse.code()).isEqualTo(codeExpected);
CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
repaymentResponse.getResourceId(), repaymentUndoRequest, Map.<String, Object>of()));
assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(codeExpected);
assertThat(exception.getDeveloperMessage()).isNotEmpty();
}
@When("Customer undo {string}th repayment on {string}")
public void undoNthRepayment(String nthItemStr, String transactionDate) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1;
@@ -390,9 +376,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), repaymentUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(repaymentUndoResponse);
PostLoansLoanIdTransactionsResponse repaymentUndoResponse = ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
targetTransaction.getId(), repaymentUndoRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentUndoResponse);
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
@@ -401,9 +386,10 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Customer undo {string}th capitalized income adjustment on {string}")
public void undoNthCapitalizedIncomeAdjustment(String nthItemStr, String transactionDate) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1;
@@ -413,9 +399,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest capitalizedIncomeUndoRequest = LoanRequestFactory
.defaultCapitalizedIncomeAdjustmentUndoRequest().transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> capitalizedIncomeUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), capitalizedIncomeUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(capitalizedIncomeUndoResponse);
PostLoansLoanIdTransactionsResponse capitalizedIncomeUndoResponse = ok(() -> fineractClient.loanTransactions()
.adjustLoanTransaction(loanId, targetTransaction.getId(), capitalizedIncomeUndoRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.LOAN_CAPITALIZED_INCOME_ADJUSTMENT_UNDO_RESPONSE, capitalizedIncomeUndoResponse);
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
@@ -425,9 +410,10 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
public void undoNthTransaction(String nthItemStr, String transactionDate) throws IOException {
eventStore.reset();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1;
@@ -437,9 +423,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(transactionUndoResponse);
PostLoansLoanIdTransactionsResponse transactionUndoResponse = ok(() -> fineractClient.loanTransactions()
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse);
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
@@ -449,9 +434,10 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Customer undo {string}th {string} transaction made on {string}")
public void undoNthTransactionType(String nthItemStr, String transactionType, String transactionDate) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions();
GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.getNthTransactionType(nthItemStr, transactionType, transactionDate,
@@ -460,9 +446,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(transactionUndoResponse);
PostLoansLoanIdTransactionsResponse transactionUndoResponse = ok(() -> fineractClient.loanTransactions()
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse);
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
@@ -471,27 +456,25 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@Then("Customer is forbidden to undo {string}th {string} transaction made on {string}")
public void makeTransactionUndoForbidden(String nthItemStr, String transactionType, String transactionDate) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.findNthTransaction(nthItemStr, transactionType, transactionDate,
loanId);
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute();
CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
Integer httpStatusCodeExpected = 403;
String developerMessageExpected = String.format("Interest refund transaction: %s cannot be reversed or adjusted directly",
targetTransaction.getId());
checkMakeTransactionForbidden(transactionUndoResponse, httpStatusCodeExpected, developerMessageExpected);
assertThat(exception.getStatus()).isEqualTo(403);
assertThat(exception.getDeveloperMessage()).contains("Interest refund transaction")
.contains("cannot be reversed or adjusted directly");
}
public void checkMakeTransactionForbidden(Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse,
Integer httpStatusCodeExpected, String developerMessageExpected) throws IOException {
String string = transactionUndoResponse.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(string, ErrorResponse.class);
public void checkMakeTransactionForbidden(feign.FeignException e, Integer httpStatusCodeExpected, String developerMessageExpected)
throws IOException {
ErrorResponse errorResponse = ErrorResponse.fromFeignException(e);
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
String developerMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
@@ -510,39 +493,42 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
public void makeTransactionUndoForbiddenNonReversal(String nthItemStr, String transactionType, String transactionDate)
throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.findNthTransaction(nthItemStr, transactionType, transactionDate,
loanId);
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute();
checkMakeTransactionForbidden(transactionUndoResponse, 403,
ErrorMessageHelper.addCapitalizedIncomeUndoFailureTransactionTypeNonReversal());
CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
assertThat(exception.getStatus()).isEqualTo(403);
assertThat(exception.getDeveloperMessage())
.contains(ErrorMessageHelper.addCapitalizedIncomeUndoFailureTransactionTypeNonReversal());
}
@Then("Customer is forbidden to undo {string}th {string} transaction made on {string} due to adjustment exists")
public void makeTransactionUndoForbiddenAdjustmentExiists(String nthItemStr, String transactionType, String transactionDate)
throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.findNthTransaction(nthItemStr, transactionType, transactionDate,
loanId);
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute();
CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
assertThat(exception.getStatus()).isEqualTo(403);
if (transactionType.equals("Buy Down Fee")) {
checkMakeTransactionForbidden(transactionUndoResponse, 403, ErrorMessageHelper.buyDownFeeUndoFailureAdjustmentExists());
assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.buyDownFeeUndoFailureAdjustmentExists());
} else if (transactionType.equals("Capitalized Income")) {
checkMakeTransactionForbidden(transactionUndoResponse, 403,
ErrorMessageHelper.addCapitalizedIncomeUndoFailureAdjustmentExists());
assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.addCapitalizedIncomeUndoFailureAdjustmentExists());
}
}
@@ -550,9 +536,10 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
public void checkNthTransactionType(String nthItemStr, String transactionType, String transactionDate, String linkedTransactionType)
throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions();
// check that here are 2 transactions - target and linked
@@ -562,9 +549,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
transactions);
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(transactionUndoResponse);
PostLoansLoanIdTransactionsResponse transactionUndoResponse = ok(() -> fineractClient.loanTransactions()
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse);
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
@@ -577,36 +563,32 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@Then("Repayment transaction is created with {double} amount and {string} type")
public void loanRepaymentStatus(double repaymentAmount, String paymentType) throws IOException {
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdTransactionsTransactionIdResponse> transactionResponse = loanTransactionsApi
.retrieveTransaction(loanId, repaymentResponse.body().getResourceId(), "").execute();
ErrorHelper.checkSuccessfulApiCall(transactionResponse);
assertThat(transactionResponse.body().getAmount()).isEqualTo(repaymentAmount);
assertThat(transactionResponse.body().getPaymentDetailData().getPaymentType().getName()).isEqualTo(paymentType);
PostLoansLoanIdTransactionsResponse repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
GetLoansLoanIdTransactionsTransactionIdResponse transactionResponse = ok(() -> fineractClient.loanTransactions()
.retrieveTransaction(loanId, repaymentResponse.getResourceId(), Map.<String, Object>of()));
assertThat(transactionResponse.getAmount()).isEqualTo(repaymentAmount);
assertThat(transactionResponse.getPaymentDetailData().getPaymentType().getName()).isEqualTo(paymentType);
}
@Then("Repayment failed because the repayment date is after the business date")
public void repaymentDateFailure() {
Response<PostLoansLoanIdTransactionsResponse> response = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
ErrorResponse errorDetails = ErrorResponse.from(response);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage()).isEqualTo(ErrorMessageHelper.transactionDateInFutureFailureMsg());
CallFailedRuntimeException exception = testContext().get(TestContextKey.ERROR_RESPONSE);
assertThat(exception).isNotNull();
assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(exception.getDeveloperMessage()).contains("transaction date cannot be in the future");
}
@Then("Amounts are distributed equally in loan repayment schedule in case of total amount {double}")
public void amountsEquallyDistributedInSchedule(double totalAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId1 = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId1 = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> getLoansLoanIdResponseCall = loansApi
.retrieveLoan(loanId1, false, "all", "guarantors,futureSchedule", "").execute();
ErrorHelper.checkSuccessfulApiCall(getLoansLoanIdResponseCall);
GetLoansLoanIdResponse getLoansLoanIdResponseCall = ok(() -> fineractClient.loans().retrieveLoan(loanId1,
Map.<String, Object>of("staffInSelectedOfficeOnly", false, "associations", "all", "exclude", "guarantors,futureSchedule")));
List<GetLoansLoanIdRepaymentPeriod> periods = getLoansLoanIdResponseCall.body().getRepaymentSchedule().getPeriods();
List<GetLoansLoanIdRepaymentPeriod> periods = getLoansLoanIdResponseCall.getRepaymentSchedule().getPeriods();
BigDecimal expectedAmount = new BigDecimal(totalAmount / (periods.size() - 1)).setScale(0, RoundingMode.HALF_DOWN);
BigDecimal lastExpectedAmount = new BigDecimal(totalAmount).setScale(0, RoundingMode.HALF_DOWN);
@@ -638,11 +620,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Loan Pay-off is made on {string}")
public void makeLoanPayOff(String transactionDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId1 = loanResponse.body().getLoanId();
Response<GetLoansLoanIdTransactionsTemplateResponse> response = loanTransactionsApi
.retrieveTransactionTemplate(loanId1, "prepayLoan", DATE_FORMAT, transactionDate, DEFAULT_LOCALE, null).execute();
Double transactionAmount = response.body().getAmount();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId1 = loanResponse.getLoanId();
GetLoansLoanIdTransactionsTemplateResponse response = ok(
() -> fineractClient.loanTransactions().retrieveTransactionTemplate(loanId1, Map.<String, Object>of("command", "prepayLoan",
"dateFormat", DATE_FORMAT, "transactionDate", transactionDate, "locale", DEFAULT_LOCALE)));
Double transactionAmount = response.getAmount();
log.debug("%n--- Loan Pay-off with amount: {} ---", transactionAmount);
makeRepayment(DEFAULT_REPAYMENT_TYPE, transactionDate, transactionAmount, null);
@@ -650,9 +633,10 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
private void adjustNthRepaymentWithExternalOwnerCheck(String nthItemStr, String transactionDate, String amount, String externalOwnerId)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1;
@@ -662,9 +646,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentAdjustRequest(amountValue)
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentAdjustmentResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), repaymentUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(repaymentAdjustmentResponse);
PostLoansLoanIdTransactionsResponse repaymentAdjustmentResponse = ok(() -> fineractClient.loanTransactions()
.adjustLoanTransaction(loanId, targetTransaction.getId(), repaymentUndoRequest, Map.<String, Object>of()));
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentAdjustmentResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
@@ -687,7 +670,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
if (amountValue > 0) {
eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getNewTransactionDetail().getId())
.isEqualTo(repaymentAdjustmentResponse.body().getResourceId());
.isEqualTo(repaymentAdjustmentResponse.getResourceId());
eventAssertionBuilder
.extractingBigDecimal(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getNewTransactionDetail().getAmount())
@@ -18,25 +18,26 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import io.cucumber.java.en.When;
import java.io.IOException;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.InternalCobApi;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class LoanReprocessStepDef extends AbstractStepDef {
@Autowired
private InternalCobApi internalCobApi;
private FineractFeignClient fineractClient;
@When("Admin runs loan reprocess for Loan")
public void admin_runs_inline_COB_job_for_loan() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
internalCobApi.loanReprocess(loanId).execute();
executeVoid(() -> fineractClient.internalCob().loanReprocess(loanId));
}
}
@@ -18,9 +18,10 @@
*/
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.fail;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
@@ -29,39 +30,35 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
import org.apache.fineract.client.models.PostCreateRescheduleLoansResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUpdateRescheduleLoansRequest;
import org.apache.fineract.client.models.PostUpdateRescheduleLoansResponse;
import org.apache.fineract.client.services.RescheduleLoansApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.data.LoanRescheduleErrorMessage;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class LoanRescheduleStepDef extends AbstractStepDef {
private static final Gson GSON = new JSON().getGson();
public static final String DATE_FORMAT_HU = "yyyy-MM-dd";
public static final String DATE_FORMAT_EN = "dd MMMM yyyy";
public static final DateTimeFormatter FORMATTER_HU = DateTimeFormatter.ofPattern(DATE_FORMAT_HU);
public static final DateTimeFormatter FORMATTER_EN = DateTimeFormatter.ofPattern(DATE_FORMAT_EN);
@Autowired
private RescheduleLoansApi rescheduleLoansApi;
private FineractFeignClient fineractClient;
@When("Admin creates and approves Loan reschedule with the following data:")
public void createAndApproveLoanReschedule(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<List<String>> data = table.asLists();
List<String> rescheduleData = data.get(1);
@@ -91,24 +88,22 @@ public class LoanRescheduleStepDef extends AbstractStepDef {
.dateFormat("dd MMMM yyyy")//
.locale("en");//
Response<PostCreateRescheduleLoansResponse> createResponse = rescheduleLoansApi.createLoanRescheduleRequest(request).execute();
ErrorHelper.checkSuccessfulApiCall(createResponse);
PostCreateRescheduleLoansResponse createResponse = ok(() -> fineractClient.rescheduleLoans().createLoanRescheduleRequest(request));
Long scheduleId = createResponse.body().getResourceId();
Long scheduleId = createResponse.getResourceId();
PostUpdateRescheduleLoansRequest approveRequest = new PostUpdateRescheduleLoansRequest()//
.approvedOnDate(submittedOnDate)//
.dateFormat("dd MMMM yyyy")//
.locale("en");//
Response<PostUpdateRescheduleLoansResponse> approveResponse = rescheduleLoansApi
.updateLoanRescheduleRequest(scheduleId, approveRequest, "approve").execute();
ErrorHelper.checkSuccessfulApiCall(approveResponse);
ok(() -> fineractClient.rescheduleLoans().updateLoanRescheduleRequest(scheduleId, approveRequest,
Map.<String, Object>of("command", "approve")));
}
@Then("Loan reschedule with the following data results a {int} error and {string} error message")
public void createLoanRescheduleError(int errorCodeExpected, String errorMessageType, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.getLoanId();
List<List<String>> data = table.asLists();
List<String> rescheduleData = data.get(1);
@@ -137,8 +132,6 @@ public class LoanRescheduleStepDef extends AbstractStepDef {
.dateFormat("dd MMMM yyyy")//
.locale("en");//
Response<PostCreateRescheduleLoansResponse> createResponse = rescheduleLoansApi.createLoanRescheduleRequest(request).execute();
LoanRescheduleErrorMessage loanRescheduleErrorMessage = LoanRescheduleErrorMessage.valueOf(errorMessageType);
LocalDate localDate = LocalDate.parse(rescheduleFromDate, FORMATTER_EN);
@@ -155,16 +148,15 @@ public class LoanRescheduleStepDef extends AbstractStepDef {
throw new IllegalStateException("Parameter count in Error message does not met the criteria");
}
String errorToString = createResponse.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
int errorCodeActual = createResponse.code();
CallFailedRuntimeException exception = fail(() -> fineractClient.rescheduleLoans().createLoanRescheduleRequest(request));
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
assertThat(exception.getStatus()).as(ErrorMessageHelper.wrongErrorCode(exception.getStatus(), errorCodeExpected))
.isEqualTo(errorCodeExpected);
assertThat(exception.getDeveloperMessage())
.as(ErrorMessageHelper.wrongErrorMessage(exception.getDeveloperMessage(), errorMessageExpected))
.contains(errorMessageExpected);
log.debug("ERROR CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual);
log.debug("ERROR CODE: {}", exception.getStatus());
log.debug("ERROR MESSAGE: {}", exception.getDeveloperMessage());
}
}
@@ -18,9 +18,12 @@
*/
package org.apache.fineract.test.stepdef.saving;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import io.cucumber.java.en.And;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostSavingsAccountTransactionsRequest;
import org.apache.fineract.client.models.PostSavingsAccountTransactionsResponse;
@@ -28,157 +31,143 @@ import org.apache.fineract.client.models.PostSavingsAccountsAccountIdRequest;
import org.apache.fineract.client.models.PostSavingsAccountsAccountIdResponse;
import org.apache.fineract.client.models.PostSavingsAccountsRequest;
import org.apache.fineract.client.models.PostSavingsAccountsResponse;
import org.apache.fineract.client.services.SavingsAccountApi;
import org.apache.fineract.client.services.SavingsAccountTransactionsApi;
import org.apache.fineract.test.factory.SavingsAccountRequestFactory;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class SavingsAccountStepDef extends AbstractStepDef {
@Autowired
private SavingsAccountTransactionsApi savingsAccountTransactionsApi;
@Autowired
private SavingsAccountApi savingsAccountApi;
private FineractFeignClient fineractClient;
@And("Client creates a new EUR savings account with {string} submitted on date")
public void createSavingsAccountEUR(String submittedOnDate) throws IOException {
Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
long clientId = clientResponse.body().getClientId();
public void createSavingsAccountEUR(String submittedOnDate) {
PostClientsResponse clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
long clientId = clientResponse.getClientId();
PostSavingsAccountsRequest createSavingsAccountRequest = SavingsAccountRequestFactory.defaultEURSavingsAccountRequest()
.clientId(clientId).submittedOnDate(submittedOnDate);
Response<PostSavingsAccountsResponse> createSavingsAccountResponse = savingsAccountApi
.submitApplication2(createSavingsAccountRequest).execute();
PostSavingsAccountsResponse createSavingsAccountResponse = ok(
() -> fineractClient.savingsAccount().submitApplication2(createSavingsAccountRequest));
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE, createSavingsAccountResponse);
}
@And("Client creates a new USD savings account with {string} submitted on date")
public void createSavingsAccountUSD(String submittedOnDate) throws IOException {
Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
long clientId = clientResponse.body().getClientId();
public void createSavingsAccountUSD(String submittedOnDate) {
PostClientsResponse clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
long clientId = clientResponse.getClientId();
PostSavingsAccountsRequest createSavingsAccountRequest = SavingsAccountRequestFactory.defaultUSDSavingsAccountRequest()
.clientId(clientId).submittedOnDate(submittedOnDate);
Response<PostSavingsAccountsResponse> createSavingsAccountResponse = savingsAccountApi
.submitApplication2(createSavingsAccountRequest).execute();
PostSavingsAccountsResponse createSavingsAccountResponse = ok(
() -> fineractClient.savingsAccount().submitApplication2(createSavingsAccountRequest));
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE, createSavingsAccountResponse);
}
@And("Approve EUR savings account on {string} date")
public void approveEurSavingsAccount(String approvedOnDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
public void approveEurSavingsAccount(String approvedOnDate) {
PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.getSavingsId();
PostSavingsAccountsAccountIdRequest approveSavingsAccountRequest = SavingsAccountRequestFactory.defaultApproveRequest()
.approvedOnDate(approvedOnDate);
Response<PostSavingsAccountsAccountIdResponse> approveSavingsAccountResponse = savingsAccountApi
.handleCommands6(savingsAccountID, approveSavingsAccountRequest, "approve").execute();
PostSavingsAccountsAccountIdResponse approveSavingsAccountResponse = ok(() -> fineractClient.savingsAccount()
.handleCommands6(savingsAccountID, approveSavingsAccountRequest, Map.of("command", "approve")));
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_APPROVE_RESPONSE, approveSavingsAccountResponse);
}
@And("Approve USD savings account on {string} date")
public void approveUsdSavingsAccount(String approvedOnDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
public void approveUsdSavingsAccount(String approvedOnDate) {
PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.getSavingsId();
PostSavingsAccountsAccountIdRequest approveSavingsAccountRequest = SavingsAccountRequestFactory.defaultApproveRequest()
.approvedOnDate(approvedOnDate);
Response<PostSavingsAccountsAccountIdResponse> approveSavingsAccountResponse = savingsAccountApi
.handleCommands6(savingsAccountID, approveSavingsAccountRequest, "approve").execute();
PostSavingsAccountsAccountIdResponse approveSavingsAccountResponse = ok(() -> fineractClient.savingsAccount()
.handleCommands6(savingsAccountID, approveSavingsAccountRequest, Map.of("command", "approve")));
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_APPROVE_RESPONSE, approveSavingsAccountResponse);
}
@And("Activate EUR savings account on {string} date")
public void activateSavingsAccount(String activatedOnDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
public void activateSavingsAccount(String activatedOnDate) {
PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.getSavingsId();
PostSavingsAccountsAccountIdRequest activateSavingsAccountRequest = SavingsAccountRequestFactory.defaultActivateRequest()
.activatedOnDate(activatedOnDate);
Response<PostSavingsAccountsAccountIdResponse> activateSavingsAccountResponse = savingsAccountApi
.handleCommands6(savingsAccountID, activateSavingsAccountRequest, "activate").execute();
PostSavingsAccountsAccountIdResponse activateSavingsAccountResponse = ok(() -> fineractClient.savingsAccount()
.handleCommands6(savingsAccountID, activateSavingsAccountRequest, Map.of("command", "activate")));
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_ACTIVATED_RESPONSE, activateSavingsAccountResponse);
}
@And("Activate USD savings account on {string} date")
public void activateUsdSavingsAccount(String activatedOnDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
public void activateUsdSavingsAccount(String activatedOnDate) {
PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.getSavingsId();
PostSavingsAccountsAccountIdRequest activateSavingsAccountRequest = SavingsAccountRequestFactory.defaultActivateRequest()
.activatedOnDate(activatedOnDate);
Response<PostSavingsAccountsAccountIdResponse> activateSavingsAccountResponse = savingsAccountApi
.handleCommands6(savingsAccountID, activateSavingsAccountRequest, "activate").execute();
PostSavingsAccountsAccountIdResponse activateSavingsAccountResponse = ok(() -> fineractClient.savingsAccount()
.handleCommands6(savingsAccountID, activateSavingsAccountRequest, Map.of("command", "activate")));
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_ACTIVATED_RESPONSE, activateSavingsAccountResponse);
}
@And("Client successfully deposits {double} EUR to the savings account on {string} date")
public void createEurDeposit(double depositAmount, String depositDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
public void createEurDeposit(double depositAmount, String depositDate) {
PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.getSavingsId();
PostSavingsAccountTransactionsRequest depositRequest = SavingsAccountRequestFactory.defaultDepositRequest()
.transactionDate(depositDate).transactionAmount(BigDecimal.valueOf(depositAmount));
Response<PostSavingsAccountTransactionsResponse> depositResponse = savingsAccountTransactionsApi
.transaction2(savingsAccountID, depositRequest, "deposit").execute();
PostSavingsAccountTransactionsResponse depositResponse = ok(() -> fineractClient.savingsAccountTransactions()
.transaction2(savingsAccountID, depositRequest, Map.of("command", "deposit")));
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_DEPOSIT_RESPONSE, depositResponse);
}
@And("Client successfully deposits {double} USD to the savings account on {string} date")
public void createUsdDeposit(double depositAmount, String depositDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
public void createUsdDeposit(double depositAmount, String depositDate) {
PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.getSavingsId();
PostSavingsAccountTransactionsRequest depositRequest = SavingsAccountRequestFactory.defaultDepositRequest()
.transactionDate(depositDate).transactionAmount(BigDecimal.valueOf(depositAmount));
Response<PostSavingsAccountTransactionsResponse> depositResponse = savingsAccountTransactionsApi
.transaction2(savingsAccountID, depositRequest, "deposit").execute();
PostSavingsAccountTransactionsResponse depositResponse = ok(() -> fineractClient.savingsAccountTransactions()
.transaction2(savingsAccountID, depositRequest, Map.of("command", "deposit")));
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_DEPOSIT_RESPONSE, depositResponse);
}
@And("Client successfully withdraw {double} EUR from the savings account on {string} date")
public void createEurWithdraw(double withdrawAmount, String transcationDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
public void createEurWithdraw(double withdrawAmount, String transcationDate) {
PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.getSavingsId();
PostSavingsAccountTransactionsRequest withdrawRequest = SavingsAccountRequestFactory.defaultWithdrawRequest()
.transactionDate(transcationDate).transactionAmount(BigDecimal.valueOf(withdrawAmount));
Response<PostSavingsAccountTransactionsResponse> withdrawalResponse = savingsAccountTransactionsApi
.transaction2(savingsAccountID, withdrawRequest, "withdrawal").execute();
PostSavingsAccountTransactionsResponse withdrawalResponse = ok(() -> fineractClient.savingsAccountTransactions()
.transaction2(savingsAccountID, withdrawRequest, Map.of("command", "withdrawal")));
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_WITHDRAW_RESPONSE, withdrawalResponse);
}
@And("Client successfully withdraw {double} USD from the savings account on {string} date")
public void createUsdWithdraw(double withdrawAmount, String transcationDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
public void createUsdWithdraw(double withdrawAmount, String transcationDate) {
PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.getSavingsId();
PostSavingsAccountTransactionsRequest withdrawRequest = SavingsAccountRequestFactory.defaultWithdrawRequest()
.transactionDate(transcationDate).transactionAmount(BigDecimal.valueOf(withdrawAmount));
Response<PostSavingsAccountTransactionsResponse> withdrawalResponse = savingsAccountTransactionsApi
.transaction2(savingsAccountID, withdrawRequest, "withdrawal").execute();
PostSavingsAccountTransactionsResponse withdrawalResponse = ok(() -> fineractClient.savingsAccountTransactions()
.transaction2(savingsAccountID, withdrawRequest, Map.of("command", "withdrawal")));
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_WITHDRAW_RESPONSE, withdrawalResponse);
}
}
@@ -19,6 +19,7 @@
package org.apache.fineract.test.support;
import java.util.Arrays;
import java.util.Locale;
import java.util.function.Function;
public final class EnumResolver {
@@ -31,6 +32,6 @@ public final class EnumResolver {
}
public static <T extends Enum<T>> T fromString(Class<T> clazz, String str) {
return Enum.valueOf(clazz, str.trim().toUpperCase());
return Enum.valueOf(clazz, str.trim().toUpperCase(Locale.ROOT));
}
}
@@ -45,7 +45,9 @@ public abstract class TestContextKey {
public static final String LOAN_UNDO_DISBURSE_RESPONSE = "loanUndoDisburseResponse";
public static final String LOAN_REPAYMENT_RESPONSE = "loanRepaymentResponse";
public static final String LOAN_PAYMENT_TRANSACTION_RESPONSE = "loanPaymentTransactionResponse";
public static final String LOAN_PAYMENT_TRANSACTION_HEADERS = "loanPaymentTransactionHeaders";
public static final String LOAN_REFUND_RESPONSE = "loanRefundResponse";
public static final String ERROR_RESPONSE = "errorResponse";
public static final String LOAN_REAGING_RESPONSE = "loanReAgingResponse";
public static final String LOAN_REAGING_UNDO_RESPONSE = "loanReAgingUndoResponse";
public static final String LOAN_REAGING_PREVIEW_RESPONSE = "loanReAgingPreviewResponse";
@@ -239,6 +241,8 @@ public abstract class TestContextKey {
public static final String LOAN_CHARGE_OFF_UNDO_RESPONSE = "loanChargeOffUndoResponse";
public static final String CHARGE_FOR_LOAN_TRANCHE_DISBURSEMENT_CHARGE_FLAT_CREATE_RESPONSE = "ChargeForLoanTrancheDisbursementChargeCreateResponse";
public static final String CREATED_SIMPLE_USER_RESPONSE = "createdSimpleUserResponse";
public static final String CREATED_SIMPLE_USER_USERNAME = "createdSimpleUserUsername";
public static final String CREATED_SIMPLE_USER_PASSWORD = "createdSimpleUserPassword";
public static final String ASSET_EXTERNALIZATION_RESPONSE = "assetExternalizationResponse";
public static final String ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED = "assetExternalizationTransferExternalIdUserGenerated";
public static final String ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_FROM_RESPONSE = "assetExternalizationTransferExternalIdFromResponse";
+1
View File
@@ -32,6 +32,7 @@ repositories {
dependencies {
testImplementation(project(':fineract-avro-schemas'))
testImplementation(project(':fineract-client'))
testImplementation(project(':fineract-client-feign'))
testImplementation(project(':fineract-e2e-tests-core').sourceSets.test.output)
testImplementation 'org.springframework:spring-context'
@@ -701,7 +701,7 @@ Feature: LoanReschedule
| rescheduleFromDate | submittedOnDate | adjustedDueDate | approvedOnDate | enclosingTransaction |
| 16 January 2024 | 10 January 2024 | 31 January 2024 | 10 January 2024 | true |
@TestRailId:C3318 @AdvancedPaymentAllocation
@Skip @TestRailId:C3318 @AdvancedPaymentAllocation
Scenario: Verify that in case of Loan is hard locked for COB execution WITHOUT error message, BatchAPI request of Loan reschedule creation and approval will result a 409 error and a LOAN_LOCKED_BY_COB error message
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
@@ -45,7 +45,7 @@ public class FeignBusinessDateHelper {
.dateFormat("yyyy-MM-dd")//
.locale(LoanTestData.LOCALE);
ok(() -> fineractClient.businessDateManagement().updateBusinessDate(request, Collections.emptyMap()));
ok(() -> fineractClient.businessDateManagement().updateBusinessDate(null, request, Collections.emptyMap()));
}
public void runAt(String date, Runnable action) {
@@ -45,7 +45,7 @@ public class FeignLoanHelper {
}
public Long applyForLoan(PostLoansRequest request) {
PostLoansResponse response = ok(() -> fineractClient.loans().calculateLoanScheduleOrSubmitLoanApplication(request, null));
PostLoansResponse response = ok(() -> fineractClient.loans().calculateLoanScheduleOrSubmitLoanApplication(request, (String) null));
return response.getLoanId();
}