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.EntityFieldConfigurationApi;
import org.apache.fineract.client.feign.services.ExternalAssetOwnerLoanProductAttributesApi; import org.apache.fineract.client.feign.services.ExternalAssetOwnerLoanProductAttributesApi;
import org.apache.fineract.client.feign.services.ExternalAssetOwnersApi; 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.ExternalEventConfigurationApi;
import org.apache.fineract.client.feign.services.ExternalServicesApi; import org.apache.fineract.client.feign.services.ExternalServicesApi;
import org.apache.fineract.client.feign.services.FetchAuthenticatedUserDetailsApi; 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.LoanCollateralApi;
import org.apache.fineract.client.feign.services.LoanCollateralManagementApi; import org.apache.fineract.client.feign.services.LoanCollateralManagementApi;
import org.apache.fineract.client.feign.services.LoanDisbursementDetailsApi; 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.LoanInterestPauseApi;
import org.apache.fineract.client.feign.services.LoanProductsApi; import org.apache.fineract.client.feign.services.LoanProductsApi;
import org.apache.fineract.client.feign.services.LoanReschedulingApi; import org.apache.fineract.client.feign.services.LoanReschedulingApi;
@@ -392,6 +394,10 @@ public final class FineractFeignClient {
return create(ExternalAssetOwnersApi.class); return create(ExternalAssetOwnersApi.class);
} }
public ExternalAssetOwnersApiExtension externalAssetOwnersExtension() {
return create(ExternalAssetOwnersApiExtension.class);
}
public ExternalEventConfigurationApi externalEventConfiguration() { public ExternalEventConfigurationApi externalEventConfiguration() {
return create(ExternalEventConfigurationApi.class); return create(ExternalEventConfigurationApi.class);
} }
@@ -528,6 +534,10 @@ public final class FineractFeignClient {
return create(LoanDisbursementDetailsApi.class); return create(LoanDisbursementDetailsApi.class);
} }
public LoanDisbursementDetailsApiExtension loanDisbursementDetailsExtension() {
return create(LoanDisbursementDetailsApiExtension.class);
}
public LoanInterestPauseApi loanInterestPause() { public LoanInterestPauseApi loanInterestPause() {
return create(LoanInterestPauseApi.class); return create(LoanInterestPauseApi.class);
} }
@@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager; 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.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.classic.HttpClients;
@@ -95,7 +96,8 @@ public final class FineractFeignClientConfig {
Encoder multipartEncoder = new FineractMultipartEncoder(jacksonEncoder); Encoder multipartEncoder = new FineractMultipartEncoder(jacksonEncoder);
return Feign.builder().client(getOrCreateHttpClient()).encoder(multipartEncoder) 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)) .options(new Request.Options(connectTimeout, TimeUnit.MILLISECONDS, readTimeout, TimeUnit.MILLISECONDS, true))
.retryer(Retryer.NEVER_RETRY).requestInterceptor(new BasicAuthRequestInterceptor(username, password)) .retryer(Retryer.NEVER_RETRY).requestInterceptor(new BasicAuthRequestInterceptor(username, password))
.requestInterceptor(new TenantIdRequestInterceptor(tenantId)).logger(new Slf4jLogger(apiType)) .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 * specific language governing permissions and limitations
* under the License. * 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 feign.Param;
import retrofit2.Call; import feign.RequestLine;
import retrofit2.http.GET; 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; 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.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FeignException;
/** /**
* Exception thrown by {@link FeignCalls} utility when Feign calls fail. * 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()); sb.append(", request=").append(e.request().url());
} }
String contentString = e.contentUTF8(); String contentString = e.responseBodyAsString();
if (contentString != null && !contentString.isEmpty()) { if (contentString != null && !contentString.isEmpty()) {
sb.append(", errorBody=").append(contentString); sb.append(", errorBody=").append(contentString);
} }
@@ -58,34 +54,9 @@ public class CallFailedRuntimeException extends RuntimeException {
} }
private static String extractDeveloperMessage(FeignException e) { private static String extractDeveloperMessage(FeignException e) {
try { if (e.getDeveloperMessage() != null) {
byte[] content = e.content(); return e.getDeveloperMessage();
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();
} }
return e.getMessage();
} }
} }
@@ -18,8 +18,8 @@
*/ */
package org.apache.fineract.client.feign.util; package org.apache.fineract.client.feign.util;
import feign.FeignException;
import java.util.function.Supplier; 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. * 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 { public static <T> T execute(Supplier<T> feignCall) throws FeignException {
return feignCall.get(); 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}}; package {{package}};
import feign.*; {{#legacyDates}}
import feign.Param; import {{invokerPackage}}.ParamExpander;
import feign.RequestLine; {{/legacyDates}}
import {{modelPackage}}.ApiResponse;
{{#imports}}import {{import}}; {{#imports}}import {{import}};
{{/imports}} {{/imports}}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
{{#useBeanValidation}}
import {{javaxPackage}}.validation.constraints.*;
import {{javaxPackage}}.validation.Valid;
{{/useBeanValidation}}
import feign.*;
{{>generatedAnnotation}}
public interface {{classname}} { public interface {{classname}} {
{{#operations}} {{#operations}}{{#operation}}
{{#operation}} /**
/** * {{summary}}
* {{summary}} * {{notes}}
* {{notes}} {{#allParams}}
{{#allParams}} * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
* @param {{paramName}} {{description}} {{/allParams}}
{{/allParams}} {{#returnType}}
* @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} * @return {{.}}
*/ {{/returnType}}
@RequestLine("{{httpMethod}} {{path}}") {{#externalDocs}}
{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{operationId}}( * {{description}}
{{#pathParams}} * @see <a href="{{url}}">{{summary}} Documentation</a>
@Param("{{baseName}}") {{{dataType}}} {{paramName}}{{^-last}},{{/-last}} {{/externalDocs}}
{{/pathParams}} {{#isDeprecated}}
{{#bodyParam}} * @deprecated
{{#hasPathParams}},{{/hasPathParams}}{{{dataType}}} {{paramName}} {{/isDeprecated}}
{{/bodyParam}} */
{{#hasQueryParams}} {{#isDeprecated}}
{{#hasPathParams}}{{^bodyParam}},{{/bodyParam}}{{/hasPathParams}}{{#bodyParam}},{{/bodyParam}} @Deprecated
@QueryMap Map<String, Object> queryParams {{/isDeprecated}}
{{/hasQueryParams}} @RequestLine("{{httpMethod}} {{{path}}}{{#hasQueryParams}}?{{/hasQueryParams}}{{#queryParams}}{{baseName}}={{=<% %>=}}{<%paramName%>}<%={{ }}=%>{{^-last}}&{{/-last}}{{/queryParams}}")
{{#hasHeaderParams}} @Headers({
{{#hasPathParams}}{{^bodyParam}}{{^hasQueryParams}},{{/hasQueryParams}}{{/bodyParam}}{{/hasPathParams}} {{#vendorExtensions.x-content-type}} "Content-Type: {{{vendorExtensions.x-content-type}}}",
{{#bodyParam}}{{^hasQueryParams}},{{/hasQueryParams}}{{/bodyParam}} {{/vendorExtensions.x-content-type}} "Accept: {{#vendorExtensions.x-accepts}}{{{.}}}{{^-last}},{{/-last}}{{/vendorExtensions.x-accepts}}",{{#headerParams}}
{{#hasQueryParams}},{{/hasQueryParams}} "{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{^-last}},
@HeaderMap Map<String, Object> headerParams {{/-last}}{{/headerParams}}
{{/hasHeaderParams}} })
); {{#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}} {{/operations}}
} }
+2
View File
@@ -59,6 +59,7 @@ tasks.named('compileTestJava') {
dependencies { dependencies {
testImplementation(project(':fineract-avro-schemas')) testImplementation(project(':fineract-avro-schemas'))
testImplementation(project(':fineract-client')) testImplementation(project(':fineract-client'))
testImplementation(project(':fineract-client-feign'))
testImplementation 'org.springframework:spring-context' testImplementation 'org.springframework:spring-context'
implementation 'org.springframework:spring-test' implementation 'org.springframework:spring-test'
@@ -67,6 +68,7 @@ dependencies {
testImplementation 'com.github.spotbugs:spotbugs-annotations' testImplementation 'com.github.spotbugs:spotbugs-annotations'
testImplementation 'com.squareup.retrofit2:retrofit:2.11.0' 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.httpcomponents:httpclient:4.5.14'
testImplementation 'org.apache.commons:commons-lang3:3.18.0' testImplementation 'org.apache.commons:commons-lang3:3.18.0'
testImplementation ('com.googlecode.json-simple:json-simple:1.1.1') { 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; package org.apache.fineract.test.api;
import java.time.Duration; import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -33,15 +33,18 @@ public class FineractClientConfiguration {
private final ApiProperties apiProperties; private final ApiProperties apiProperties;
@Bean @Bean
public FineractClient fineractClient() { public FineractFeignClient fineractFeignClient() {
String baseUrl = apiProperties.getBaseUrl(); String baseUrl = apiProperties.getBaseUrl();
String username = apiProperties.getUsername(); String username = apiProperties.getUsername();
String password = apiProperties.getPassword(); String password = apiProperties.getPassword();
String tenantId = apiProperties.getTenantId(); String tenantId = apiProperties.getTenantId();
long readTimeout = apiProperties.getReadTimeout(); long readTimeout = apiProperties.getReadTimeout();
String apiBaseUrl = baseUrl + "/fineract-provider/api/"; String apiBaseUrl = baseUrl + "/fineract-provider/api/";
log.info("Using base URL '{}'", apiBaseUrl); boolean debugEnabled = Boolean.parseBoolean(System.getProperty("fineract.feign.debug", "false"));
return FineractClient.builder().readTimeout(Duration.ofSeconds(readTimeout)).basicAuth(username, password).tenant(tenantId)
.baseURL(apiBaseUrl).insecure(true).build(); 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; 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.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; 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.GetGLAccountsResponse;
import org.apache.fineract.client.services.GeneralLedgerAccountApi;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class AccountTypeResolver { public class AccountTypeResolver {
private final GeneralLedgerAccountApi glaApi; private final FineractFeignClient fineractClient;
@Cacheable(key = "#accountType.getName()", value = "accountTypesByName") @Cacheable(key = "#accountType.getName()", value = "accountTypesByName")
public long resolve(AccountType accountType) { public long resolve(AccountType accountType) {
try { String accountTypeName = accountType.getName();
String accountTypeName = accountType.getName(); log.debug("Resolving account type by name [{}]", accountTypeName);
log.debug("Resolving account type by name [{}]", accountTypeName); List<GetGLAccountsResponse> accountTypeResponses = ok(() -> fineractClient.generalLedgerAccount().retrieveAllAccountsUniversal(
Response<List<GetGLAccountsResponse>> response = glaApi.retrieveAllAccounts(null, "", 1, true, false, false).execute(); Map.of("usage", 1, "manualEntriesAllowed", true, "disabled", false, "fetchRunningBalance", false)));
if (!response.isSuccessful()) { GetGLAccountsResponse foundAtr = accountTypeResponses.stream()//
throw new IllegalStateException("Unable to get account types. Status code was HTTP " + response.code()); .filter(atr -> accountTypeName.equals(atr.getName()))//
} .findAny()//
List<GetGLAccountsResponse> accountTypeResponses = response.body(); .orElseThrow(() -> new IllegalArgumentException("Account type [%s] not found".formatted(accountTypeName)));//
GetGLAccountsResponse foundAtr = accountTypeResponses.stream()//
.filter(atr -> accountTypeName.equals(atr.getName()))//
.findAny()//
.orElseThrow(() -> new IllegalArgumentException("Account type [%s] not found".formatted(accountTypeName)));//
return foundAtr.getId(); return foundAtr.getId();
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
} }
@@ -18,41 +18,33 @@
*/ */
package org.apache.fineract.test.data.codevalue; 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.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetCodeValuesDataResponse; import org.apache.fineract.client.models.GetCodeValuesDataResponse;
import org.apache.fineract.client.services.CodeValuesApi;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class CodeValueResolver { public class CodeValueResolver {
private final CodeValuesApi codeValuesApi; private final FineractFeignClient fineractClient;
@Cacheable(key = "#codeValue.getName()", value = "codeValuesByName") @Cacheable(key = "#codeValue.getName()", value = "codeValuesByName")
public long resolve(Long codeId, CodeValue codeValue) { 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); log.debug("Resolving code value by code id and name [{}]", codeValue);
Response<List<GetCodeValuesDataResponse>> response = codeValuesApi.retrieveAllCodeValues(codeId).execute(); List<GetCodeValuesDataResponse> codeValuesResponses = ok(() -> fineractClient.codeValues().retrieveAllCodeValues(codeId, Map.of()));
if (!response.isSuccessful()) { GetCodeValuesDataResponse foundPtr = codeValuesResponses.stream().filter(ptr -> codeValueName.equals(ptr.getName())).findAny()
throw new IllegalStateException("Unable to get payment types. Status code was HTTP " + response.code()); .orElseThrow(() -> new IllegalArgumentException("Payment type [%s] not found".formatted(codeValueName)));
}
List<GetCodeValuesDataResponse> codeValuesResponses = response.body(); return foundPtr.getId();
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);
}
} }
} }
@@ -18,6 +18,7 @@
*/ */
package org.apache.fineract.test.data.datatable; package org.apache.fineract.test.data.datatable;
import java.util.Locale;
import org.apache.fineract.test.helper.Utils; import org.apache.fineract.test.helper.Utils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -25,6 +26,6 @@ import org.springframework.stereotype.Component;
public class DatatableNameGenerator { public class DatatableNameGenerator {
public String generate(DatatableEntityType entityType) { 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; 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.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetJobsResponse; import org.apache.fineract.client.models.GetJobsResponse;
import org.apache.fineract.client.services.SchedulerJobApi;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class JobResolver { public class JobResolver {
private final SchedulerJobApi schedulerJobApi; private final FineractFeignClient fineractClient;
@Cacheable(key = "#job.getShortName()", value = "jobsByShortName") @Cacheable(key = "#job.getShortName()", value = "jobsByShortName")
public long resolve(Job job) { public long resolve(Job job) {
try { String shortName = job.getShortName();
String shortName = job.getShortName(); log.debug("Resolving job by short-name [{}]", shortName);
log.debug("Resolving job by short-name [{}]", shortName); GetJobsResponse response = ok(() -> fineractClient.schedulerJob().retrieveByShortName(shortName, Map.of()));
Response<GetJobsResponse> response = schedulerJobApi.retrieveByShortName(shortName).execute(); return response.getJobId();
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);
}
} }
} }
@@ -18,39 +18,33 @@
*/ */
package org.apache.fineract.test.data.loanproduct; 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.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetLoanProductsResponse; 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 org.springframework.stereotype.Component;
import retrofit2.Response;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class LoanProductResolver { public class LoanProductResolver {
private final LoanProductsApi loanProductsApi; private final FineractFeignClient fineractClient;
@Cacheable(key = "#loanProduct.getName()", value = "loanProductsByName")
public long resolve(LoanProduct loanProduct) { public long resolve(LoanProduct loanProduct) {
try { String loanProductName = loanProduct.getName();
String loanProductName = loanProduct.getName(); log.debug("Resolving loan product by name [{}]", loanProductName);
log.debug("Resolving loan product by name [{}]", loanProductName); List<GetLoanProductsResponse> loanProductsResponses = ok(() -> fineractClient.loanProducts().retrieveAllLoanProducts(Map.of()));
Response<List<GetLoanProductsResponse>> response = loanProductsApi.retrieveAllLoanProducts().execute();
if (!response.isSuccessful()) {
throw new IllegalStateException("Unable to get loan products. Status code was HTTP " + response.code());
}
List<GetLoanProductsResponse> loanProductsResponses = response.body(); log.info("Retrieved {} loan products from API", loanProductsResponses.size());
GetLoanProductsResponse foundLpr = loanProductsResponses.stream().filter(lpr -> loanProductName.equals(lpr.getName())).findAny() log.info("Available loan products: {}", loanProductsResponses.stream().map(GetLoanProductsResponse::getName).toList());
.orElseThrow(() -> new IllegalArgumentException("Loan product [%s] not found".formatted(loanProductName)));
return foundLpr.getId(); GetLoanProductsResponse foundLpr = loanProductsResponses.stream().filter(lpr -> loanProductName.equals(lpr.getName())).findAny()
} catch (IOException e) { .orElseThrow(() -> new IllegalArgumentException("Loan product [%s] not found".formatted(loanProductName)));
throw new RuntimeException(e); return foundLpr.getId();
}
} }
} }
@@ -18,40 +18,33 @@
*/ */
package org.apache.fineract.test.data.paymenttype; 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.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; 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.PaymentTypeData;
import org.apache.fineract.client.services.PaymentTypeApi;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class PaymentTypeResolver { public class PaymentTypeResolver {
private final PaymentTypeApi paymentTypeApi; private final FineractFeignClient fineractClient;
@Cacheable(key = "#paymentType.getName()", value = "paymentTypesByName") @Cacheable(key = "#paymentType.getName()", value = "paymentTypesByName")
public long resolve(PaymentType paymentType) { public long resolve(PaymentType paymentType) {
try { String paymentTypeName = paymentType.getName();
String paymentTypeName = paymentType.getName(); log.debug("Resolving payment type by name [{}]", paymentTypeName);
log.debug("Resolving payment type by name [{}]", paymentTypeName); List<PaymentTypeData> paymentTypesResponses = ok(() -> fineractClient.paymentType().getAllPaymentTypesUniversal(Map.of()));
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());
}
List<PaymentTypeData> paymentTypesResponses = response.body(); PaymentTypeData foundPtr = paymentTypesResponses.stream().filter(ptr -> paymentTypeName.equals(ptr.getName())).findAny()
PaymentTypeData foundPtr = paymentTypesResponses.stream().filter(ptr -> paymentTypeName.equals(ptr.getName())).findAny() .orElseThrow(() -> new IllegalArgumentException("Payment type [%s] not found".formatted(paymentTypeName)));
.orElseThrow(() -> new IllegalArgumentException("Payment type [%s] not found".formatted(paymentTypeName)));
return foundPtr.getId(); return foundPtr.getId();
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
} }
@@ -18,17 +18,21 @@
*/ */
package org.apache.fineract.test.helper; 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.time.format.DateTimeFormatter;
import java.util.Map;
import lombok.RequiredArgsConstructor; 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.BusinessDateUpdateRequest;
import org.apache.fineract.client.models.BusinessDateUpdateResponse; 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.TestContext;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import retrofit2.Response;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
public class BusinessDateHelper { public class BusinessDateHelper {
@@ -38,18 +42,23 @@ public class BusinessDateHelper {
public static final String BUSINESS_DATE = "BUSINESS_DATE"; public static final String BUSINESS_DATE = "BUSINESS_DATE";
public static final String COB = "COB_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); 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 } catch (Exception e) {
.updateBusinessDate(null, businessDateRequest).execute(); log.error("Error: {}", e.getMessage());
ErrorHelper.checkSuccessfulApiCall(businessDateRequestResponse); throw e;
TestContext.INSTANCE.set(TestContextKey.BUSINESS_DATE_RESPONSE, businessDateRequestResponse); }
} }
public void setBusinessDateToday() throws IOException { public void setBusinessDateToday() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String today = formatter.format(Utils.now()); String today = formatter.format(Utils.now());
setBusinessDate(today); setBusinessDate(today);
@@ -60,7 +69,9 @@ public class BusinessDateHelper {
.locale(DEFAULT_LOCALE); .locale(DEFAULT_LOCALE);
} }
public String getBusinessDate() throws IOException { public String getBusinessDate() {
return businessDateManagementApi.getBusinessDate(BUSINESS_DATE).toString(); 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; 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.RequiredArgsConstructor;
import lombok.SneakyThrows; import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetCodesResponse; import org.apache.fineract.client.models.GetCodesResponse;
import org.apache.fineract.client.models.PostCodeValueDataResponse; import org.apache.fineract.client.models.PostCodeValueDataResponse;
import org.apache.fineract.client.models.PostCodeValuesDataRequest; 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 org.springframework.stereotype.Component;
import retrofit2.Response;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -37,31 +36,30 @@ public class CodeHelper {
private static final String STATE_CODE_NAME = "STATE"; private static final String STATE_CODE_NAME = "STATE";
private static final String ADDRESS_TYPE_CODE_NAME = "ADDRESS_TYPE"; private static final String ADDRESS_TYPE_CODE_NAME = "ADDRESS_TYPE";
private final CodesApi codesApi; private final FineractFeignClient fineractClient;
private final CodeValuesApi codeValuesApi;
public Response<PostCodeValueDataResponse> createAddressTypeCodeValue(String addressTypeName) throws IOException { public PostCodeValueDataResponse createAddressTypeCodeValue(String addressTypeName) {
Long codeId = retrieveCodeByName(ADDRESS_TYPE_CODE_NAME).getId(); 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(); 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(); 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) { 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")); .orElseThrow(() -> new IllegalArgumentException("Code with name " + name + " has not been found"));
} }
public Response<PostCodeValueDataResponse> createCodeValue(Long codeId, PostCodeValuesDataRequest request) throws IOException { public PostCodeValueDataResponse createCodeValue(Long codeId, PostCodeValuesDataRequest request) {
return codeValuesApi.createCodeValue(codeId, request).execute(); 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.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.client.models.BatchResponse; 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.Header;
import org.apache.fineract.client.models.LoanAccountLockResponseDTO; import org.apache.fineract.client.models.LoanAccountLockResponseDTO;
import retrofit2.Response; 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) { public static String repaymentUndoFailureDueToChargeOff(Long loanId) {
String loanIdStr = String.valueOf(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", 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); 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) { 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, return String.format("The transaction should be reversed, but it is not. Actual value is: %s - But expected value is: %s", actual,
expected); expected);
@@ -303,34 +292,6 @@ public final class ErrorMessageHelper {
expectedToStr); 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) { public static String wrongAmountInTransactionsOverpayment(Double actual, Double expected) {
String actualToStr = actual.toString(); String actualToStr = actual.toString();
String expectedToStr = expected.toString(); String expectedToStr = expected.toString();
@@ -338,57 +299,10 @@ public final class ErrorMessageHelper {
expectedToStr); 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) { 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); 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) { public static String wrongAmountInTotalOutstanding(Double actual, Double expected) {
String actualToStr = actual.toString(); String actualToStr = actual.toString();
String expectedToStr = expected.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."; 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) { 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", 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()); actual.toString(), expected.toString());
@@ -596,31 +496,6 @@ public final class ErrorMessageHelper {
resourceId, line, actual, expected); 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) { 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", return String.format("Wrong data in Loan details/Timeline/actualMaturityDate. Actual value is: %s - But expected value is: %s",
actual, expected); actual, expected);
@@ -707,10 +582,6 @@ public final class ErrorMessageHelper {
return String.format("No attribute %s for loan product %s is found!", attributeKey, loanProduct); 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) { public static String wrongValueInExternalAssetDetails(int line, List<List<String>> actual, List<String> expected) {
String lineStr = String.valueOf(line); String lineStr = String.valueOf(line);
String expectedStr = expected.toString(); String expectedStr = expected.toString();
@@ -774,13 +645,13 @@ public final class ErrorMessageHelper {
expectedStr); expectedStr);
} }
public static String listOfLockedLoansNotEmpty(Response<LoanAccountLockResponseDTO> response) { public static String listOfLockedLoansNotEmpty(LoanAccountLockResponseDTO response) {
String bodyStr = response.body().toString(); String bodyStr = response.toString();
return String.format("List of locked loan accounts is not empty. Actual response is: %n%s", bodyStr); 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) { public static String listOfLockedLoansContainsLoan(Long loanId, LoanAccountLockResponseDTO response) {
String bodyStr = response.body().toString(); 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); 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); 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() { public static String reAgeChargedOffLoanFailure() {
return "Loan re-aging is not allowed on charged-off loan."; return "Loan re-aging is not allowed on charged-off loan.";
} }
@@ -18,13 +18,14 @@
*/ */
package org.apache.fineract.test.helper; 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.io.IOException;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.apache.fineract.client.util.JSON; import org.apache.fineract.client.feign.ObjectMapperFactory;
import retrofit2.Response; import retrofit2.Response;
@NoArgsConstructor @NoArgsConstructor
@@ -32,36 +33,88 @@ import retrofit2.Response;
@Setter @Setter
public class ErrorResponse { public class ErrorResponse {
private static final Gson GSON = new JSON().getGson(); private static final ObjectMapper OBJECT_MAPPER = ObjectMapperFactory.getShared();
private String developerMessage; private String developerMessage;
private Integer httpStatusCode; 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) { if (errors.size() != 1) {
throw new IllegalStateException("Multiple errors found"); 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) { public static ErrorResponse from(Response retrofitResponse) {
try { try {
String errorBody = retrofitResponse.errorBody().string(); String errorBody = retrofitResponse.errorBody().string();
return GSON.fromJson(errorBody, ErrorResponse.class); return OBJECT_MAPPER.readValue(errorBody, ErrorResponse.class);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Error while parsing the error body", 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 @NoArgsConstructor
@Getter @Getter
@Setter @Setter
public static class Error { public static class ErrorDetail {
private String developerMessage; private String developerMessage;
private List<ErrorMessageArg> args; 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 @NoArgsConstructor
@@ -18,66 +18,60 @@
*/ */
package org.apache.fineract.test.helper; 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 static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException; import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.BooleanUtils; 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.GlobalConfigurationPropertyData;
import org.apache.fineract.client.models.PutGlobalConfigurationsRequest; 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 org.springframework.stereotype.Component;
import retrofit2.Response;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class GlobalConfigurationHelper { 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); switchAndSetGlobalConfiguration(configKey, false, value);
} }
public void enableGlobalConfiguration(String configKey, Long value) throws IOException { public void enableGlobalConfiguration(String configKey, Long value) {
switchAndSetGlobalConfiguration(configKey, true, value); switchAndSetGlobalConfiguration(configKey, true, value);
} }
private void switchAndSetGlobalConfiguration(String configKey, boolean enabled, Long value) throws IOException { private void switchAndSetGlobalConfiguration(String configKey, boolean enabled, Long value) {
Response<GlobalConfigurationPropertyData> configuration = globalConfigurationApi.retrieveOneByName(configKey).execute(); GlobalConfigurationPropertyData configuration = ok(
ErrorHelper.checkSuccessfulApiCall(configuration); () -> fineractClient.globalConfiguration().retrieveOneByName(configKey, Map.of()));
Long configId = configuration.body().getId(); Long configId = configuration.getId();
PutGlobalConfigurationsRequest updateRequest = new PutGlobalConfigurationsRequest().enabled(enabled).value(value); PutGlobalConfigurationsRequest updateRequest = new PutGlobalConfigurationsRequest().enabled(enabled).value(value);
Response<PutGlobalConfigurationsResponse> updateResponse = globalConfigurationApi.updateConfiguration1(configId, updateRequest) ok(() -> fineractClient.globalConfiguration().updateConfiguration1(configId, updateRequest, Map.of()));
.execute(); GlobalConfigurationPropertyData updatedConfiguration = ok(
assertThat(updateResponse.code()).isEqualTo(HttpStatus.SC_OK); () -> fineractClient.globalConfiguration().retrieveOneByName(configKey, Map.of()));
Response<GlobalConfigurationPropertyData> updatedConfiguration = globalConfigurationApi.retrieveOneByName(configKey).execute(); boolean isEnabled = BooleanUtils.toBoolean(updatedConfiguration.getEnabled());
boolean isEnabled = BooleanUtils.toBoolean(updatedConfiguration.body().getEnabled());
assertThat(isEnabled).isEqualTo(enabled); assertThat(isEnabled).isEqualTo(enabled);
} }
public void setGlobalConfigValueString(String configKey, String value) throws IOException { public void setGlobalConfigValueString(String configKey, String value) {
Response<GlobalConfigurationPropertyData> configuration = globalConfigurationApi.retrieveOneByName(configKey).execute(); GlobalConfigurationPropertyData configuration = ok(
ErrorHelper.checkSuccessfulApiCall(configuration); () -> fineractClient.globalConfiguration().retrieveOneByName(configKey, Map.of()));
Long configId = configuration.body().getId(); Long configId = configuration.getId();
PutGlobalConfigurationsRequest updateRequest = new PutGlobalConfigurationsRequest().enabled(true).stringValue(value); PutGlobalConfigurationsRequest updateRequest = new PutGlobalConfigurationsRequest().enabled(true).stringValue(value);
Response<PutGlobalConfigurationsResponse> updateResponse = globalConfigurationApi.updateConfiguration1(configId, updateRequest) ok(() -> fineractClient.globalConfiguration().updateConfiguration1(configId, updateRequest, Map.of()));
.execute(); GlobalConfigurationPropertyData updatedConfiguration = ok(
assertThat(updateResponse.code()).isEqualTo(HttpStatus.SC_OK); () -> fineractClient.globalConfiguration().retrieveOneByName(configKey, Map.of()));
Response<GlobalConfigurationPropertyData> updatedConfiguration = globalConfigurationApi.retrieveOneByName(configKey).execute(); boolean isEnabled = BooleanUtils.toBoolean(updatedConfiguration.getEnabled());
boolean isEnabled = BooleanUtils.toBoolean(updatedConfiguration.body().getEnabled());
assertThat(isEnabled).isEqualTo(true); assertThat(isEnabled).isEqualTo(true);
} }
public GlobalConfigurationPropertyData getGlobalConfiguration(String configKey) throws IOException { public GlobalConfigurationPropertyData getGlobalConfiguration(String configKey) {
Response<GlobalConfigurationPropertyData> configuration = globalConfigurationApi.retrieveOneByName(configKey).execute(); return ok(() -> fineractClient.globalConfiguration().retrieveOneByName(configKey, Map.of()));
ErrorHelper.checkSuccessfulApiCall(configuration);
return configuration.body();
} }
} }
@@ -18,18 +18,20 @@
*/ */
package org.apache.fineract.test.helper; 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.Comparator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; 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.BusinessStep;
import org.apache.fineract.client.models.BusinessStepRequest; import org.apache.fineract.client.models.BusinessStepRequest;
import org.apache.fineract.client.models.JobBusinessStepConfigData; import org.apache.fineract.client.models.JobBusinessStepConfigData;
import org.apache.fineract.client.services.BusinessStepConfigurationApi;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import retrofit2.Response;
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
@@ -38,9 +40,9 @@ public class WorkFlowJobHelper {
private static final String WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS = "LOAN_CLOSE_OF_BUSINESS"; 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), // 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("LOAN_DELINQUENCY_CLASSIFICATION").order(2L), //
new BusinessStep().stepName("CHECK_LOAN_REPAYMENT_DUE").order(3L), // 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)// new BusinessStep().stepName("EXTERNAL_ASSET_OWNER_TRANSFER").order(12L)//
); );
BusinessStepRequest request = new BusinessStepRequest().businessSteps(businessSteps); BusinessStepRequest request = new BusinessStepRequest().businessSteps(businessSteps);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request) executeVoid(() -> fineractClient.businessStepConfiguration().updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS,
.execute(); request, Map.of()));
ErrorHelper.checkSuccessfulApiCall(response);
// --- log changes ---
logChanges(); logChanges();
} }
private void logChanges() throws IOException { private void logChanges() {
// --- log changes --- JobBusinessStepConfigData changesResponse = ok(() -> fineractClient.businessStepConfiguration()
Response<JobBusinessStepConfigData> changesResponse = businessStepConfigurationApi .retrieveAllConfiguredBusinessStep(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, Map.of()));
.retrieveAllConfiguredBusinessStep(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS).execute(); List<BusinessStep> businessStepsChanged = changesResponse.getBusinessSteps();
List<BusinessStep> businessStepsChanged = changesResponse.body().getBusinessSteps();
List<String> changes = businessStepsChanged// List<String> changes = businessStepsChanged//
.stream()// .stream()//
.sorted(Comparator.comparingLong(BusinessStep::getOrder))// .sorted(Comparator.comparingLong(BusinessStep::getOrder))//
@@ -41,6 +41,12 @@ public class FineractInitializer implements InitializingBean {
@Override @Override
public void afterPropertiesSet() throws Exception { 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()) { if (log.isDebugEnabled()) {
String globalInitializers = globalInitializerSteps.stream().map(Object::getClass).map(Class::getName) String globalInitializers = globalInitializerSteps.stream().map(Object::getClass).map(Class::getName)
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
@@ -54,6 +60,11 @@ public class FineractInitializer implements InitializingBean {
Suite initializers: [{}] Suite initializers: [{}]
Scenario initializers: [{}] Scenario initializers: [{}]
""", globalInitializers, suiteInitializers, scenarioInitializers); """, 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 { public void setupDefaultsForSuite() throws Exception {
log.info("=== setupDefaultsForSuite() called - {} suite initializers to execute ===", suiteInitializerSteps.size());
for (FineractSuiteInitializerStep initializerStep : suiteInitializerSteps) { for (FineractSuiteInitializerStep initializerStep : suiteInitializerSteps) {
log.info("Executing suite initializer: {}", initializerStep.getClass().getName());
initializerStep.initializeForSuite(); initializerStep.initializeForSuite();
} }
businessDateHelper.setBusinessDateToday(); businessDateHelper.setBusinessDateToday();
@@ -18,10 +18,18 @@
*/ */
package org.apache.fineract.test.initializer.global; 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.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.ChargeRequest;
import org.apache.fineract.client.models.PostChargesResponse; 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.ChargeCalculationType;
import org.apache.fineract.test.data.ChargePaymentMode; import org.apache.fineract.test.data.ChargePaymentMode;
import org.apache.fineract.test.data.ChargeProductAppliesTo; 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.TestContext;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import retrofit2.Response;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
public class ChargeGlobalInitializerStep implements FineractGlobalInitializerStep { 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_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; 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 @Override
public void initialize() throws Exception { public void initialize() throws Exception {
// Loan - % late (overdue) fee List<ChargeData> existingCharges = new ArrayList<>();
ChargeRequest requestLoanPercentLate = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_PERCENTAGE_LATE_FEE, try {
CHARGE_TIME_TYPE_OVERDUE_FEES, CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT, CHARGE_AMOUNT_OVERDUE_PERCENTAGE, true, true); existingCharges = fineractClient.charges().retrieveAllCharges(Map.of());
Response<PostChargesResponse> responseLoanPercentLate = chargesApi.createCharge(requestLoanPercentLate).execute(); } 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); TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_PERCENT_LATE_CREATE_RESPONSE, responseLoanPercentLate);
// Loan - % processing fee PostChargesResponse responseLoanPercentProcessing = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
ChargeRequest requestLoanPercentProcessing = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_PERCENTAGE_PROCESSING_FEE, CHARGE_LOAN_PERCENTAGE_PROCESSING_FEE, CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE,
CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_PERCENTAGE_LOAN_AMOUNT_PLUS_INTEREST, CHARGE_AMOUNT_PERCENTAGE, CHARGE_CALCULATION_TYPE_PERCENTAGE_LOAN_AMOUNT_PLUS_INTEREST, CHARGE_AMOUNT_PERCENTAGE, true, false);
true, false);
Response<PostChargesResponse> responseLoanPercentProcessing = chargesApi.createCharge(requestLoanPercentProcessing).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_PERCENT_PROCESSING_CREATE_RESPONSE, responseLoanPercentProcessing); TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_PERCENT_PROCESSING_CREATE_RESPONSE, responseLoanPercentProcessing);
// Loan - fixed late (overdue) fee PostChargesResponse responseLoanFixedLate = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_FIXED_LATE_FEE,
ChargeRequest requestLoanFixedLate = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_FIXED_LATE_FEE,
CHARGE_TIME_TYPE_OVERDUE_FEES, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, true); 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); TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_FIXED_LATE_CREATE_RESPONSE, responseLoanFixedLate);
// Loan - fixed returned payment fee PostChargesResponse responseLoanFixedReturnedPayment = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
ChargeRequest requestLoanFixedReturnedPayment = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_FIXED_RETURNED_PAYMENT_FEE, CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_LOAN_FIXED_RETURNED_PAYMENT_FEE, CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_FLAT,
CHARGE_AMOUNT_FLAT, true, false); 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); TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_FIXED_RETURNED_PAYMENT_CREATE_RESPONSE, responseLoanFixedReturnedPayment);
// Loan - snooze fee PostChargesResponse responseLoanSnooze = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_SNOOZE_FEE,
ChargeRequest requestLoanSnooze = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_SNOOZE_FEE,
CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, false); 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); TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_SNOOZE_FEE_CREATE_RESPONSE, responseLoanSnooze);
// Loan - NSF fee PostChargesResponse responseLoanNsf = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_NSF_FEE,
ChargeRequest requestLoanNsf = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_NSF_FEE,
CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, true); 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); TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_NSF_FEE_CREATE_RESPONSE, responseLoanNsf);
// Loan - Disbursement % fee PostChargesResponse responseLoanDisbursePercent = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
ChargeRequest requestLoanDisbursePercent = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_DISBURSEMENT_PERCENT_FEE, CHARGE_LOAN_DISBURSEMENT_PERCENT_FEE, CHARGE_TIME_TYPE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT,
CHARGE_TIME_TYPE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT, CHARGE_AMOUNT_DISBURSEMENT_PERCENTAGE, true, CHARGE_AMOUNT_DISBURSEMENT_PERCENTAGE, true, false);
false);
Response<PostChargesResponse> responseLoanDisbursePercent = chargesApi.createCharge(requestLoanDisbursePercent).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_DISBURSEMENET_FEE_CREATE_RESPONSE, responseLoanDisbursePercent); TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_DISBURSEMENET_FEE_CREATE_RESPONSE, responseLoanDisbursePercent);
// Loan - Tranche Disbursement % fee PostChargesResponse responseLoanTrancheDisbursePercent = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
ChargeRequest requestLoanTrancheDisbursePercent = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_TRANCHE_DISBURSEMENT_PERCENT_FEE, CHARGE_TIME_TYPE_TRANCHE_DISBURSEMENT, CHARGE_LOAN_TRANCHE_DISBURSEMENT_PERCENT_FEE, CHARGE_TIME_TYPE_TRANCHE_DISBURSEMENT,
CHARGE_CALCULATION_TYPE_PERCENTAGE_DISBURSEMENT_AMOUNT, CHARGE_AMOUNT_DISBURSEMENT_PERCENTAGE, true, false); 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, TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_TRANCHE_DISBURSEMENT_PERCENT_CREATE_RESPONSE,
responseLoanTrancheDisbursePercent); responseLoanTrancheDisbursePercent);
// Loan - Installment % fee Amount + Interest PostChargesResponse responseLoanInstallmentPercentAmountPlusInterest = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
final ChargeRequest requestLoanInstallmentPercentAmountPlusInterest = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_INSTALLMENT_FEE_PERCENT_AMOUNT_PLUS_INTEREST, CHARGE_TIME_TYPE_INSTALLMENT, 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); 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, TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_INSTALLMENT_FEE_PERCENTAGE_AMOUNT_PLUS_INTEREST_CREATE_RESPONSE,
responseLoanInstallmentPercentAmountPlusInterest); responseLoanInstallmentPercentAmountPlusInterest);
// Loan - % late (overdue) fee amount+interest PostChargesResponse responseLoanPercentAmountPlusInterestLate = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
ChargeRequest requestLoanPercentAmountPlusInterestLate = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_PERCENTAGE_LATE_FEE_AMOUNT_PLUS_INTEREST, CHARGE_TIME_TYPE_OVERDUE_FEES, 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); 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, TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_PERCENT_LATE_AMOUNT_PLUS_INTEREST_CREATE_RESPONSE,
responseLoanPercentAmountPlusInterestLate); responseLoanPercentAmountPlusInterestLate);
// Client - fixed fee PostChargesResponse responseClientFixed = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_CLIENT, CHARGE_CLIENT_FIXED_FEE,
ChargeRequest requestClientFixed = defaultChargesRequest(CHARGE_APPLIES_TO_CLIENT, CHARGE_CLIENT_FIXED_FEE,
CHARGE_TIME_TYPE_SPECIFIED_DUE_DATE, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, false); 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); TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_CLIENT_FIXED_FEE_CREATE_RESPONSE, responseClientFixed);
// Loan - Disbursement fixed fee PostChargesResponse responseDisbursementCharge = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
ChargeRequest requestDisbursementCharge = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_DISBURSEMENT_CHARGE, CHARGE_DISBURSEMENT_CHARGE, CHARGE_TIME_TYPE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, false);
CHARGE_TIME_TYPE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_AMOUNT_FLAT, true, false);
Response<PostChargesResponse> responseDisbursementCharge = chargesApi.createCharge(requestDisbursementCharge).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_DISBURSEMENT_CHARGE_CREATE_RESPONSE, responseDisbursementCharge); TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_DISBURSEMENT_CHARGE_CREATE_RESPONSE, responseDisbursementCharge);
// Loan - Tranche Disbursement Charge (Flat) PostChargesResponse responseTrancheDisbursementCharge = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
ChargeRequest requestTrancheDisbursementCharge = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_TRANCHE_DISBURSEMENT_CHARGE_AMOUNT, CHARGE_TIME_TYPE_TRANCHE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_FLAT, 10.0, CHARGE_LOAN_TRANCHE_DISBURSEMENT_CHARGE_AMOUNT, CHARGE_TIME_TYPE_TRANCHE_DISBURSEMENT, CHARGE_CALCULATION_TYPE_FLAT, 10.0,
true, false); true, false);
Response<PostChargesResponse> responseTrancheDisbursementCharge = chargesApi.createCharge(requestTrancheDisbursementCharge)
.execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_TRANCHE_DISBURSEMENT_CHARGE_FLAT_CREATE_RESPONSE, TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_TRANCHE_DISBURSEMENT_CHARGE_FLAT_CREATE_RESPONSE,
responseTrancheDisbursementCharge); responseTrancheDisbursementCharge);
// Loan - Tranche Disbursement Charge (%) PostChargesResponse responseTrancheDisbursementChargePercent = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
ChargeRequest requestTrancheDisbursementChargePercent = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_TRANCHE_DISBURSEMENT_CHARGE_PERCENT, CHARGE_TIME_TYPE_TRANCHE_DISBURSEMENT, CHARGE_LOAN_TRANCHE_DISBURSEMENT_CHARGE_PERCENT, CHARGE_TIME_TYPE_TRANCHE_DISBURSEMENT,
CHARGE_CALCULATION_TYPE_PERCENTAGE_DISBURSEMENT_AMOUNT, 2.0, true, false); 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, TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_TRANCHE_DISBURSEMENT_CHARGE_PERCENT_CREATE_RESPONSE,
responseTrancheDisbursementChargePercent); responseTrancheDisbursementChargePercent);
// Loan - Installment flat fee PostChargesResponse responseLoanInstallmentFlat = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
final ChargeRequest requestLoanInstallmentFlat = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN, CHARGE_LOAN_INSTALLMENT_FEE_FLAT, CHARGE_LOAN_INSTALLMENT_FEE_FLAT, CHARGE_TIME_TYPE_INSTALLMENT, CHARGE_CALCULATION_TYPE_FLAT,
CHARGE_TIME_TYPE_INSTALLMENT, CHARGE_CALCULATION_TYPE_FLAT, CHARGE_INSTALLMENT_FEE_AMOUNT_FLAT, true, false); CHARGE_INSTALLMENT_FEE_AMOUNT_FLAT, true, false);
final Response<PostChargesResponse> responseLoanInstallmentFlat = chargesApi.createCharge(requestLoanInstallmentFlat).execute();
TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_INSTALLMENT_FEE_FLAT_CREATE_RESPONSE, responseLoanInstallmentFlat); TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_INSTALLMENT_FEE_FLAT_CREATE_RESPONSE, responseLoanInstallmentFlat);
// Loan - Installment % fee Amount PostChargesResponse responseLoanInstallmentPercentAmount = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
final ChargeRequest requestLoanInstallmentPercentAmount = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_INSTALLMENT_FEE_PERCENT_AMOUNT, CHARGE_TIME_TYPE_INSTALLMENT, CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT, CHARGE_LOAN_INSTALLMENT_FEE_PERCENT_AMOUNT, CHARGE_TIME_TYPE_INSTALLMENT, CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT,
CHARGE_INSTALLMENT_FEE_AMOUNT_PERCENTAGE, true, false); 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, TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_INSTALLMENT_FEE_PERCENTAGE_AMOUNT_CREATE_RESPONSE,
responseLoanInstallmentPercentAmount); responseLoanInstallmentPercentAmount);
// Loan - Installment % fee Interest PostChargesResponse responseLoanInstallmentPercentInterest = createChargeIfNotExists(charges, CHARGE_APPLIES_TO_LOAN,
final ChargeRequest requestLoanInstallmentPercentInterest = defaultChargesRequest(CHARGE_APPLIES_TO_LOAN,
CHARGE_LOAN_INSTALLMENT_FEE_PERCENT_INTEREST, CHARGE_TIME_TYPE_INSTALLMENT, CHARGE_CALCULATION_TYPE_PERCENTAGE_INTEREST, CHARGE_LOAN_INSTALLMENT_FEE_PERCENT_INTEREST, CHARGE_TIME_TYPE_INSTALLMENT, CHARGE_CALCULATION_TYPE_PERCENTAGE_INTEREST,
CHARGE_AMOUNT_PERCENTAGE, true, false); 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, TestContext.INSTANCE.set(TestContextKey.CHARGE_FOR_LOAN_INSTALLMENT_FEE_PERCENTAGE_INTEREST_CREATE_RESPONSE,
responseLoanInstallmentPercentInterest); 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, public static ChargeRequest defaultChargesRequest(Enum<ChargeProductAppliesTo> appliesTo, String name, Integer chargeTimeType,
Integer chargeCalculationType, Double amount, Boolean isActive, Boolean isPenalty) throws Exception { Integer chargeCalculationType, Double amount, Boolean isActive, Boolean isPenalty) throws Exception {
ChargeRequest request = new ChargeRequest(); ChargeRequest request = new ChargeRequest();
@@ -29,7 +29,7 @@ public class CobBusinessStepInitializerStep implements FineractGlobalInitializer
private final WorkFlowJobHelper workFlowJobHelper; private final WorkFlowJobHelper workFlowJobHelper;
@Override @Override
public void initialize() throws Exception { public void initialize() {
workFlowJobHelper.setWorkflowJobs(); workFlowJobHelper.setWorkflowJobs();
} }
} }
@@ -18,19 +18,23 @@
*/ */
package org.apache.fineract.test.initializer.global; 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; 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.PostCodeValuesDataRequest;
import org.apache.fineract.client.models.PostCodesRequest; import org.apache.fineract.client.models.PostCodesRequest;
import org.apache.fineract.client.models.PutCodeValuesDataRequest; 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.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
@Order(Ordered.HIGHEST_PRECEDENCE) @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_2 = "Forgiven";
public static final String CODE_VALUE_WRITE_OFF_REASON_TEST_3 = "Test"; public static final String CODE_VALUE_WRITE_OFF_REASON_TEST_3 = "Test";
private final CodesApi codesApi; private final FineractFeignClient fineractClient;
private final CodeValuesApi codeValuesApi;
@Override @Override
public void initialize() throws Exception { public void initialize() {
createCodeNames(); createCodeNames();
createCodeValues(); createCodeValues();
} }
@@ -267,9 +270,14 @@ public class CodeGlobalInitializerStep implements FineractGlobalInitializerStep
postCodeValuesDataRequest.position(position); postCodeValuesDataRequest.position(position);
try { try {
codeValuesApi.createCodeValue(codeId, postCodeValuesDataRequest).execute(); executeVoid(() -> fineractClient.codeValues().createCodeValue(codeId, postCodeValuesDataRequest, Map.of()));
} catch (IOException e) { log.info("Code value '{}' created successfully", name);
throw new RuntimeException("Error while creating code value", e); } 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.name(name);
putCodeValuesDataRequest.position(position); putCodeValuesDataRequest.position(position);
try { executeVoid(() -> fineractClient.codeValues().updateCodeValue(codeId, (long) position, putCodeValuesDataRequest, Map.of()));
codeValuesApi.updateCodeValue(codeId, (long) position, putCodeValuesDataRequest).execute();
} catch (IOException e) {
throw new RuntimeException("Error while updating code value", e);
}
}); });
} }
@@ -301,11 +305,13 @@ public class CodeGlobalInitializerStep implements FineractGlobalInitializerStep
codesNameList.add(CODE_NAME_ACTIVE_DUTY_TAG); codesNameList.add(CODE_NAME_ACTIVE_DUTY_TAG);
codesNameList.forEach(codeName -> { codesNameList.forEach(codeName -> {
PostCodesRequest postCodesRequest = new PostCodesRequest();
try { try {
codesApi.createCode(postCodesRequest.name(codeName)).execute(); fineractClient.codes().retrieveCodeByName(codeName);
} catch (IOException e) { // Code already exists, skip creation
throw new RuntimeException("Error while creating code", e); } 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; package org.apache.fineract.test.initializer.global;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.CurrencyUpdateRequest; 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.TestContext;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
@@ -36,12 +39,12 @@ public class CurrencyGlobalInitializerStep implements FineractGlobalInitializerS
public static final List<String> CURRENCIES = Arrays.asList("EUR", "USD"); public static final List<String> CURRENCIES = Arrays.asList("EUR", "USD");
private final CurrencyApi currencyApi; private final FineractFeignClient fineractClient;
@Override @Override
public void initialize() throws Exception { public void initialize() {
var request = new CurrencyUpdateRequest(); 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); TestContext.INSTANCE.set(TestContextKey.PUT_CURRENCIES_RESPONSE, response);
} }
} }
@@ -18,16 +18,21 @@
*/ */
package org.apache.fineract.test.initializer.global; package org.apache.fineract.test.initializer.global;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; 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.PostColumnHeaderData;
import org.apache.fineract.client.models.PostDataTablesRequest; import org.apache.fineract.client.models.PostDataTablesRequest;
import org.apache.fineract.client.services.DataTablesApi;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
@Order(Ordered.HIGHEST_PRECEDENCE) @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_TYPE = "Dropdown";
public static final String DATA_TABLE_3_COLUMN_5_CODE = "active_duty_tag"; public static final String DATA_TABLE_3_COLUMN_5_CODE = "active_duty_tag";
private final DataTablesApi dataTablesApi; private final FineractFeignClient fineractClient;
@Override @Override
public void initialize() throws Exception { public void initialize() {
// autopay // autopay
PostColumnHeaderData column1 = new PostColumnHeaderData(); PostColumnHeaderData column1 = new PostColumnHeaderData();
column1.name(DATA_TABLE_1_COLUMN_1_NAME); column1.name(DATA_TABLE_1_COLUMN_1_NAME);
@@ -97,7 +102,7 @@ public class DatatablesGlobalInitializerStep implements FineractGlobalInitialize
postDataTablesRequest.multiRow(true); postDataTablesRequest.multiRow(true);
postDataTablesRequest.columns(columns); postDataTablesRequest.columns(columns);
dataTablesApi.createDatatable(postDataTablesRequest).execute(); createDatatableIdempotent(postDataTablesRequest);
// scheduled payments // scheduled payments
PostColumnHeaderData columnScheduled1 = new PostColumnHeaderData(); PostColumnHeaderData columnScheduled1 = new PostColumnHeaderData();
@@ -134,7 +139,7 @@ public class DatatablesGlobalInitializerStep implements FineractGlobalInitialize
postDataTablesRequestScheduled.multiRow(true); postDataTablesRequestScheduled.multiRow(true);
postDataTablesRequestScheduled.columns(columnsScheduled); postDataTablesRequestScheduled.columns(columnsScheduled);
dataTablesApi.createDatatable(postDataTablesRequestScheduled).execute(); createDatatableIdempotent(postDataTablesRequestScheduled);
// 3 tags // 3 tags
PostColumnHeaderData column3Tags1 = new PostColumnHeaderData(); PostColumnHeaderData column3Tags1 = new PostColumnHeaderData();
@@ -181,6 +186,16 @@ public class DatatablesGlobalInitializerStep implements FineractGlobalInitialize
postDataTablesRequest3Tags.multiRow(false); postDataTablesRequest3Tags.multiRow(false);
postDataTablesRequest3Tags.columns(columns3Tags); 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; 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.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; 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.DelinquencyBucketRequest;
import org.apache.fineract.client.models.DelinquencyRangeData;
import org.apache.fineract.client.models.DelinquencyRangeRequest; 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.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
@Order(Ordered.HIGHEST_PRECEDENCE) @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 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"; 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 @Override
public void initialize() throws Exception { public void initialize() {
setDefaultDelinquencyRanges(); setDefaultDelinquencyRanges();
setDefaultDelinquencyBucket(); 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++) { 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(); DelinquencyRangeRequest postDelinquencyRangeRequest = new DelinquencyRangeRequest();
postDelinquencyRangeRequest.classification("Delinquency range " + DEFAULT_DELINQUENCY_RANGES.get(i).toString()); postDelinquencyRangeRequest.classification(classification);
postDelinquencyRangeRequest.locale(DEFAULT_LOCALE); postDelinquencyRangeRequest.locale(DEFAULT_LOCALE);
if (DEFAULT_DELINQUENCY_RANGES.get(i) == 1) { if (DEFAULT_DELINQUENCY_RANGES.get(i) == 1) {
postDelinquencyRangeRequest.minimumAgeDays(1); postDelinquencyRangeRequest.minimumAgeDays(1);
@@ -60,30 +88,49 @@ public class DelinquencyGlobalInitializerStep implements FineractGlobalInitializ
postDelinquencyRangeRequest.maximumAgeDays(DEFAULT_DELINQUENCY_RANGES.get(i + 1)); 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(); 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.locale(DEFAULT_LOCALE);
lastRange.minimumAgeDays(DEFAULT_DELINQUENCY_RANGES.get(DEFAULT_DELINQUENCY_RANGES.size() - 1) + 1); lastRange.minimumAgeDays(DEFAULT_DELINQUENCY_RANGES.get(DEFAULT_DELINQUENCY_RANGES.size() - 1) + 1);
lastRange.maximumAgeDays(null); 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 { public void setDefaultDelinquencyBucket() {
List<Long> rangesNr = new ArrayList<>(); 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++) { if (bucketExists) {
rangesNr.add((long) DEFAULT_DELINQUENCY_RANGES.indexOf(DEFAULT_DELINQUENCY_RANGES.get(i - 1))); 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(); DelinquencyBucketRequest postDelinquencyBucketRequest = new DelinquencyBucketRequest();
postDelinquencyBucketRequest.name(DEFAULT_DELINQUENCY_BUCKET_NAME); 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; 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.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.models.PostFinancialActivityAccountsRequest;
import org.apache.fineract.client.services.MappingFinancialActivitiesToAccountsApi;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
public class FinancialActivityMappingGlobalInitializerStep implements FineractGlobalInitializerStep { 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 FINANCIAL_ACTIVITY_ID_ASSET_TRANSFER = 100L;
public static final Long GL_ACCOUNT_ID_ASSET_TRANSFER = 21L; public static final Long GL_ACCOUNT_ID_ASSET_TRANSFER = 21L;
private final MappingFinancialActivitiesToAccountsApi mappingFinancialActivitiesToAccountsApi; private final FineractFeignClient fineractClient;
@Override @Override
public void initialize() throws Exception { public void initialize() {
PostFinancialActivityAccountsRequest request = new PostFinancialActivityAccountsRequest() PostFinancialActivityAccountsRequest request = new PostFinancialActivityAccountsRequest()
.financialActivityId(FINANCIAL_ACTIVITY_ID_ASSET_TRANSFER).glAccountId(GL_ACCOUNT_ID_ASSET_TRANSFER); .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; 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; 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.models.FundRequest;
import org.apache.fineract.client.services.FundsApi;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE) @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_A = "Lender A";
public static final String FUNDS_LENDER_B = "Lender B"; public static final String FUNDS_LENDER_B = "Lender B";
private final FundsApi fundsApi; private final FineractFeignClient fineractClient;
@Override @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<>(); List<String> fundNames = new ArrayList<>();
fundNames.add(FUNDS_LENDER_A); fundNames.add(FUNDS_LENDER_A);
fundNames.add(FUNDS_LENDER_B); fundNames.add(FUNDS_LENDER_B);
fundNames.forEach(name -> { fundNames.forEach(name -> {
boolean fundExists = funds.stream().anyMatch(f -> name.equals(f.getName()));
if (fundExists) {
return;
}
FundRequest postFundsRequest = new FundRequest(); FundRequest postFundsRequest = new FundRequest();
postFundsRequest.name(name); postFundsRequest.name(name);
try { executeVoid(() -> fineractClient.funds().createFund(postFundsRequest, Map.of()));
fundsApi.createFund(postFundsRequest).execute();
} catch (IOException e) {
throw new RuntimeException("Error while creating fund", e);
}
}); });
} }
@@ -18,9 +18,16 @@
*/ */
package org.apache.fineract.test.initializer.global; 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.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.models.PostGLAccountsRequest;
import org.apache.fineract.client.services.GeneralLedgerAccountApi;
import org.apache.fineract.test.data.GLAType; import org.apache.fineract.test.data.GLAType;
import org.apache.fineract.test.data.GLAUsage; import org.apache.fineract.test.data.GLAUsage;
import org.apache.fineract.test.factory.GLAccountRequestFactory; 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.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
@Order(Ordered.HIGHEST_PRECEDENCE) @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_23 = "450280";
public static final String GLA_GL_CODE_24 = "450281"; public static final String GLA_GL_CODE_24 = "450281";
private final GeneralLedgerAccountApi glaApi; private final FineractFeignClient fineractClient;
@Override @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, final List<GetGLAccountsResponse> accounts = existingAccounts;
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest1).execute(); createGLAccountIfNotExists(accounts, GLA_NAME_1, GLA_GL_CODE_1, GLA_TYPE_ASSET);
PostGLAccountsRequest postGLAccountsRequest2 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_2, GLA_GL_CODE_2, createGLAccountIfNotExists(accounts, GLA_NAME_2, GLA_GL_CODE_2, GLA_TYPE_ASSET);
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true); createGLAccountIfNotExists(accounts, GLA_NAME_3, GLA_GL_CODE_3, GLA_TYPE_ASSET);
glaApi.createGLAccount1(postGLAccountsRequest2).execute(); createGLAccountIfNotExists(accounts, GLA_NAME_4, GLA_GL_CODE_4, GLA_TYPE_ASSET);
PostGLAccountsRequest postGLAccountsRequest3 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_3, GLA_GL_CODE_3, createGLAccountIfNotExists(accounts, GLA_NAME_5, GLA_GL_CODE_5, GLA_TYPE_LIABILITY);
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true); createGLAccountIfNotExists(accounts, GLA_NAME_6, GLA_GL_CODE_6, GLA_TYPE_LIABILITY);
glaApi.createGLAccount1(postGLAccountsRequest3).execute(); createGLAccountIfNotExists(accounts, GLA_NAME_7, GLA_GL_CODE_7, GLA_TYPE_INCOME);
PostGLAccountsRequest postGLAccountsRequest4 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_4, GLA_GL_CODE_4, createGLAccountIfNotExists(accounts, GLA_NAME_8, GLA_GL_CODE_8, GLA_TYPE_INCOME);
GLA_TYPE_ASSET, GLA_USAGE_DETAIL, true); createGLAccountIfNotExists(accounts, GLA_NAME_9, GLA_GL_CODE_9, GLA_TYPE_INCOME);
glaApi.createGLAccount1(postGLAccountsRequest4).execute(); createGLAccountIfNotExists(accounts, GLA_NAME_10, GLA_GL_CODE_10, GLA_TYPE_INCOME);
PostGLAccountsRequest postGLAccountsRequest5 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_5, GLA_GL_CODE_5, createGLAccountIfNotExists(accounts, GLA_NAME_11, GLA_GL_CODE_11, GLA_TYPE_INCOME);
GLA_TYPE_LIABILITY, GLA_USAGE_DETAIL, true); createGLAccountIfNotExists(accounts, GLA_NAME_12, GLA_GL_CODE_12, GLA_TYPE_EXPENSE);
glaApi.createGLAccount1(postGLAccountsRequest5).execute(); createGLAccountIfNotExists(accounts, GLA_NAME_13, GLA_GL_CODE_13, GLA_TYPE_EXPENSE);
PostGLAccountsRequest postGLAccountsRequest6 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_6, GLA_GL_CODE_6, createGLAccountIfNotExists(accounts, GLA_NAME_14, GLA_GL_CODE_14, GLA_TYPE_ASSET);
GLA_TYPE_LIABILITY, GLA_USAGE_DETAIL, true); createGLAccountIfNotExists(accounts, GLA_NAME_15, GLA_GL_CODE_15, GLA_TYPE_INCOME);
glaApi.createGLAccount1(postGLAccountsRequest6).execute(); createGLAccountIfNotExists(accounts, GLA_NAME_16, GLA_GL_CODE_16, GLA_TYPE_EXPENSE);
PostGLAccountsRequest postGLAccountsRequest7 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_7, GLA_GL_CODE_7, createGLAccountIfNotExists(accounts, GLA_NAME_17, GLA_GL_CODE_17, GLA_TYPE_LIABILITY);
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true); createGLAccountIfNotExists(accounts, GLA_NAME_18, GLA_GL_CODE_18, GLA_TYPE_ASSET);
glaApi.createGLAccount1(postGLAccountsRequest7).execute(); createGLAccountIfNotExists(accounts, GLA_NAME_19, GLA_GL_CODE_19, GLA_TYPE_EXPENSE);
PostGLAccountsRequest postGLAccountsRequest8 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_8, GLA_GL_CODE_8, createGLAccountIfNotExists(accounts, GLA_NAME_20, GLA_GL_CODE_20, GLA_TYPE_INCOME);
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true); createGLAccountIfNotExists(accounts, GLA_NAME_21, GLA_GL_CODE_21, GLA_TYPE_ASSET);
glaApi.createGLAccount1(postGLAccountsRequest8).execute(); createGLAccountIfNotExists(accounts, GLA_NAME_22, GLA_GL_CODE_22, GLA_TYPE_LIABILITY);
PostGLAccountsRequest postGLAccountsRequest9 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_9, GLA_GL_CODE_9, createGLAccountIfNotExists(accounts, GLA_NAME_23, GLA_GL_CODE_23, GLA_TYPE_EXPENSE);
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true); createGLAccountIfNotExists(accounts, GLA_NAME_24, GLA_GL_CODE_24, GLA_TYPE_INCOME);
glaApi.createGLAccount1(postGLAccountsRequest9).execute(); }
PostGLAccountsRequest postGLAccountsRequest10 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_10, GLA_GL_CODE_10,
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true); private void createGLAccountIfNotExists(List<GetGLAccountsResponse> existingAccounts, String name, String glCode, Integer type) {
glaApi.createGLAccount1(postGLAccountsRequest10).execute(); boolean accountExists = existingAccounts.stream().anyMatch(a -> glCode.equals(a.getGlCode()));
PostGLAccountsRequest postGLAccountsRequest11 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_11, GLA_GL_CODE_11, if (accountExists) {
GLA_TYPE_INCOME, GLA_USAGE_DETAIL, true); return;
glaApi.createGLAccount1(postGLAccountsRequest11).execute(); }
PostGLAccountsRequest postGLAccountsRequest12 = GLAccountRequestFactory.defaultGLAccountRequest(GLA_NAME_12, GLA_GL_CODE_12,
GLA_TYPE_EXPENSE, GLA_USAGE_DETAIL, true); PostGLAccountsRequest request = GLAccountRequestFactory.defaultGLAccountRequest(name, glCode, type, GLA_USAGE_DETAIL, true);
glaApi.createGLAccount1(postGLAccountsRequest12).execute(); executeVoid(() -> fineractClient.generalLedgerAccount().createGLAccount1(request, Map.of()));
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();
} }
} }
@@ -18,17 +18,22 @@
*/ */
package org.apache.fineract.test.initializer.global; 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; 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.models.PaymentTypeRequest;
import org.apache.fineract.client.services.PaymentTypeApi;
import org.apache.fineract.test.factory.PaymentTypesRequestFactory; import org.apache.fineract.test.factory.PaymentTypesRequestFactory;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
@Order(Ordered.HIGHEST_PRECEDENCE) @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_CHARGEBACK = "REPAYMENT_ADJUSTMENT_CHARGEBACK";
public static final String PAYMENT_TYPE_REPAYMENT_ADJUSTMENT_REFUND = "REPAYMENT_ADJUSTMENT_REFUND"; public static final String PAYMENT_TYPE_REPAYMENT_ADJUSTMENT_REFUND = "REPAYMENT_ADJUSTMENT_REFUND";
private final PaymentTypeApi paymentTypeApi; private final FineractFeignClient fineractClient;
@Override @Override
public void initialize() throws Exception { public void initialize() {
List<String> paymentTypes = new ArrayList<>(); List<PaymentTypeData> existingPaymentTypes = new ArrayList<>();
paymentTypes.add(PAYMENT_TYPE_AUTOPAY); try {
paymentTypes.add(PAYMENT_TYPE_DOWN_PAYMENT); existingPaymentTypes = fineractClient.paymentType().getAllPaymentTypesUniversal(Map.of());
paymentTypes.add(PAYMENT_TYPE_REAL_TIME); } catch (Exception e) {
paymentTypes.add(PAYMENT_TYPE_SCHEDULED); log.debug("Could not retrieve existing payment types, will create them", e);
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);
paymentTypes.forEach(paymentType -> { final List<PaymentTypeData> paymentTypes = existingPaymentTypes;
Integer position = paymentTypes.indexOf(paymentType) + 2;
PaymentTypeRequest postPaymentTypesRequest = PaymentTypesRequestFactory.defaultPaymentTypeRequest(paymentType, paymentType,
false, position);
try { List<String> paymentTypeNames = new ArrayList<>();
paymentTypeApi.createPaymentType(postPaymentTypesRequest).execute(); paymentTypeNames.add(PAYMENT_TYPE_AUTOPAY);
} catch (IOException e) { paymentTypeNames.add(PAYMENT_TYPE_DOWN_PAYMENT);
throw new RuntimeException("Error while creating payment type", e); 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 { public class SavingsProductGlobalInitializer implements FineractGlobalInitializerStep {
@Override @Override
public void initialize() throws Exception { public void initialize() {
/** // TODO uncomment and check when PS-1088 is done
* TODO uncomment and check when PS-1088 is done
*/
// //EUR // //EUR
// PostSavingsProductsRequest savingsProductsRequestEUR = // PostSavingsProductsRequest savingsProductsRequestEUR =
// SavingsProductRequestFactory.defaultSavingsProductRequest(); // SavingsProductRequestFactory.defaultSavingsProductRequest();
@@ -18,8 +18,11 @@
*/ */
package org.apache.fineract.test.initializer.global; 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.RequiredArgsConstructor;
import org.apache.fineract.client.services.SchedulerApi; import org.apache.fineract.client.feign.FineractFeignClient;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -31,10 +34,10 @@ public class SchedulerGlobalInitializerStep implements FineractGlobalInitializer
private static final String SCHEDULER_STATUS_STOP = "stop"; private static final String SCHEDULER_STATUS_STOP = "stop";
private final SchedulerApi schedulerApi; private final FineractFeignClient fineractClient;
@Override @Override
public void initialize() throws Exception { 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 @Override
public void initializeForScenario() throws Exception { public void initializeForScenario() throws Exception {
/** // Enable-address set to false
* Enable-address set to false
*/
globalConfigurationHelper.disableGlobalConfiguration(CONFIG_KEY_ENABLE_ADDRESS, 0L); 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_BUSINESS_DATE, 0L);
globalConfigurationHelper.enableGlobalConfiguration(CONFIG_KEY_ENABLE_RECALCULATE_COB_DATE, 0L); globalConfigurationHelper.enableGlobalConfiguration(CONFIG_KEY_ENABLE_RECALCULATE_COB_DATE, 0L);
} }
@@ -18,36 +18,52 @@
*/ */
package org.apache.fineract.test.initializer.suite; 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.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.RequiredArgsConstructor; 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.ExternalEventConfigurationItemResponse;
import org.apache.fineract.client.models.ExternalEventConfigurationResponse; import org.apache.fineract.client.models.ExternalEventConfigurationResponse;
import org.apache.fineract.client.models.ExternalEventConfigurationUpdateRequest; 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 org.springframework.stereotype.Component;
import retrofit2.Response;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
public class ExternalEventSuiteInitializerStep implements FineractSuiteInitializerStep { 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 @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<>(); Map<String, Boolean> eventConfigMap = new HashMap<>();
Response<ExternalEventConfigurationResponse> response = eventConfigurationApi.getExternalEventConfigurations().execute(); ExternalEventConfigurationResponse response = ok(
if (!response.isSuccessful()) { () -> fineractClient.externalEventConfiguration().getExternalEventConfigurations(Map.of()));
String responseBody = response.errorBody().string();
throw new RuntimeException("Cannot configure external events due to " + lineSeparator() + responseBody);
}
List<ExternalEventConfigurationItemResponse> externalEventConfiguration = response.body().getExternalEventConfiguration(); List<ExternalEventConfigurationItemResponse> externalEventConfiguration = response.getExternalEventConfiguration();
externalEventConfiguration.forEach(e -> { externalEventConfiguration.forEach(e -> {
eventConfigMap.put(e.getType(), true); eventConfigMap.put(e.getType(), true);
}); });
@@ -55,6 +71,28 @@ public class ExternalEventSuiteInitializerStep implements FineractSuiteInitializ
ExternalEventConfigurationUpdateRequest request = new ExternalEventConfigurationUpdateRequest() ExternalEventConfigurationUpdateRequest request = new ExternalEventConfigurationUpdateRequest()
.externalEventConfigurations(eventConfigMap); .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; package org.apache.fineract.test.initializer.suite;
import java.io.IOException; import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import lombok.RequiredArgsConstructor; 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.GetJobsResponse;
import org.apache.fineract.client.models.PutJobsJobIDRequest; import org.apache.fineract.client.models.PutJobsJobIDRequest;
import org.apache.fineract.client.services.SchedulerJobApi;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@Component @Component
@RequiredArgsConstructor
public class JobSuiteInitializerStep implements FineractSuiteInitializerStep { public class JobSuiteInitializerStep implements FineractSuiteInitializerStep {
public static final String SEND_ASYNCHRONOUS_EVENTS_JOB_NAME = "Send Asynchronous Events"; 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_1_SECONDS = "0/1 * * * * ?";
public static final String EVERY_60_SECONDS = "0 0/1 * * * ?"; public static final String EVERY_60_SECONDS = "0 0/1 * * * ?";
private final SchedulerJobApi jobApi; private final FineractFeignClient fineractClient;
@Override public JobSuiteInitializerStep(FineractFeignClient fineractClient) {
public void initializeForSuite() throws Exception { log.info("=== JobSuiteInitializerStep: Constructor called - bean is being created ===");
updateExternalEventJobFrequency(EVERY_1_SECONDS); this.fineractClient = fineractClient;
log.info("=== JobSuiteInitializerStep: FineractFeignClient injected successfully ===");
} }
@Override @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); updateExternalEventJobFrequency(EVERY_60_SECONDS);
log.info("=== JobSuiteInitializerStep.resetAfterSuite() - COMPLETED ===");
} }
private void updateExternalEventJobFrequency(String cronExpression) throws IOException { private Long updateExternalEventJobFrequency(String cronExpression) {
GetJobsResponse externalEventJobResponse = jobApi.retrieveAll8().execute().body().stream() GetJobsResponse externalEventJobResponse = ok(() -> fineractClient.schedulerJob().retrieveAll8()).stream()
.filter(r -> r.getDisplayName().equals(SEND_ASYNCHRONOUS_EVENTS_JOB_NAME)).findAny() .filter(r -> r.getDisplayName().equals(SEND_ASYNCHRONOUS_EVENTS_JOB_NAME)).findAny()
.orElseThrow(() -> new IllegalStateException(SEND_ASYNCHRONOUS_EVENTS_JOB_NAME + " is not found")); .orElseThrow(() -> new IllegalStateException(SEND_ASYNCHRONOUS_EVENTS_JOB_NAME + " is not found"));
Long jobId = externalEventJobResponse.getJobId(); 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; 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.apache.fineract.test.stepdef.loan.LoanRepaymentStepDef.DATE_FORMAT;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.avro.client.v1.ClientDataV1; 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.LoanOwnershipTransferDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionAdjustmentDataV1; import org.apache.fineract.avro.loan.v1.LoanTransactionAdjustmentDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1; 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.ExternalTransferData;
import org.apache.fineract.client.models.GetClientsClientIdResponse; import org.apache.fineract.client.models.GetClientsClientIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdDelinquencyPausePeriod; 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.PostLoansLoanIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansResponse; 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.AssetExternalizationTransferStatus;
import org.apache.fineract.test.data.AssetExternalizationTransferStatusReason; import org.apache.fineract.test.data.AssetExternalizationTransferStatusReason;
import org.apache.fineract.test.data.TransactionType; 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.apache.fineract.test.messaging.event.loan.transaction.LoanUndoContractTerminationBusinessEvent;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import retrofit2.Response;
@Slf4j @Slf4j
@Component @Component
@@ -91,103 +89,85 @@ import retrofit2.Response;
public class EventCheckHelper { public class EventCheckHelper {
private static final DateTimeFormatter FORMATTER_EVENTS = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter FORMATTER_EVENTS = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final long TRANSACTION_COMMIT_DELAY_MS = 500L;
@Autowired @Autowired
private ClientApi clientApi; private FineractFeignClient fineractClient;
@Autowired
private LoansApi loansApi;
@Autowired @Autowired
private EventAssertion eventAssertion; private EventAssertion eventAssertion;
@Autowired @Autowired
private ExternalAssetOwnersApi externalAssetOwnersApi;
@Autowired
private GlobalConfigurationHelper configurationHelper; private GlobalConfigurationHelper configurationHelper;
@Autowired
private org.apache.fineract.test.messaging.config.EventProperties eventProperties;
public void clientEventCheck(Response<PostClientsResponse> clientCreationResponse) throws IOException { private void waitForTransactionCommit() {
Response<GetClientsClientIdResponse> clientDetails = clientApi.retrieveOne11(clientCreationResponse.body().getClientId(), false) if (eventProperties.isEventVerificationEnabled() && TRANSACTION_COMMIT_DELAY_MS > 0) {
.execute(); 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()); Long clientId = Long.valueOf(body.getId());
Integer status = body.getStatus().getId().intValue(); Integer status = body.getStatus().getId().intValue();
String firstname = body.getFirstname(); String firstname = body.getFirstname();
String lastname = body.getLastname(); String lastname = body.getLastname();
Boolean active = body.getActive(); Boolean active = body.getActive();
eventAssertion.assertEvent(ClientCreatedEvent.class, clientCreationResponse.body().getClientId())// eventAssertion.assertEvent(ClientCreatedEvent.class, clientCreationResponse.getClientId())//
.extractingData(ClientDataV1::getId).isEqualTo(clientId)// .extractingData(ClientDataV1::getId).isEqualTo(clientId)//
.extractingData(clientDataV1 -> clientDataV1.getStatus().getId()).isEqualTo(status)// .extractingData(clientDataV1 -> clientDataV1.getStatus().getId()).isEqualTo(status)//
.extractingData(ClientDataV1::getFirstname).isEqualTo(firstname)// .extractingData(ClientDataV1::getFirstname).isEqualTo(firstname)//
.extractingData(ClientDataV1::getLastname).isEqualTo(lastname)// .extractingData(ClientDataV1::getLastname).isEqualTo(lastname)//
.extractingData(ClientDataV1::getActive).isEqualTo(active);// .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::getActive).isEqualTo(true)//
.extractingData(clientDataV1 -> clientDataV1.getStatus().getId()).isEqualTo(status);// .extractingData(clientDataV1 -> clientDataV1.getStatus().getId()).isEqualTo(status);//
} }
public void createLoanEventCheck(Response<PostLoansResponse> createLoanResponse) throws IOException { public void undoApproveLoanEventCheck(PostLoansLoanIdResponse loanUndoApproveResponse) {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(createLoanResponse.body().getLoanId(), false, "all", "", "") waitForTransactionCommit();
.execute(); GetLoansLoanIdResponse body = ok(() -> fineractClient.loans().retrieveLoan(loanUndoApproveResponse.getLoanId(),
GetLoansLoanIdResponse body = loanDetails.body(); Map.of("staffInSelectedOfficeOnly", false, "associations", "", "exclude", "", "fields", "")));
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();
eventAssertion.assertEventRaised(LoanUndoApprovalEvent.class, body.getId()); eventAssertion.assertEventRaised(LoanUndoApprovalEvent.class, body.getId());
} }
public void loanRejectedEventCheck(Response<PostLoansLoanIdResponse> loanRejectedResponse) throws IOException { public void loanRejectedEventCheck(PostLoansLoanIdResponse loanRejectedResponse) {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanRejectedResponse.body().getLoanId(), false, "", "", "") waitForTransactionCommit();
.execute(); GetLoansLoanIdResponse body = ok(() -> fineractClient.loans().retrieveLoan(loanRejectedResponse.getLoanId(),
GetLoansLoanIdResponse body = loanDetails.body(); Map.of("staffInSelectedOfficeOnly", false, "associations", "", "exclude", "", "fields", "")));
eventAssertion.assertEventRaised(LoanRejectedEvent.class, body.getId()); eventAssertion.assertEventRaised(LoanRejectedEvent.class, body.getId());
} }
public void disburseLoanEventCheck(Long loanId) throws IOException { public void disburseLoanEventCheck(Long loanId) {
waitForTransactionCommit();
loanAccountDataV1Check(LoanDisbursalEvent.class, loanId); loanAccountDataV1Check(LoanDisbursalEvent.class, loanId);
} }
public void loanBalanceChangedEventCheck(Long loanId) throws IOException { public void loanBalanceChangedEventCheck(Long loanId) {
waitForTransactionCommit();
loanAccountDataV1Check(LoanBalanceChangedEvent.class, loanId); loanAccountDataV1Check(LoanBalanceChangedEvent.class, loanId);
} }
public void loanStatusChangedEventCheck(Long loanId) throws IOException { public void loanStatusChangedEventCheck(Long loanId) {
waitForTransactionCommit();
loanAccountDataV1Check(LoanStatusChangedEvent.class, loanId); loanAccountDataV1Check(LoanStatusChangedEvent.class, loanId);
} }
private void loanAccountDataV1Check(Class<? extends AbstractLoanEvent> eventClazz, Long loanId) throws IOException { private void loanAccountDataV1Check(Class<? extends AbstractLoanEvent> eventClazz, Long loanId) {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", "", "").execute(); GetLoansLoanIdResponse body = ok(() -> fineractClient.loans().retrieveLoan(loanId,
GetLoansLoanIdResponse body = loanDetails.body(); Map.of("staffInSelectedOfficeOnly", false, "associations", "all", "exclude", "", "fields", "")));
eventAssertion.assertEvent(eventClazz, loanId)// eventAssertion.assertEvent(eventClazz, loanId)//
.extractingData(loanAccountDataV1 -> { .extractingData(loanAccountDataV1 -> {
@@ -254,10 +234,10 @@ public class EventCheckHelper {
return targetTransaction; return targetTransaction;
} }
public GetLoansLoanIdTransactions findNthTransaction(String nthItemStr, String transactionType, String transactionDate, long loanId) public GetLoansLoanIdTransactions findNthTransaction(String nthItemStr, String transactionType, String transactionDate, long loanId) {
throws IOException { GetLoansLoanIdResponse loanResponse = ok(() -> fineractClient.loans().retrieveLoan(loanId,
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body() Map.of("staffInSelectedOfficeOnly", false, "associations", "transactions", "exclude", "", "fields", "")));
.getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanResponse.getTransactions();
GetLoansLoanIdTransactions targetTransaction = getNthTransactionType(nthItemStr, transactionType, transactionDate, transactions); GetLoansLoanIdTransactions targetTransaction = getNthTransactionType(nthItemStr, transactionType, transactionDate, transactions);
return targetTransaction; return targetTransaction;
} }
@@ -276,6 +256,7 @@ public class EventCheckHelper {
} }
public void loanUndoContractTerminationEventCheck(final GetLoansLoanIdTransactions transaction) { public void loanUndoContractTerminationEventCheck(final GetLoansLoanIdTransactions transaction) {
waitForTransactionCommit();
eventAssertion.assertEventRaised(LoanUndoContractTerminationBusinessEvent.class, transaction.getId()); eventAssertion.assertEventRaised(LoanUndoContractTerminationBusinessEvent.class, transaction.getId());
} }
@@ -286,12 +267,12 @@ public class EventCheckHelper {
return actual.compareTo(expected) == 0; return actual.compareTo(expected) == 0;
} }
public void loanDisbursalTransactionEventCheck(Response<PostLoansLoanIdResponse> loanDisburseResponse) throws IOException { public void loanDisbursalTransactionEventCheck(PostLoansLoanIdResponse loanDisburseResponse) {
Long disbursementTransactionId = loanDisburseResponse.body().getSubResourceId(); waitForTransactionCommit();
Long disbursementTransactionId = loanDisburseResponse.getSubResourceId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi GetLoansLoanIdResponse body = ok(() -> fineractClient.loans().retrieveLoan(loanDisburseResponse.getLoanId(),
.retrieveLoan(loanDisburseResponse.body().getLoanId(), false, "transactions", "", "").execute(); Map.of("staffInSelectedOfficeOnly", false, "associations", "transactions", "exclude", "", "fields", "")));
GetLoansLoanIdResponse body = loanDetails.body();
List<GetLoansLoanIdTransactions> transactions = body.getTransactions(); List<GetLoansLoanIdTransactions> transactions = body.getTransactions();
GetLoansLoanIdTransactions disbursementTransaction = transactions// GetLoansLoanIdTransactions disbursementTransaction = transactions//
.stream()// .stream()//
@@ -306,12 +287,12 @@ public class EventCheckHelper {
} }
public EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> transactionEventCheck( public EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> transactionEventCheck(
Response<PostLoansLoanIdTransactionsResponse> transactionResponse, TransactionType transactionType, String externalOwnerId) PostLoansLoanIdTransactionsResponse transactionResponse, TransactionType transactionType, String externalOwnerId) {
throws IOException { Long loanId = transactionResponse.getLoanId();
Long loanId = transactionResponse.body().getLoanId(); Long transactionId = transactionResponse.getResourceId();
Long transactionId = transactionResponse.body().getResourceId(); GetLoansLoanIdResponse loanDetailsResponse = ok(() -> fineractClient.loans().retrieveLoan(loanId,
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute(); Map.of("staffInSelectedOfficeOnly", false, "associations", "transactions", "exclude", "", "fields", "")));
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
GetLoansLoanIdTransactions transactionFound = transactions// GetLoansLoanIdTransactions transactionFound = transactions//
.stream()// .stream()//
.filter(t -> t.getId().equals(transactionId))// .filter(t -> t.getId().equals(transactionId))//
@@ -330,16 +311,17 @@ public class EventCheckHelper {
}; };
EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> eventBuilder = eventAssertion.assertEvent(eventClass, transactionId); 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()))// .extractingData(LoanTransactionDataV1::getDate).isEqualTo(FORMATTER_EVENTS.format(transactionFound.getDate()))//
.extractingBigDecimal(LoanTransactionDataV1::getAmount).isEqualTo(transactionFound.getAmount())// .extractingBigDecimal(LoanTransactionDataV1::getAmount).isEqualTo(transactionFound.getAmount())//
.extractingData(LoanTransactionDataV1::getExternalOwnerId).isEqualTo(externalOwnerId);// .extractingData(LoanTransactionDataV1::getExternalOwnerId).isEqualTo(externalOwnerId);//
return eventBuilder; return eventBuilder;
} }
public void loanOwnershipTransferBusinessEventCheck(Long loanId, Long transferId) throws IOException { public void loanOwnershipTransferBusinessEventCheck(Long loanId, Long transferId) {
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute(); waitForTransactionCommit();
List<ExternalTransferData> content = response.body().getContent(); 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) ExternalTransferData filtered = content.stream().filter(t -> transferId.equals(t.getTransferId())).reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException("No element found")); .orElseThrow(() -> new IllegalStateException("No element found"));
@@ -368,9 +350,9 @@ public class EventCheckHelper {
} }
public void loanOwnershipTransferBusinessEventWithStatusCheck(Long loanId, Long transferId, String transferStatus, public void loanOwnershipTransferBusinessEventWithStatusCheck(Long loanId, Long transferId, String transferStatus,
String transferStatusReason) throws IOException { String transferStatusReason) {
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute(); PageExternalTransferData response = ok(() -> fineractClient.externalAssetOwners().getTransfers(Map.of("loanId", loanId)));
List<ExternalTransferData> content = response.body().getContent(); List<ExternalTransferData> content = response.getContent();
ExternalTransferData filtered = content.stream().filter(t -> transferId.equals(t.getTransferId())).reduce((first, second) -> second) ExternalTransferData filtered = content.stream().filter(t -> transferId.equals(t.getTransferId())).reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException("No element found")); .orElseThrow(() -> new IllegalStateException("No element found"));
@@ -414,9 +396,9 @@ public class EventCheckHelper {
} }
public void loanOwnershipTransferBusinessEventWithTypeCheck(Long loanId, ExternalTransferData transferData, String transferType, public void loanOwnershipTransferBusinessEventWithTypeCheck(Long loanId, ExternalTransferData transferData, String transferType,
String previousAssetOwner) throws IOException { String previousAssetOwner) {
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute(); PageExternalTransferData response = ok(() -> fineractClient.externalAssetOwners().getTransfers(Map.of("loanId", loanId)));
List<ExternalTransferData> content = response.body().getContent(); List<ExternalTransferData> content = response.getContent();
Long transferId = transferData.getTransferId(); Long transferId = transferData.getTransferId();
String assetOwner = transferData.getOwner() == null ? null : transferData.getOwner().getExternalId(); String assetOwner = transferData.getOwner() == null ? null : transferData.getOwner().getExternalId();
@@ -454,9 +436,10 @@ public class EventCheckHelper {
.extractingData(LoanOwnershipTransferDataV1::getPreviousOwnerExternalId).isEqualTo(previousAssetOwner); .extractingData(LoanOwnershipTransferDataV1::getPreviousOwnerExternalId).isEqualTo(previousAssetOwner);
} }
public void loanAccountSnapshotBusinessEventCheck(Long loanId, Long transferId) throws IOException { public void loanAccountSnapshotBusinessEventCheck(Long loanId, Long transferId) {
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute(); waitForTransactionCommit();
List<ExternalTransferData> content = response.body().getContent(); 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) ExternalTransferData filtered = content.stream().filter(t -> transferId.equals(t.getTransferId())).reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException("No element found")); .orElseThrow(() -> new IllegalStateException("No element found"));
@@ -467,9 +450,10 @@ public class EventCheckHelper {
GlobalConfigurationPropertyData outstandingInterestStrategy = configurationHelper GlobalConfigurationPropertyData outstandingInterestStrategy = configurationHelper
.getGlobalConfiguration("outstanding-interest-calculation-strategy-for-external-asset-transfer"); .getGlobalConfiguration("outstanding-interest-calculation-strategy-for-external-asset-transfer");
if ("PAYABLE_OUTSTANDING_INTEREST".equals(outstandingInterestStrategy.getStringValue())) { if ("PAYABLE_OUTSTANDING_INTEREST".equals(outstandingInterestStrategy.getStringValue())) {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", null, null).execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId,
totalOutstandingBalanceAmountExpected = zeroConversion(loanDetails.body().getSummary().getTotalOutstanding()); Map.of("staffInSelectedOfficeOnly", false, "associations", "all", "exclude", "", "fields", "")));
outstandingInterestPortionExpected = zeroConversion(loanDetails.body().getSummary().getInterestOutstanding()); totalOutstandingBalanceAmountExpected = zeroConversion(loanDetails.getSummary().getTotalOutstanding());
outstandingInterestPortionExpected = zeroConversion(loanDetails.getSummary().getInterestOutstanding());
} }
String ownerExternalIdExpected = filtered.getStatus().getValue().equals("BUYBACK") ? null : filtered.getOwner().getExternalId(); String ownerExternalIdExpected = filtered.getStatus().getValue().equals("BUYBACK") ? null : filtered.getOwner().getExternalId();
@@ -498,10 +482,11 @@ public class EventCheckHelper {
.isEqualTo(overPaymentPortionExpected); .isEqualTo(overPaymentPortionExpected);
} }
public void loanAccountDelinquencyPauseChangedBusinessEventCheck(Long loanId) throws IOException { public void loanAccountDelinquencyPauseChangedBusinessEventCheck(Long loanId) {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", "", "").execute(); waitForTransactionCommit();
List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriodsActual = loanDetails.body().getDelinquent() GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId,
.getDelinquencyPausePeriods(); Map.of("staffInSelectedOfficeOnly", false, "associations", "all", "exclude", "", "fields", "")));
List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriodsActual = loanDetails.getDelinquent().getDelinquencyPausePeriods();
eventAssertion.assertEvent(LoanDelinquencyPauseChangedEvent.class, loanId)// eventAssertion.assertEvent(LoanDelinquencyPauseChangedEvent.class, loanId)//
.extractingData(LoanAccountDataV1::getId).isEqualTo(loanId)// .extractingData(LoanAccountDataV1::getId).isEqualTo(loanId)//
@@ -539,6 +524,7 @@ public class EventCheckHelper {
} }
public void installmentLevelDelinquencyRangeChangeEventCheck(Long loanId) { public void installmentLevelDelinquencyRangeChangeEventCheck(Long loanId) {
waitForTransactionCommit();
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId).extractingData(loanAccountDelinquencyRangeDataV1 -> { eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId).extractingData(loanAccountDelinquencyRangeDataV1 -> {
// check if sum of total amounts equal the sum of amount types in installmentDelinquencyBuckets // check if sum of total amounts equal the sum of amount types in installmentDelinquencyBuckets
BigDecimal totalAmountSum = loanAccountDelinquencyRangeDataV1.getInstallmentDelinquencyBuckets().stream()// BigDecimal totalAmountSum = loanAccountDelinquencyRangeDataV1.getInstallmentDelinquencyBuckets().stream()//
@@ -582,4 +568,36 @@ public class EventCheckHelper {
private BigDecimal zeroConversion(BigDecimal input) { private BigDecimal zeroConversion(BigDecimal input) {
return input.compareTo(new BigDecimal("0.000000")) == 0 ? new BigDecimal(input.toEngineeringString()) : input.setScale(8); 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; 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) { public void receiveMessage(ActiveMQBytesMessage message) {
try { try {
byte[] buffer = new byte[(int) message.getBodyLength()]; byte[] buffer = new byte[(int) message.getBodyLength()];
@@ -18,23 +18,24 @@
*/ */
package org.apache.fineract.test.service; 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 static org.awaitility.Awaitility.await;
import java.io.IOException;
import java.time.Duration; import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils; 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.ExecuteJobRequest;
import org.apache.fineract.client.models.GetJobsResponse; 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.Job;
import org.apache.fineract.test.data.job.JobResolver; 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.apache.fineract.test.messaging.config.JobPollingProperties;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -42,7 +43,7 @@ import retrofit2.Response;
public class JobService { public class JobService {
@Autowired @Autowired
private SchedulerJobApi schedulerJobApi; private FineractFeignClient fineractClient;
@Autowired @Autowired
private JobPollingProperties jobPollingProperties; private JobPollingProperties jobPollingProperties;
@@ -50,13 +51,10 @@ public class JobService {
private final JobResolver jobResolver; private final JobResolver jobResolver;
public void execute(Job job) { public void execute(Job job) {
try { Long jobId = jobResolver.resolve(job);
Long jobId = jobResolver.resolve(job); Map<String, Object> queryParams = new HashMap<>();
Response<Void> response = schedulerJobApi.executeJob(jobId, "executeJob", new ExecuteJobRequest()).execute(); queryParams.put("command", "executeJob");
ErrorHelper.checkSuccessfulApiCall(response); executeVoid(() -> fineractClient.schedulerJob().executeJob(jobId, new ExecuteJobRequest(), queryParams));
} catch (IOException e) {
throw new RuntimeException("Exception while executing job %s".formatted(job.getName()), e);
}
} }
public void executeAndWait(Job job) { public void executeAndWait(Job job) {
@@ -73,9 +71,8 @@ public class JobService {
.until(() -> { .until(() -> {
log.debug("Waiting for job {} to finish", jobName); log.debug("Waiting for job {} to finish", jobName);
Long jobId = jobResolver.resolve(job); Long jobId = jobResolver.resolve(job);
Response<GetJobsResponse> getJobsResponse = schedulerJobApi.retrieveOne5(jobId).execute(); GetJobsResponse getJobsResponse = ok(() -> fineractClient.schedulerJob().retrieveOne5(jobId));
ErrorHelper.checkSuccessfulApiCall(getJobsResponse); Boolean currentlyRunning = getJobsResponse.getCurrentlyRunning();
Boolean currentlyRunning = getJobsResponse.body().getCurrentlyRunning();
return BooleanUtils.isFalse(currentlyRunning); return BooleanUtils.isFalse(currentlyRunning);
}); });
} }
@@ -36,9 +36,9 @@
*/ */
package org.apache.fineract.test.stepdef.assetexternalization; 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 static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable; import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
@@ -46,10 +46,17 @@ import java.io.IOException;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j; 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.ExternalAssetOwnerRequest;
import org.apache.fineract.client.models.ExternalOwnerJournalEntryData; import org.apache.fineract.client.models.ExternalOwnerJournalEntryData;
import org.apache.fineract.client.models.ExternalOwnerTransferJournalEntryData; 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.PostInitiateTransferResponse;
import org.apache.fineract.client.models.PostLoansResponse; import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PutExternalAssetOwnerLoanProductAttributeRequest; 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.data.AssetExternalizationErrorMessage;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper; 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.helper.Utils;
import org.apache.fineract.test.messaging.EventAssertion; import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.EventCheckHelper; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j @Slf4j
public class AssetExternalizationStepDef extends AbstractStepDef { 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_BUYBACK = "buyback";
public static final String TRANSACTION_TYPE_INTERMEDIARY_SALE = "intermediarySale"; public static final String TRANSACTION_TYPE_INTERMEDIARY_SALE = "intermediarySale";
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT_ASSET_EXT); public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT_ASSET_EXT);
private static final Gson GSON = new JSON().getGson();
@Autowired @Autowired
private ExternalAssetOwnersApi externalAssetOwnersApi; private FineractFeignClient fineractFeignClient;
@Autowired @Autowired
private EventCheckHelper eventCheckHelper; private EventCheckHelper eventCheckHelper;
@@ -102,6 +100,22 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
@Autowired @Autowired
private EventAssertion eventAssertion; 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:") @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 { public void createAssetExternalizationRequestByLoanIdUserGeneratedExtId(DataTable table) throws IOException {
// if user created transferExternalId previously, it will use that, otherwise create a new one // 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<List<String>> data = table.asLists();
List<String> transferData = data.get(1); List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest(); ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest();
if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) { if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) {
@@ -138,12 +152,11 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)// .dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi PostInitiateTransferResponse response = externalAssetOwnersApi().transferRequestWithLoanId(loanId, request, transferData.get(0),
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute(); Collections.emptyMap());
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response); testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE, testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId()); response.getResourceExternalId());
ErrorHelper.checkSuccessfulApiCall(response);
} else if ((transferData.get(0).equals(TRANSACTION_TYPE_SALE) || transferData.get(0).equals(TRANSACTION_TYPE_INTERMEDIARY_SALE))) { } else if ((transferData.get(0).equals(TRANSACTION_TYPE_SALE) || transferData.get(0).equals(TRANSACTION_TYPE_INTERMEDIARY_SALE))) {
String ownerExternalId; String ownerExternalId;
@@ -160,21 +173,18 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)// .dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi PostInitiateTransferResponse response = externalAssetOwnersApi().transferRequestWithLoanId(loanId, request,
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute(); Map.of("command", transferData.get(0)));
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response); testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_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); testContext().set(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID, ownerExternalId);
if (transferData.get(0).equals(TRANSACTION_TYPE_INTERMEDIARY_SALE)) { if (transferData.get(0).equals(TRANSACTION_TYPE_INTERMEDIARY_SALE)) {
assertThat(ownerExternalId).isNotNull(); assertThat(ownerExternalId).isNotNull();
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_PREVIOUS_OWNER_EXTERNAL_ID, ownerExternalId); testContext().set(TestContextKey.ASSET_EXTERNALIZATION_PREVIOUS_OWNER_EXTERNAL_ID, ownerExternalId);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_INTERMEDIARY_SALE_TRANSFER_EXTERNAL_ID_FROM_RESPONSE, testContext().set(TestContextKey.ASSET_EXTERNALIZATION_INTERMEDIARY_SALE_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId()); response.getResourceExternalId());
} }
ErrorHelper.checkSuccessfulApiCall(response);
} else { } else {
throw new IllegalStateException(String.format("%s is not supported Asset externalization transaction", transferData.get(0))); 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") @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 { public void createAssetExternalizationBuybackRequestOwnerNullByLoanIdSystemGeneratedExtId(String settlementDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest()// ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest()//
.settlementDate(settlementDate)// .settlementDate(settlementDate)//
@@ -192,12 +202,11 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)// .dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi PostInitiateTransferResponse response = externalAssetOwnersApi().transferRequestWithLoanId(loanId, request,
.transferRequestWithLoanId(loanId, request, TRANSACTION_TYPE_BUYBACK).execute(); Map.of("command", TRANSACTION_TYPE_BUYBACK));
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response); testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE, testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId()); response.getResourceExternalId());
ErrorHelper.checkSuccessfulApiCall(response);
} }
@When("Admin makes asset externalization request by Loan external ID with unique ownerExternalId, user-generated transferExternalId and the following data:") @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<List<String>> data = table.asLists();
List<String> transferData = data.get(1); List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId(); String loanExternalId = loanResponse.getResourceExternalId();
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest(); ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest();
if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) { if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) {
@@ -231,13 +240,11 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)// .dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi PostInitiateTransferResponse response = externalAssetOwnersApi().transferRequestWithLoanExternalId(loanExternalId, request,
.transferRequestWithLoanExternalId(loanExternalId, request, transferData.get(0)).execute(); Map.of("command", transferData.get(0)));
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response); testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE, testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId()); response.getResourceExternalId());
ErrorHelper.checkSuccessfulApiCall(response);
} else if (transferData.get(0).equals(TRANSACTION_TYPE_SALE)) { } else if (transferData.get(0).equals(TRANSACTION_TYPE_SALE)) {
String ownerExternalId = Utils.randomNameGenerator(OWNER_EXTERNAL_ID_PREFIX, 3); String ownerExternalId = Utils.randomNameGenerator(OWNER_EXTERNAL_ID_PREFIX, 3);
@@ -248,14 +255,12 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)// .dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi PostInitiateTransferResponse response = externalAssetOwnersApi().transferRequestWithLoanExternalId(loanExternalId, request,
.transferRequestWithLoanExternalId(loanExternalId, request, transferData.get(0)).execute(); Map.of("command", transferData.get(0)));
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response); testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_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); testContext().set(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID, ownerExternalId);
ErrorHelper.checkSuccessfulApiCall(response);
} else { } else {
throw new IllegalStateException(String.format("%s is not supported Asset externalization transaction", transferData.get(0))); 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); String transferExternalIdExpected = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostInitiateTransferResponse> response = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE); PostInitiateTransferResponse response = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
PostInitiateTransferResponse body = response.body(); Long loanIdActual = response.getSubResourceId();
Long loanIdActual = body.getSubResourceId(); String transferExternalIdActual = response.getResourceExternalId();
String transferExternalIdActual = body.getResourceExternalId();
log.debug("loanId: {}", loanId); log.debug("loanId: {}", loanId);
log.debug("ownerExternalIdStored: {}", ownerExternalIdStored); log.debug("ownerExternalIdStored: {}", ownerExternalIdStored);
@@ -281,7 +285,7 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
log.debug("transferExternalIdActual: {}", transferExternalIdActual); log.debug("transferExternalIdActual: {}", transferExternalIdActual);
assertThat(loanIdActual).as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(loanIdActual, loanId)).isEqualTo(loanId); assertThat(loanIdActual).as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(loanIdActual, loanId)).isEqualTo(loanId);
assertThat(body.getResourceId()).isNotNull(); assertThat(response.getResourceId()).isNotNull();
if (transferExternalIdExpected != null) { if (transferExternalIdExpected != null) {
assertThat(transferExternalIdActual) assertThat(transferExternalIdActual)
.as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(transferExternalIdActual, transferExternalIdExpected)) .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:") @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 { public void checkAssetExternalizationDetailsByLoanId(int numberOfElements, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute(); PageExternalTransferData response = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
ErrorHelper.checkSuccessfulApiCall(response);
checkExternalAssetDetails(loanId, null, response, numberOfElements, table); 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:") @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 { public void checkAssetExternalizationDetailsByLoanExternalId(int numberOfElements, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId(); String loanExternalId = loanResponse.getResourceExternalId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, null, loanExternalId, null, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);
PageExternalTransferData response = externalAssetOwnersApi().getTransfers(Map.of("loanExternalId", loanExternalId));
checkExternalAssetDetails(null, loanExternalId, response, numberOfElements, table); checkExternalAssetDetails(null, loanExternalId, response, numberOfElements, table);
} }
@@ -317,24 +318,21 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
public void checkAssetExternalizationDetailsByTransferExternalId(int numberOfElements, DataTable table) throws IOException { public void checkAssetExternalizationDetailsByTransferExternalId(int numberOfElements, DataTable table) throws IOException {
String transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE); String transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(transferExternalId, null, null, null, null) PageExternalTransferData response = externalAssetOwnersApi().getTransfers(Map.of("transferExternalId", transferExternalId),
.execute(); Map.of());
ErrorHelper.checkSuccessfulApiCall(response);
checkExternalAssetDetails(null, null, response, numberOfElements, table); checkExternalAssetDetails(null, null, response, numberOfElements, table);
} }
@Then("Asset externalization details has the generated transferExternalId") @Then("Asset externalization details has the generated transferExternalId")
public void checkGeneratedTransferExternalId() throws IOException { public void checkGeneratedTransferExternalId() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostInitiateTransferResponse> assetExtResponse = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE); PostInitiateTransferResponse assetExtResponse = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
String transferExternalIdExpected = assetExtResponse.body().getResourceExternalId(); String transferExternalIdExpected = assetExtResponse.getResourceExternalId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute(); PageExternalTransferData response = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
ErrorHelper.checkSuccessfulApiCall(response); List<ExternalTransferData> content = response.getContent();
List<ExternalTransferData> content = response.body().getContent();
content.forEach(e -> { content.forEach(e -> {
assertThat(e.getTransferExternalId()).as(ErrorMessageHelper assertThat(e.getTransferExternalId()).as(ErrorMessageHelper
@@ -343,11 +341,10 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
}); });
} }
private void checkExternalAssetDetails(Long loanId, String loanExternalId, Response<PageExternalTransferData> response, private void checkExternalAssetDetails(Long loanId, String loanExternalId, PageExternalTransferData response, int numberOfElements,
int numberOfElements, DataTable table) { DataTable table) {
PageExternalTransferData body = response.body(); Integer numberOfElementsActual = response.getNumberOfElements();
Integer numberOfElementsActual = body.getNumberOfElements(); List<ExternalTransferData> content = response.getContent();
List<ExternalTransferData> content = body.getContent();
String transferExternalId; String transferExternalId;
String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID); 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<List<String>> data = table.asLists();
List<String> transferData = data.get(1); List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
String transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_FROM_RESPONSE); 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()// ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest()//
.settlementDate(transferData.get(1))// .settlementDate(transferData.get(1))//
.transferExternalId(transferExternalId)// .transferExternalId(transferExternalId)//
.dateFormat(DATE_FORMAT_ASSET_EXT)// .dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi CallFailedRuntimeException exception = fail(
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute(); () -> externalAssetOwnersApi().transferRequestWithLoanId(loanId, request, Map.of("command", transferData.get(0))));
PageExternalTransferData transfers = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute().body(); int errorCodeActual = exception.getStatus();
String settlementDateOriginal = FORMATTER.format(transfers.getContent().get(0).getSettlementDate()); String errorMessageActual = exception.getDeveloperMessage();
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();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected); assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected)) assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected); .contains(errorMessageExpected);
log.debug("ERROR CODE: {}", errorCodeActual); log.debug("ERROR CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual); log.debug("ERROR MESSAGE: {}", errorMessageActual);
@@ -495,8 +490,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
List<String> transferData = data.get(1); List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest(); ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest();
if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) { 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))); 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); AssetExternalizationErrorMessage errorMsgType = AssetExternalizationErrorMessage.valueOf(errorMessageType);
String errorMessageExpected = errorMsgType.getValue(); String errorMessageExpected = errorMsgType.getValue();
String errorToString = response.errorBody().string(); CallFailedRuntimeException exception = fail(
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class); () -> externalAssetOwnersApi().transferRequestWithLoanId(loanId, request, Map.of("command", transferData.get(0))));
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(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected)) if (errorMessageType.equals("INVALID_REQUEST")) {
.isEqualTo(errorMessageExpected); 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 CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual); log.debug("ERROR MESSAGE: {}", errorMessageActual);
@@ -543,8 +541,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
List<String> transferData = data.get(1); List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest()// ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest()//
.settlementDate(transferData.get(0))// .settlementDate(transferData.get(0))//
@@ -554,20 +552,23 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT_ASSET_EXT)// .dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, TRANSACTION_TYPE_SALE).execute();
AssetExternalizationErrorMessage errorMsgType = AssetExternalizationErrorMessage.valueOf(errorMessageType); AssetExternalizationErrorMessage errorMsgType = AssetExternalizationErrorMessage.valueOf(errorMessageType);
String errorMessageExpected = errorMsgType.getValue(); String errorMessageExpected = errorMsgType.getValue();
String errorToString = response.errorBody().string(); CallFailedRuntimeException exception = fail(
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class); () -> externalAssetOwnersApi().transferRequestWithLoanId(loanId, request, Map.of("command", TRANSACTION_TYPE_SALE)));
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(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected)) if (errorMessageType.equals("INVALID_REQUEST")) {
.isEqualTo(errorMessageExpected); 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 CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual); 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:") @Then("The latest asset externalization transaction with {string} status has the following TRANSFER Journal entries:")
public void checkJournalEntriesTransaction(String status, DataTable table) throws IOException { public void checkJournalEntriesTransaction(String status, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Long lastTransferIdByStatus = getLastTransferIdByStatus(loanId, status); Long lastTransferIdByStatus = getLastTransferIdByStatus(loanId, status);
Response<ExternalOwnerTransferJournalEntryData> journalEntriesOfTransfer = externalAssetOwnersApi ExternalOwnerTransferJournalEntryData journalEntriesOfTransfer = externalAssetOwnersApi()
.getJournalEntriesOfTransfer(lastTransferIdByStatus, null, null).execute(); .getJournalEntriesOfTransfer(lastTransferIdByStatus, Map.of());
List<JournalEntryData> content = journalEntriesOfTransfer.body().getJournalEntryData().getContent(); List<JournalEntryData> content = journalEntriesOfTransfer.getJournalEntryData().getContent();
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
int linesExpected = data.size() - 1; int linesExpected = data.size() - 1;
@@ -603,22 +604,21 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
assertThat(containsExpectedValues) assertThat(containsExpectedValues)
.as(ErrorMessageHelper.wrongValueInLineInAssetExternalizationJournalEntry(i, actualValuesList, expectedValues)) .as(ErrorMessageHelper.wrongValueInLineInAssetExternalizationJournalEntry(i, actualValuesList, expectedValues))
.isTrue(); .isTrue();
int linesActual = journalEntriesOfTransfer.body().getJournalEntryData().getNumberOfElements();
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInAssetExternalizationJournalEntry(linesActual, linesExpected))
.isEqualTo(linesExpected);
} }
log.debug("loanId: {}", journalEntriesOfTransfer.body().getTransferData().getLoan().getLoanId()); int linesActual = journalEntriesOfTransfer.getJournalEntryData().getNumberOfElements();
log.debug("ownerExternalId: {}", journalEntriesOfTransfer.body().getTransferData().getOwner().getExternalId()); 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("transferId: {}", lastTransferIdByStatus);
log.debug("transferExternalId: {}", journalEntriesOfTransfer.body().getTransferData().getTransferExternalId()); log.debug("transferExternalId: {}", journalEntriesOfTransfer.getTransferData().getTransferExternalId());
} }
private Long getLastTransferIdByStatus(Long loanId, String status) throws IOException { private Long getLastTransferIdByStatus(Long loanId, String status) throws IOException {
Response<PageExternalTransferData> transfersResponse = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null) PageExternalTransferData transfersResponse = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
.execute(); List<ExternalTransferData> content = transfersResponse.getContent();
List<ExternalTransferData> content = transfersResponse.body().getContent();
ExternalTransferData result = content.stream().filter(t -> status.equals(t.getStatus().getValue())) ExternalTransferData result = content.stream().filter(t -> status.equals(t.getStatus().getValue()))
.reduce((first, second) -> second) .reduce((first, second) -> second)
@@ -628,11 +628,10 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
} }
private Long getLastTransferId(Long loanId) throws IOException { private Long getLastTransferId(Long loanId) throws IOException {
Response<PageExternalTransferData> transfersResponse = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null) PageExternalTransferData transfersResponse = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
.execute(); List<ExternalTransferData> content = transfersResponse.getContent();
List<ExternalTransferData> content = transfersResponse.body().getContent();
ExternalTransferData result = content.stream().reduce((first, second) -> second) 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(); return result.getTransferId();
} }
@@ -650,12 +649,11 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
transferExternalId = null; transferExternalId = null;
} }
Response<PageExternalTransferData> transfersResponse = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null) PageExternalTransferData transfersResponse = externalAssetOwnersApi().getTransfers(Map.of("loanId", loanId));
.execute(); List<ExternalTransferData> content = transfersResponse.getContent();
List<ExternalTransferData> content = transfersResponse.body().getContent();
ExternalTransferData result = content.stream().filter(bizEvent -> bizEvent.getTransferExternalId().equals(transferExternalId)) ExternalTransferData result = content.stream().filter(bizEvent -> bizEvent.getTransferExternalId().equals(transferExternalId))
.toList().stream().reduce((first, second) -> second) .toList().stream().reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException("transfersResponse.body().getContent() is empty")); .orElseThrow(() -> new IllegalStateException("transfersResponse.getContent() is empty"));
return result; return result;
} }
@@ -664,9 +662,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
public void checkJournalEntriesOwner(DataTable table) throws IOException { public void checkJournalEntriesOwner(DataTable table) throws IOException {
String ownerExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID); String ownerExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
Response<ExternalOwnerJournalEntryData> journalEntriesOfOwner = externalAssetOwnersApi ExternalOwnerJournalEntryData journalEntriesOfOwner = externalAssetOwnersApi().getJournalEntriesOfOwner(ownerExternalId, Map.of());
.getJournalEntriesOfOwner(ownerExternalId, null, null).execute(); List<JournalEntryData> content = journalEntriesOfOwner.getJournalEntryData().getContent();
List<JournalEntryData> content = journalEntriesOfOwner.body().getJournalEntryData().getContent();
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
int linesExpected = data.size() - 1; int linesExpected = data.size() - 1;
@@ -687,19 +684,19 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
assertThat(containsExpectedValues) assertThat(containsExpectedValues)
.as(ErrorMessageHelper.wrongValueInLineInAssetExternalizationJournalEntry(i, actualValuesList, expectedValues)) .as(ErrorMessageHelper.wrongValueInLineInAssetExternalizationJournalEntry(i, actualValuesList, expectedValues))
.isTrue(); .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") @Then("LoanOwnershipTransferBusinessEvent is created")
public void loanOwnershipTransferBusinessEventCheck() throws IOException { public void loanOwnershipTransferBusinessEventCheck() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Long transferId = getLastTransferId(loanId); Long transferId = getLastTransferId(loanId);
eventCheckHelper.loanOwnershipTransferBusinessEventCheck(loanId, transferId); 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") @Then("LoanOwnershipTransferBusinessEvent with transfer status: {string} and transfer status reason {string} is created")
public void loanOwnershipTransferBusinessEventCheck(String transferStatus, String transferStatusReason) throws IOException { public void loanOwnershipTransferBusinessEventCheck(String transferStatus, String transferStatusReason) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Long transferId = getLastTransferId(loanId); Long transferId = getLastTransferId(loanId);
eventCheckHelper.loanOwnershipTransferBusinessEventWithStatusCheck(loanId, transferId, transferStatus, transferStatusReason); 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") @Then("LoanOwnershipTransferBusinessEvent with transfer type: {string} and transfer asset owner is created")
public void loanOwnershipTransferBusinessEventCheck(String transferType) throws IOException { public void loanOwnershipTransferBusinessEventCheck(String transferType) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
ExternalTransferData transferData = getLastTransferByTransferType(loanId, transferType); ExternalTransferData transferData = getLastTransferByTransferType(loanId, transferType);
String previousAssetOwner = getPreviousAssetOwner(transferData, transferType, false); 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") @Then("LoanOwnershipTransferBusinessEvent with transfer type: {string} and transfer asset owner based on intermediarySale is created")
public void loanOwnershipTransferBusinessEventCheckBasedOnIntermediarySale(String transferType) throws IOException { public void loanOwnershipTransferBusinessEventCheckBasedOnIntermediarySale(String transferType) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
ExternalTransferData transferData = getLastTransferByTransferType(loanId, transferType); ExternalTransferData transferData = getLastTransferByTransferType(loanId, transferType);
String previousAssetOwner = getPreviousAssetOwner(transferData, transferType, true); String previousAssetOwner = getPreviousAssetOwner(transferData, transferType, true);
@@ -758,8 +755,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
@Then("LoanAccountSnapshotBusinessEvent is created") @Then("LoanAccountSnapshotBusinessEvent is created")
public void loanAccountSnapshotBusinessEventCheck() throws IOException { public void loanAccountSnapshotBusinessEventCheck() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Long transferId = getLastTransferId(loanId); Long transferId = getLastTransferId(loanId);
eventCheckHelper.loanAccountSnapshotBusinessEventCheck(loanId, transferId); eventCheckHelper.loanAccountSnapshotBusinessEventCheck(loanId, transferId);
@@ -771,13 +768,12 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
String transferExternalIdExpected = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_PREFIX + "_" + type); String transferExternalIdExpected = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_PREFIX + "_" + type);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostInitiateTransferResponse> response = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE); PostInitiateTransferResponse response = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
PostInitiateTransferResponse body = response.body(); Long loanIdActual = response.getSubResourceId();
Long loanIdActual = body.getSubResourceId(); String transferExternalIdActual = response.getResourceExternalId();
String transferExternalIdActual = body.getResourceExternalId();
log.debug("loanId: {}", loanId); log.debug("loanId: {}", loanId);
log.debug("ownerExternalIdStored: {}", ownerExternalIdStored); log.debug("ownerExternalIdStored: {}", ownerExternalIdStored);
@@ -785,7 +781,7 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
log.debug("transferExternalIdActual: {}", transferExternalIdActual); log.debug("transferExternalIdActual: {}", transferExternalIdActual);
assertThat(loanIdActual).as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(loanIdActual, loanId)).isEqualTo(loanId); assertThat(loanIdActual).as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(loanIdActual, loanId)).isEqualTo(loanId);
assertThat(body.getResourceId()).isNotNull(); assertThat(response.getResourceId()).isNotNull();
if (transferExternalIdExpected != null) { if (transferExternalIdExpected != null) {
assertThat(transferExternalIdActual) assertThat(transferExternalIdActual)
.as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(transferExternalIdActual, transferExternalIdExpected)) .as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(transferExternalIdActual, transferExternalIdExpected))
@@ -812,37 +808,39 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
public void adminTransactionCommandTheWithType(String command, String type) throws IOException { public void adminTransactionCommandTheWithType(String command, String type) throws IOException {
String transferExternalId = testContext() String transferExternalId = testContext()
.get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type); .get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type);
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command)
.execute(); ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest().dateFormat(DATE_FORMAT_ASSET_EXT).locale(DEFAULT_LOCALE);
ErrorHelper.checkSuccessfulApiCall(response);
externalAssetOwnersApiExtension().transferRequestWithId1WithBody(transferExternalId, request, Map.of("command", command));
} }
@When("Admin send {string} command to the transaction type {string} will throw error") @When("Admin send {string} command to the transaction type {string} will throw error")
public void adminTransactionCommandTheWithTypeThrowError(String command, String type) throws IOException { public void adminTransactionCommandTheWithTypeThrowError(String command, String type) throws IOException {
String transferExternalId = testContext() String transferExternalId = testContext()
.get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type); .get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type);
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command)
.execute(); ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest().dateFormat(DATE_FORMAT_ASSET_EXT).locale(DEFAULT_LOCALE);
ErrorHelper.checkFailedApiCall(response, 403);
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:") @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) public void checkAssetExternalizationDetailsByLoanIdIgnoreTransactionExternalId(int numberOfElements, DataTable table)
throws IOException { throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); 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));
checkExternalAssetDetailsIgnoreTransferExternalId(loanId, null, response, numberOfElements, table); checkExternalAssetDetailsIgnoreTransferExternalId(loanId, null, response, numberOfElements, table);
} }
private void checkExternalAssetDetailsIgnoreTransferExternalId(Long loanId, String loanExternalId, private void checkExternalAssetDetailsIgnoreTransferExternalId(Long loanId, String loanExternalId, PageExternalTransferData response,
Response<PageExternalTransferData> response, int numberOfElements, DataTable table) { int numberOfElements, DataTable table) {
PageExternalTransferData body = response.body(); Integer numberOfElementsActual = response.getNumberOfElements();
Integer numberOfElementsActual = body.getNumberOfElements(); List<ExternalTransferData> content = response.getContent();
List<ExternalTransferData> content = body.getContent();
String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID); 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); transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
} }
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command) ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest().dateFormat(DATE_FORMAT_ASSET_EXT).locale(DEFAULT_LOCALE);
.execute();
ErrorHelper.checkFailedApiCall(response, 403); 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") @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); transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
} }
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command) ExternalAssetOwnerRequest request = new ExternalAssetOwnerRequest().dateFormat(DATE_FORMAT_ASSET_EXT).locale(DEFAULT_LOCALE);
.execute();
ErrorHelper.checkSuccessfulApiCall(response); 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}") @When("Admin set external asset owner loan product attribute {string} value {string} for loan product {string}")
public void setAExternalAssetOwnerLoanProductAttribute(String externalAssetOwnerLoanProductAttributeKey, public void setAExternalAssetOwnerLoanProductAttribute(String externalAssetOwnerLoanProductAttributeKey,
String externalAssetOwnerLoanProductAttributeValue, String loanProductName) throws IOException { String externalAssetOwnerLoanProductAttributeValue, String loanProductName) throws IOException {
Response<List<GetLoanProductsResponse>> loanProducts = loanProductsApi.retrieveAllLoanProducts().execute(); List<GetLoanProductsResponse> loanProducts = loanProductsApi().retrieveAllLoanProducts(Map.of());
long loanProductId = loanProducts.body().stream().filter(loanProduct -> loanProduct.getName().equals(loanProductName)).findFirst() long loanProductId = loanProducts.stream().filter(loanProduct -> loanProduct.getName().equals(loanProductName)).findFirst()
.orElseThrow(() -> new RuntimeException("No loan product is found!")).getId(); .orElseThrow(() -> new RuntimeException("No loan product is found!")).getId();
Response<PageExternalTransferLoanProductAttributesData> getExternalAssetOwnerLoanProductAttribute = externalAssetOwnerLoanProductAttributesApi PageExternalTransferLoanProductAttributesData getExternalAssetOwnerLoanProductAttribute = externalAssetOwnerLoanProductAttributesApi()
.getExternalAssetOwnerLoanProductAttributes(loanProductId, externalAssetOwnerLoanProductAttributeKey).execute(); .getExternalAssetOwnerLoanProductAttributes(loanProductId,
PageExternalTransferLoanProductAttributesData getExternalAssetOwnerLoanProductAttributeBody = getExternalAssetOwnerLoanProductAttribute Map.of("attributeKey", externalAssetOwnerLoanProductAttributeKey));
.body();
if (getExternalAssetOwnerLoanProductAttributeBody.getTotalFilteredRecords() == 0) { if (getExternalAssetOwnerLoanProductAttribute.getTotalFilteredRecords() == 0) {
PostExternalAssetOwnerLoanProductAttributeRequest setLoanProductAttributeRequest = new PostExternalAssetOwnerLoanProductAttributeRequest() PostExternalAssetOwnerLoanProductAttributeRequest setLoanProductAttributeRequest = new PostExternalAssetOwnerLoanProductAttributeRequest()
.attributeKey(externalAssetOwnerLoanProductAttributeKey).attributeValue(externalAssetOwnerLoanProductAttributeValue); .attributeKey(externalAssetOwnerLoanProductAttributeKey).attributeValue(externalAssetOwnerLoanProductAttributeValue);
Response<CommandProcessingResult> updateLoanProductAttributerEsponse = externalAssetOwnerLoanProductAttributesApi externalAssetOwnerLoanProductAttributesApi().postExternalAssetOwnerLoanProductAttribute(loanProductId,
.postExternalAssetOwnerLoanProductAttribute(loanProductId, setLoanProductAttributeRequest).execute(); setLoanProductAttributeRequest);
ErrorHelper.checkSuccessfulApiCall(updateLoanProductAttributerEsponse);
} else { } else {
List<ExternalTransferLoanProductAttributesData> attributes = getExternalAssetOwnerLoanProductAttributeBody.getPageItems(); List<ExternalTransferLoanProductAttributesData> attributes = getExternalAssetOwnerLoanProductAttribute.getPageItems();
assert attributes != null; assert attributes != null;
long attributeId = attributes.stream() long attributeId = attributes.stream()
.filter(attribute -> attribute.getAttributeKey().equals(externalAssetOwnerLoanProductAttributeKey)).findFirst() .filter(attribute -> attribute.getAttributeKey().equals(externalAssetOwnerLoanProductAttributeKey)).findFirst()
@@ -941,9 +931,8 @@ public class AssetExternalizationStepDef extends AbstractStepDef {
.getAttributeId(); .getAttributeId();
PutExternalAssetOwnerLoanProductAttributeRequest setLoanProductAttributeRequest = new PutExternalAssetOwnerLoanProductAttributeRequest() PutExternalAssetOwnerLoanProductAttributeRequest setLoanProductAttributeRequest = new PutExternalAssetOwnerLoanProductAttributeRequest()
.attributeKey(externalAssetOwnerLoanProductAttributeKey).attributeValue(externalAssetOwnerLoanProductAttributeValue); .attributeKey(externalAssetOwnerLoanProductAttributeKey).attributeValue(externalAssetOwnerLoanProductAttributeValue);
Response<CommandProcessingResult> updateLoanProductAttributerEsponse = externalAssetOwnerLoanProductAttributesApi externalAssetOwnerLoanProductAttributesApi().updateLoanProductAttribute(loanProductId, attributeId,
.updateLoanProductAttribute(loanProductId, attributeId, setLoanProductAttributeRequest).execute(); setLoanProductAttributeRequest);
ErrorHelper.checkSuccessfulApiCall(updateLoanProductAttributerEsponse);
} }
} }
@@ -18,16 +18,15 @@
*/ */
package org.apache.fineract.test.stepdef.common; 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.apache.fineract.test.stepdef.datatable.DatatablesStepDef.DATATABLE_NAME;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable; import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
@@ -40,16 +39,19 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.fineract.avro.loan.v1.LoanSchedulePeriodDataV1; 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.BatchRequest;
import org.apache.fineract.client.models.BatchResponse; import org.apache.fineract.client.models.BatchResponse;
import org.apache.fineract.client.models.GetClientsClientIdResponse; import org.apache.fineract.client.models.GetClientsClientIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdStatus; import org.apache.fineract.client.models.GetLoansLoanIdStatus;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions; 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.Header;
import org.apache.fineract.client.models.InlineJobRequest;
import org.apache.fineract.client.models.PostClientsRequest; import org.apache.fineract.client.models.PostClientsRequest;
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest; import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest; 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.PostLoansRequest;
import org.apache.fineract.client.models.PostLoansResponse; import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUpdateRescheduleLoansRequest; 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.ChargeProductType;
import org.apache.fineract.test.data.LoanRescheduleErrorMessage; import org.apache.fineract.test.data.LoanRescheduleErrorMessage;
import org.apache.fineract.test.data.LoanStatus; import org.apache.fineract.test.data.LoanStatus;
import org.apache.fineract.test.data.TransactionType; import org.apache.fineract.test.data.TransactionType;
import org.apache.fineract.test.factory.ClientRequestFactory; import org.apache.fineract.test.factory.ClientRequestFactory;
import org.apache.fineract.test.factory.LoanRequestFactory; 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.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse; import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.helper.Utils; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j @Slf4j
public class BatchApiStepDef extends AbstractStepDef { 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 DATE_FORMAT = "dd MMMM yyyy";
private static final String DEFAULT_LOCALE = "en"; private static final String DEFAULT_LOCALE = "en";
private static final Long BATCH_API_SAMPLE_REQUEST_ID_1 = 1L; 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!"; private static final String PWD_USER_WITH_ROLE = "1234567890Aa!";
@Autowired @Autowired
private BatchApiApi batchApiApi; private FineractFeignClient fineractFeignClient;
@Autowired
private LoansApi loansApi;
@Autowired
private ClientApi clientApi;
@Autowired @Autowired
private ClientRequestFactory clientRequestFactory; private ClientRequestFactory clientRequestFactory;
@@ -138,8 +127,17 @@ public class BatchApiStepDef extends AbstractStepDef {
@Autowired @Autowired
private LoanRequestFactory loanRequestFactory; private LoanRequestFactory loanRequestFactory;
@Autowired private BatchApiApi batchApiApi() {
private UsersApi usersApi; return fineractFeignClient.batch();
}
private LoansApi loansApi() {
return fineractFeignClient.loans();
}
private ClientApi clientApi() {
return fineractFeignClient.clients();
}
@When("Batch API sample call ran") @When("Batch API sample call ran")
public void runSampleBatchApiCall() throws IOException { public void runSampleBatchApiCall() throws IOException {
@@ -149,7 +147,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 1 - create client // request 1 - create client
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest(); PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest();
String bodyClientsRequest = GSON.toJson(clientsRequest); String bodyClientsRequest = toJson(clientsRequest);
BatchRequest batchRequest1 = new BatchRequest(); BatchRequest batchRequest1 = new BatchRequest();
batchRequest1.requestId(BATCH_API_SAMPLE_REQUEST_ID_1); batchRequest1.requestId(BATCH_API_SAMPLE_REQUEST_ID_1);
@@ -160,7 +158,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 2 - create Loan // request 2 - create Loan
PostLoansRequest loansRequest = loanRequestFactory.defaultLoansRequest(1L); PostLoansRequest loansRequest = loanRequestFactory.defaultLoansRequest(1L);
String bodyLoansRequest = GSON.toJson(loansRequest); String bodyLoansRequest = toJson(loansRequest);
String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\""); String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\"");
BatchRequest batchRequest2 = new BatchRequest(); BatchRequest batchRequest2 = new BatchRequest();
@@ -181,7 +179,7 @@ public class BatchApiStepDef extends AbstractStepDef {
loanIdChargesRequest.dueDate(dateOfCharge); loanIdChargesRequest.dueDate(dateOfCharge);
loanIdChargesRequest.dateFormat(DATE_FORMAT); loanIdChargesRequest.dateFormat(DATE_FORMAT);
loanIdChargesRequest.locale(DEFAULT_LOCALE); loanIdChargesRequest.locale(DEFAULT_LOCALE);
String bodyLoanIdChargesRequest = GSON.toJson(loanIdChargesRequest); String bodyLoanIdChargesRequest = toJson(loanIdChargesRequest);
BatchRequest batchRequest3 = new BatchRequest(); BatchRequest batchRequest3 = new BatchRequest();
batchRequest3.requestId(BATCH_API_SAMPLE_REQUEST_ID_3); batchRequest3.requestId(BATCH_API_SAMPLE_REQUEST_ID_3);
@@ -205,7 +203,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(batchRequest2); requestList.add(batchRequest2);
requestList.add(batchRequest3); requestList.add(batchRequest3);
requestList.add(batchRequest4); 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); testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
} }
@@ -217,7 +217,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 1 - create client // request 1 - create client
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest(); PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest();
String bodyClientsRequest = GSON.toJson(clientsRequest); String bodyClientsRequest = toJson(clientsRequest);
BatchRequest batchRequest1 = new BatchRequest(); BatchRequest batchRequest1 = new BatchRequest();
batchRequest1.requestId(BATCH_API_SAMPLE_REQUEST_ID_1); batchRequest1.requestId(BATCH_API_SAMPLE_REQUEST_ID_1);
@@ -228,7 +228,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 2 - create Loan // request 2 - create Loan
PostLoansRequest loansRequest = loanRequestFactory.defaultLoansRequest(1L); PostLoansRequest loansRequest = loanRequestFactory.defaultLoansRequest(1L);
String bodyLoansRequest = GSON.toJson(loansRequest); String bodyLoansRequest = toJson(loansRequest);
String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\""); String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\"");
BatchRequest batchRequest2 = new BatchRequest(); BatchRequest batchRequest2 = new BatchRequest();
@@ -241,7 +241,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 3 - approve Loan // request 3 - approve Loan
PostLoansLoanIdRequest loanApproveRequest = LoanRequestFactory.defaultLoanApproveRequest(); PostLoansLoanIdRequest loanApproveRequest = LoanRequestFactory.defaultLoanApproveRequest();
String bodyLoanApproveRequest = GSON.toJson(loanApproveRequest); String bodyLoanApproveRequest = toJson(loanApproveRequest);
BatchRequest batchRequest3 = new BatchRequest(); BatchRequest batchRequest3 = new BatchRequest();
batchRequest3.requestId(BATCH_API_SAMPLE_REQUEST_ID_3); batchRequest3.requestId(BATCH_API_SAMPLE_REQUEST_ID_3);
@@ -253,7 +253,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 4 - disburse Loan // request 4 - disburse Loan
PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest(); PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest();
String bodyLoanDisburseRequest = GSON.toJson(loanDisburseRequest); String bodyLoanDisburseRequest = toJson(loanDisburseRequest);
BatchRequest batchRequest4 = new BatchRequest(); BatchRequest batchRequest4 = new BatchRequest();
batchRequest4.requestId(BATCH_API_SAMPLE_REQUEST_ID_4); batchRequest4.requestId(BATCH_API_SAMPLE_REQUEST_ID_4);
@@ -265,7 +265,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 5 - repayment with idempotency key // request 5 - repayment with idempotency key
PostLoansLoanIdTransactionsRequest loanRepaymentRequest1 = LoanRequestFactory.defaultRepaymentRequest(); PostLoansLoanIdTransactionsRequest loanRepaymentRequest1 = LoanRequestFactory.defaultRepaymentRequest();
String bodyLoanRepaymentRequest1 = GSON.toJson(loanRepaymentRequest1); String bodyLoanRepaymentRequest1 = toJson(loanRepaymentRequest1);
String idempotencyKey = UUID.randomUUID().toString(); String idempotencyKey = UUID.randomUUID().toString();
headers.add(new Header().name("Idempotency-Key").value(idempotencyKey)); 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 // request 6 - repayment with same idempotency key
PostLoansLoanIdTransactionsRequest loanRepaymentRequest2 = LoanRequestFactory.defaultRepaymentRequest(); PostLoansLoanIdTransactionsRequest loanRepaymentRequest2 = LoanRequestFactory.defaultRepaymentRequest();
String bodyLoanRepaymentRequest2 = GSON.toJson(loanRepaymentRequest2); String bodyLoanRepaymentRequest2 = toJson(loanRepaymentRequest2);
BatchRequest batchRequest6 = new BatchRequest(); BatchRequest batchRequest6 = new BatchRequest();
batchRequest6.requestId(BATCH_API_SAMPLE_REQUEST_ID_6); batchRequest6.requestId(BATCH_API_SAMPLE_REQUEST_ID_6);
@@ -297,7 +297,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(batchRequest4); requestList.add(batchRequest4);
requestList.add(batchRequest5); requestList.add(batchRequest5);
requestList.add(batchRequest6); 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); testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
} }
@@ -315,7 +317,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(getLoanDetailsByExternalId(4L, 2L, idempotencyKey)); requestList.add(getLoanDetailsByExternalId(4L, 2L, idempotencyKey));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction); 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_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); 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_CLIENT_EXTERNAL_ID, clientExternalId);
@@ -336,7 +340,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(getLoanDetailsByExternalId(4L, 2L, idempotencyKey)); requestList.add(getLoanDetailsByExternalId(4L, 2L, idempotencyKey));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction); 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_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); 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_CLIENT_EXTERNAL_ID, clientExternalId);
@@ -364,7 +370,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(getLoanDetailsByExternalId(8L, 6L, idempotencyKey2)); requestList.add(getLoanDetailsByExternalId(8L, 6L, idempotencyKey2));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction); 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_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); 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_CLIENT_EXTERNAL_ID, clientExternalId);
@@ -395,7 +403,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(getLoanDetailsByExternalId(8L, 6L, idempotencyKey2)); requestList.add(getLoanDetailsByExternalId(8L, 6L, idempotencyKey2));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction); 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_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
@@ -421,7 +431,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(getLoanDetailsByExternalId(5L, 2L, idempotencyKey)); requestList.add(getLoanDetailsByExternalId(5L, 2L, idempotencyKey));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction); 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_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); 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_CLIENT_EXTERNAL_ID, clientExternalId);
@@ -433,8 +445,8 @@ public class BatchApiStepDef extends AbstractStepDef {
String approvedOnDate, String enclosingTransaction) throws IOException { String approvedOnDate, String enclosingTransaction) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String idempotencyKey = UUID.randomUUID().toString(); String idempotencyKey = UUID.randomUUID().toString();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.body().getLoanId(); Long loanId = loanResponse.getLoanId();
List<BatchRequest> requestList = new ArrayList<>(); List<BatchRequest> requestList = new ArrayList<>();
@@ -442,7 +454,9 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L)); requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction); 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_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
eventAssertion.assertEvent(LoanRescheduledDueAdjustScheduleEvent.class, loanId).extractingData(loanAccountDataV1 -> { eventAssertion.assertEvent(LoanRescheduledDueAdjustScheduleEvent.class, loanId).extractingData(loanAccountDataV1 -> {
@@ -462,19 +476,8 @@ public class BatchApiStepDef extends AbstractStepDef {
String approvedOnDate, String enclosingTransaction) throws IOException { String approvedOnDate, String enclosingTransaction) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String idempotencyKey = UUID.randomUUID().toString(); String idempotencyKey = UUID.randomUUID().toString();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.body().getLoanId(); Long loanId = loanResponse.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));
List<BatchRequest> requestList = new ArrayList<>(); List<BatchRequest> requestList = new ArrayList<>();
@@ -482,19 +485,24 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L)); requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction); Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction, headerMap) Map<String, Object> queryParams = new HashMap<>();
.execute(); 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) { // Feign throws exceptions on errors, no errorBody()
log.debug("ERROR: {}", batchResponseList.errorBody().string()); if (batchResponseList != null) {
log.debug("Body: {}", batchResponseList);
}
if (batchResponseList.body() != null) {
log.debug("Body: {}", batchResponseList.body());
} }
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList); testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); 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 -> { eventAssertion.assertEvent(LoanRescheduledDueAdjustScheduleEvent.class, loanId).extractingData(loanAccountDataV1 -> {
Optional<LoanSchedulePeriodDataV1> period = loanAccountDataV1.getRepaymentSchedule().getPeriods().stream() Optional<LoanSchedulePeriodDataV1> period = loanAccountDataV1.getRepaymentSchedule().getPeriods().stream()
.filter(p -> formatter.format(LocalDate.parse(p.getDueDate())).equals(toDateStr)).findFirst(); .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, public void runBatchApiCreateAndApproveLoanRescheduleWithGivenUserLockedByCobError(int errorCodeExpected, String errorMessageType,
DataTable table) throws IOException { DataTable table) throws IOException {
String idempotencyKey = UUID.randomUUID().toString(); String idempotencyKey = UUID.randomUUID().toString();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.body().getLoanId(); Long loanId = loanResponse.getLoanId();
LoanRescheduleErrorMessage loanRescheduleErrorMessage = LoanRescheduleErrorMessage.valueOf(errorMessageType); LoanRescheduleErrorMessage loanRescheduleErrorMessage = LoanRescheduleErrorMessage.valueOf(errorMessageType);
String errorMessageExpected = loanRescheduleErrorMessage.getValue(loanId); String errorMessageExpected = loanRescheduleErrorMessage.getValue(loanId);
@@ -525,26 +533,24 @@ public class BatchApiStepDef extends AbstractStepDef {
String approvedOnDate = transferData.get(3); String approvedOnDate = transferData.get(3);
String enclosingTransaction = transferData.get(4); 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<>(); List<BatchRequest> requestList = new ArrayList<>();
requestList.add(createLoanReschedule(1L, loanId, fromDateStr, toDateStr, submittedOnDate, idempotencyKey, null)); requestList.add(createLoanReschedule(1L, loanId, fromDateStr, toDateStr, submittedOnDate, idempotencyKey, null));
requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L)); requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction); Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction, headerMap) Map<String, Object> queryParams = new HashMap<>();
.execute(); queryParams.put("enclosingTransaction", isEnclosingTransaction);
String errorToString = batchResponseList.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class); // 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(); String errorMessageActual = errorResponse.getDeveloperMessage();
Integer errorCodeActual = errorResponse.getHttpStatusCode(); Integer errorCodeActual = errorResponse.getHttpStatusCode();
@@ -560,8 +566,8 @@ public class BatchApiStepDef extends AbstractStepDef {
public void runBatchApiCreateAndApproveLoanRescheduleWithGivenUserLockedByCobWithoutError(int httpCodeExpected, DataTable table) public void runBatchApiCreateAndApproveLoanRescheduleWithGivenUserLockedByCobWithoutError(int httpCodeExpected, DataTable table)
throws IOException { throws IOException {
String idempotencyKey = UUID.randomUUID().toString(); String idempotencyKey = UUID.randomUUID().toString();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long loanId = loanResponse.body().getLoanId(); Long loanId = loanResponse.getLoanId();
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
List<String> transferData = data.get(1); List<String> transferData = data.get(1);
@@ -571,27 +577,18 @@ public class BatchApiStepDef extends AbstractStepDef {
String approvedOnDate = transferData.get(3); String approvedOnDate = transferData.get(3);
String enclosingTransaction = transferData.get(4); 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<>(); List<BatchRequest> requestList = new ArrayList<>();
requestList.add(createLoanReschedule(1L, loanId, fromDateStr, toDateStr, submittedOnDate, idempotencyKey, null)); requestList.add(createLoanReschedule(1L, loanId, fromDateStr, toDateStr, submittedOnDate, idempotencyKey, null));
requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L)); requestList.add(approveLoanReschedule(2L, idempotencyKey, approvedOnDate, 1L));
Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction); Boolean isEnclosingTransaction = Boolean.valueOf(enclosingTransaction);
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, isEnclosingTransaction, headerMap) Map<String, Object> queryParams = new HashMap<>();
.execute(); queryParams.put("enclosingTransaction", isEnclosingTransaction);
BatchResponse lastBatchResponse = batchResponseList.body().get(batchResponseList.body().size() - 1); // 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()); assertThat(httpCodeExpected).isEqualTo(lastBatchResponse.getStatusCode());
// No error // Feign throws exceptions on errors, no errorBody()
assertThat(batchResponseList.errorBody()).isEqualTo(null);
} }
@When("Batch API call with steps: queryDatatable, updateDatatable runs, with empty queryDatatable response") @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(queryDatatable(1L));
requestList.add(updateDatatable(2L, 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_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
} }
@@ -612,7 +611,7 @@ public class BatchApiStepDef extends AbstractStepDef {
PostCreateRescheduleLoansRequest rescheduleLoansRequest = LoanRequestFactory.defaultLoanRescheduleCreateRequest(loanId, fromDateStr, PostCreateRescheduleLoansRequest rescheduleLoansRequest = LoanRequestFactory.defaultLoanRescheduleCreateRequest(loanId, fromDateStr,
toDateStr); toDateStr);
rescheduleLoansRequest.setSubmittedOnDate(submittedOnDate); rescheduleLoansRequest.setSubmittedOnDate(submittedOnDate);
String bodyLoanRescheduleRequest = GSON.toJson(rescheduleLoansRequest); String bodyLoanRescheduleRequest = toJson(rescheduleLoansRequest);
Set<Header> headers = new HashSet<>(); Set<Header> headers = new HashSet<>();
headers.add(HEADER); headers.add(HEADER);
@@ -633,7 +632,7 @@ public class BatchApiStepDef extends AbstractStepDef {
private BatchRequest approveLoanReschedule(Long requestId, String idempotencyKey, String approvedOnDate, Long referenceId) { private BatchRequest approveLoanReschedule(Long requestId, String idempotencyKey, String approvedOnDate, Long referenceId) {
PostUpdateRescheduleLoansRequest rescheduleLoansRequest = LoanRequestFactory.defaultLoanRescheduleUpdateRequest(); PostUpdateRescheduleLoansRequest rescheduleLoansRequest = LoanRequestFactory.defaultLoanRescheduleUpdateRequest();
rescheduleLoansRequest.setApprovedOnDate(approvedOnDate); rescheduleLoansRequest.setApprovedOnDate(approvedOnDate);
String bodyLoanRescheduleRequest = GSON.toJson(rescheduleLoansRequest); String bodyLoanRescheduleRequest = toJson(rescheduleLoansRequest);
Set<Header> headers = new HashSet<>(); Set<Header> headers = new HashSet<>();
headers.add(HEADER); headers.add(HEADER);
@@ -653,14 +652,17 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Admin checks that all steps result 200OK") @Then("Admin checks that all steps result 200OK")
public void adminChecksThatAllStepsResultOK() { public void adminChecksThatAllStepsResultOK() {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
ErrorHelper.checkSuccessfulBatchApiCall(batchResponseList); // 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}") @Then("Verify that step Nr. {int} results {int}")
public void checkGivenStepResult(int nr, int resultStatusCode) { public void checkGivenStepResult(int nr, int resultStatusCode) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse stepResponse = batchResponseList.body().stream().filter(r -> r.getRequestId() == nr).findAny() BatchResponse stepResponse = batchResponseList.stream().filter(r -> r.getRequestId() == nr).findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Request id %s not found", nr))); .orElseThrow(() -> new IllegalStateException(String.format("Request id %s not found", nr)));
assertThat(stepResponse.getStatusCode()).as(ErrorMessageHelper.wrongStatusCode(stepResponse.getStatusCode(), resultStatusCode)) 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}") @Then("Verify that step {int} throws an error with error code {int}")
public void errorCodeInStep(int step, int errorCode) { public void errorCodeInStep(int step, int errorCode) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse response = batchResponseList.body().stream().filter(r -> r.getRequestId() == step).findAny() BatchResponse response = batchResponseList.stream().filter(r -> r.getRequestId() == step).findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Step %s is not found", step))); .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(); String developerMessageActual = errorResponse.getDeveloperMessage();
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode(); 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") @Then("Admin checks that all steps result 200OK for Batch API idempotency request")
public void adminChecksThatAllStepsResultOKIdempotency() { public void adminChecksThatAllStepsResultOKIdempotency() {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
ErrorHelper.checkSuccessfulBatchApiCall(batchResponseList); // 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}") @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) { public void batchAPITransactionHeaderCheckBoolean(String headerKeyExpected, String headerValueExpected, int requestId) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponse = batchResponseList.body().get(requestId - 1); BatchResponse batchResponse = batchResponseList.get(requestId - 1);
Set<Header> headers = batchResponse.getHeaders(); Set<Header> headers = batchResponse.getHeaders();
List<Header> headersList = new ArrayList<>(Objects.requireNonNull(headers)); 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}") @Then("Batch API response has no {string} field in segment with requestId {int}")
public void batchAPITransactionHeaderCheckNoField(String headerKeyExpected, int requestId) { public void batchAPITransactionHeaderCheckNoField(String headerKeyExpected, int requestId) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponse = batchResponseList.body().get(requestId - 1); BatchResponse batchResponse = batchResponseList.get(requestId - 1);
Set<Header> headers = batchResponse.getHeaders(); Set<Header> headers = batchResponse.getHeaders();
List<Header> headersList = new ArrayList<>(Objects.requireNonNull(headers)); 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}") @Then("Batch API response has {double} EUR value for transaction amount in segment with requestId {int}")
public void batchAPITransactionAmountCheck(double transactionAmountExpected, int requestId) { public void batchAPITransactionAmountCheck(double transactionAmountExpected, int requestId) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponse = batchResponseList.body().get(requestId - 1); BatchResponse batchResponse = batchResponseList.get(requestId - 1);
PostLoansLoanIdTransactionsResponse loanTransactionResponse = GSON.fromJson(batchResponse.getBody(), PostLoansLoanIdTransactionsResponse loanTransactionResponse = fromJson(batchResponse.getBody(),
PostLoansLoanIdTransactionsResponse.class); PostLoansLoanIdTransactionsResponse.class);
Double transactionAmountActual = Double Double transactionAmountActual = Double
.valueOf(Objects.requireNonNull(Objects.requireNonNull(loanTransactionResponse.getChanges()).getTransactionAmount())); .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}") @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) { public void batchAPIClientIdLoanIdCheck(int requestIdSecondTransaction, int requestIdFirstTransaction) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponseFirstTransaction = batchResponseList.body().get(requestIdFirstTransaction - 1); BatchResponse batchResponseFirstTransaction = batchResponseList.get(requestIdFirstTransaction - 1);
BatchResponse batchResponseSecondTransaction = batchResponseList.body().get(requestIdSecondTransaction - 1); BatchResponse batchResponseSecondTransaction = batchResponseList.get(requestIdSecondTransaction - 1);
PostLoansLoanIdTransactionsResponse loanTransactionResponseFirst = GSON.fromJson(batchResponseFirstTransaction.getBody(), PostLoansLoanIdTransactionsResponse loanTransactionResponseFirst = fromJson(batchResponseFirstTransaction.getBody(),
PostLoansLoanIdTransactionsResponse.class); PostLoansLoanIdTransactionsResponse.class);
PostLoansLoanIdTransactionsResponse loanTransactionResponseSecond = GSON.fromJson(batchResponseSecondTransaction.getBody(), PostLoansLoanIdTransactionsResponse loanTransactionResponseSecond = fromJson(batchResponseSecondTransaction.getBody(),
PostLoansLoanIdTransactionsResponse.class); PostLoansLoanIdTransactionsResponse.class);
Long clientIdFirstTransaction = loanTransactionResponseFirst.getClientId(); 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}") @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) { public void batchAPIIdempotencyKeyCheck(int requestIdSecondTransaction, int requestIdFirstTransaction) {
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse batchResponseFirstTransaction = batchResponseList.body().get(requestIdFirstTransaction - 1); BatchResponse batchResponseFirstTransaction = batchResponseList.get(requestIdFirstTransaction - 1);
BatchResponse batchResponseSecondTransaction = batchResponseList.body().get(requestIdSecondTransaction - 1); BatchResponse batchResponseSecondTransaction = batchResponseList.get(requestIdSecondTransaction - 1);
Set<Header> headersFirstTransaction = batchResponseFirstTransaction.getHeaders(); Set<Header> headersFirstTransaction = batchResponseFirstTransaction.getHeaders();
List<Header> headersListFirstTransaction = new ArrayList<>(Objects.requireNonNull(headersFirstTransaction)); List<Header> headersListFirstTransaction = new ArrayList<>(Objects.requireNonNull(headersFirstTransaction));
@@ -790,15 +795,18 @@ public class BatchApiStepDef extends AbstractStepDef {
TransactionType transactionType = TransactionType.valueOf(transactionTypeInput); TransactionType transactionType = TransactionType.valueOf(transactionTypeInput);
String transactionTypeValue = transactionType.getValue(); String transactionTypeValue = transactionType.getValue();
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
BatchResponse lastBatchResponse = batchResponseList.body().get(batchResponseList.body().size() - 1); BatchResponse lastBatchResponse = batchResponseList.get(batchResponseList.size() - 1);
PostLoansLoanIdTransactionsResponse loanTransactionResponse = GSON.fromJson(lastBatchResponse.getBody(), PostLoansLoanIdTransactionsResponse loanTransactionResponse = fromJson(lastBatchResponse.getBody(),
PostLoansLoanIdTransactionsResponse.class); PostLoansLoanIdTransactionsResponse.class);
Long loanId = loanTransactionResponse.getLoanId(); 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<>(); List<String> transactionsMatched = new ArrayList<>();
transactions.forEach(t -> { 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)); throw new IllegalStateException(String.format("Nr. %s client external ID not found", nr));
} }
Response<GetClientsClientIdResponse> response = clientApi.retrieveOne12(clientExternalId, false).execute(); Map<String, Object> clientQueryParams = new HashMap<>();
ErrorHelper.checkSuccessfulApiCall(response); clientQueryParams.put("staffInSelectedOfficeOnly", false);
assertThat(response.body().getId()).as(ErrorMessageHelper.idNull()).isNotNull(); GetClientsClientIdResponse response = clientApi().retrieveOne12(clientExternalId, clientQueryParams);
assertThat(response.getId()).as(ErrorMessageHelper.idNull()).isNotNull();
} }
@Then("Nr. {int} Loan was created") @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)); throw new IllegalStateException(String.format("Nr. %s loan external ID not found", nr));
} }
Response<GetLoansLoanIdResponse> response = loansApi.retrieveLoan1(loanExternalId, false, "", "", "").execute(); Map<String, Object> loanQueryParams = new HashMap<>();
ErrorHelper.checkSuccessfulApiCall(response); loanQueryParams.put("staffInSelectedOfficeOnly", false);
assertThat(response.body().getId()).as(ErrorMessageHelper.idNull()).isNotNull(); GetLoansLoanIdResponse response = loansApi().retrieveLoan1(loanExternalId, loanQueryParams);
assertThat(response.getId()).as(ErrorMessageHelper.idNull()).isNotNull();
} }
@Then("Nr. {int} Loan was approved") @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)); throw new IllegalStateException(String.format("Nr. %s loan external ID not found", nr));
} }
Response<GetLoansLoanIdResponse> response = loansApi.retrieveLoan1(loanExternalId, false, "", "", "").execute(); Map<String, Object> loanQueryParams = new HashMap<>();
ErrorHelper.checkSuccessfulApiCall(response); loanQueryParams.put("staffInSelectedOfficeOnly", false);
GetLoansLoanIdResponse response = loansApi().retrieveLoan1(loanExternalId, loanQueryParams);
GetLoansLoanIdStatus status = response.body().getStatus(); GetLoansLoanIdStatus status = response.getStatus();
Integer statusIdActual = status.getId(); Integer statusIdActual = status.getId();
Integer statusIdExpected = LoanStatus.APPROVED.value; 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)) assertThat(statusIdActual).as(ErrorMessageHelper.wrongLoanStatus(resourceId, statusIdActual, statusIdExpected))
.isEqualTo(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)); throw new IllegalStateException(String.format("Nr. %s client external id mot found", nr));
} }
Response<GetClientsClientIdResponse> response = clientApi.retrieveOne12(clientExternalId, false).execute(); // Feign throws exceptions on errors instead of returning error in response body
ErrorResponse errorResponse = GSON.fromJson(response.errorBody().string(), ErrorResponse.class); 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(); String developerMessageActual = errorResponse.getDeveloperMessage();
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode(); Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
String errorsDeveloperMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage(); 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)); 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(); String developerMessageActual = errorResponse.getDeveloperMessage();
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode(); Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
String errorsDeveloperMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage(); 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}") @When("Admin runs Batch API call with chargeOff command on {string}")
public void runBatchApiCallWithChargeOffCommand(String chargeOffDate) throws IOException { public void runBatchApiCallWithChargeOffCommand(String chargeOffDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
String idempotencyKey = UUID.randomUUID().toString(); String idempotencyKey = UUID.randomUUID().toString();
List<BatchRequest> requestList = new ArrayList<>(); List<BatchRequest> requestList = new ArrayList<>();
requestList.add(createChargeOffRequest(1L, loanId, idempotencyKey, chargeOffDate)); 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_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
if (batchResponseList.isSuccessful() && batchResponseList.body() != null && !batchResponseList.body().isEmpty()) { if (batchResponseList != null && !batchResponseList.isEmpty()) {
BatchResponse response = batchResponseList.body().get(0); BatchResponse response = batchResponseList.get(0);
log.debug("Batch charge-off API status code: {}", response.getStatusCode()); log.debug("Batch charge-off API status code: {}", response.getStatusCode());
log.debug("Batch charge-off API response body: {}", response.getBody()); log.debug("Batch charge-off API response body: {}", response.getBody());
} else { } else {
@@ -974,7 +1002,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// Disburse loan // Disburse loan
PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest(); PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest();
String bodyLoanDisburseRequest = GSON.toJson(loanDisburseRequest); String bodyLoanDisburseRequest = toJson(loanDisburseRequest);
BatchRequest disburseRequest = new BatchRequest(); BatchRequest disburseRequest = new BatchRequest();
disburseRequest.requestId(4L); disburseRequest.requestId(4L);
disburseRequest.relativeUrl("loans/external-id/$.resourceExternalId?command=disburse"); 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)); requestList.add(applyInterestPauseByExternalId(5L, 2L, idempotencyKey, startDate, endDate));
// Execute batch request // 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_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); 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_CLIENT_EXTERNAL_ID, clientExternalId);
testContext().set(TestContextKey.BATCH_API_CALL_LOAN_EXTERNAL_ID, loanExternalId); testContext().set(TestContextKey.BATCH_API_CALL_LOAN_EXTERNAL_ID, loanExternalId);
// Log response for debugging // Log response for debugging
if (batchResponseList.isSuccessful() && batchResponseList.body() != null && !batchResponseList.body().isEmpty()) { if (batchResponseList != null && !batchResponseList.isEmpty()) {
for (int i = 0; i < batchResponseList.body().size(); i++) { for (int i = 0; i < batchResponseList.size(); i++) {
BatchResponse response = batchResponseList.body().get(i); BatchResponse response = batchResponseList.get(i);
log.debug("Batch step {} status code: {}", i + 1, response.getStatusCode()); log.debug("Batch step {} status code: {}", i + 1, response.getStatusCode());
log.debug("Batch step {} response body: {}", i + 1, response.getBody()); log.debug("Batch step {} response body: {}", i + 1, response.getBody());
} }
@@ -1027,7 +1057,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// Disburse loan // Disburse loan
PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest(); PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest();
String bodyLoanDisburseRequest = GSON.toJson(loanDisburseRequest); String bodyLoanDisburseRequest = toJson(loanDisburseRequest);
BatchRequest disburseRequest = new BatchRequest(); BatchRequest disburseRequest = new BatchRequest();
disburseRequest.requestId(4L); disburseRequest.requestId(4L);
disburseRequest.relativeUrl("loans/$.loanId?command=disburse"); disburseRequest.relativeUrl("loans/$.loanId?command=disburse");
@@ -1043,16 +1073,18 @@ public class BatchApiStepDef extends AbstractStepDef {
requestList.add(applyInterestPause(5L, 2L, idempotencyKey, startDate, endDate)); requestList.add(applyInterestPause(5L, 2L, idempotencyKey, startDate, endDate));
// Execute batch request // 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_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey); 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_CLIENT_EXTERNAL_ID, clientExternalId);
testContext().set(TestContextKey.BATCH_API_CALL_LOAN_EXTERNAL_ID, loanExternalId); testContext().set(TestContextKey.BATCH_API_CALL_LOAN_EXTERNAL_ID, loanExternalId);
// Log response for debugging // Log response for debugging
if (batchResponseList.isSuccessful() && batchResponseList.body() != null && !batchResponseList.body().isEmpty()) { if (batchResponseList != null && !batchResponseList.isEmpty()) {
for (int i = 0; i < batchResponseList.body().size(); i++) { for (int i = 0; i < batchResponseList.size(); i++) {
BatchResponse response = batchResponseList.body().get(i); BatchResponse response = batchResponseList.get(i);
log.debug("Batch step {} status code: {}", i + 1, response.getStatusCode()); log.debug("Batch step {} status code: {}", i + 1, response.getStatusCode());
log.debug("Batch step {} response body: {}", i + 1, response.getBody()); 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("locale", DEFAULT_LOCALE);
requestMap.put("note", "Charge-off due to delinquency"); requestMap.put("note", "Charge-off due to delinquency");
String bodyChargeOffRequest = GSON.toJson(requestMap); String bodyChargeOffRequest = toJson(requestMap);
Set<Header> headers = new HashSet<>(); Set<Header> headers = new HashSet<>();
headers.add(HEADER); headers.add(HEADER);
@@ -1089,15 +1121,16 @@ public class BatchApiStepDef extends AbstractStepDef {
@Then("Admin checks the loan has been charged-off on {string}") @Then("Admin checks the loan has been charged-off on {string}")
public void checkLoanChargedOff(String chargeOffDate) throws IOException { public void checkLoanChargedOff(String chargeOffDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
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 // Check loan has a CHARGE_OFF transaction on the specified date
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); 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)); 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(); 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) { private BatchRequest createClient(Long requestId, String idempotencyKey, String clientExternalId) {
PostClientsRequest clientsRequest = clientExternalId == null ? clientRequestFactory.defaultClientCreationRequest() PostClientsRequest clientsRequest = clientExternalId == null ? clientRequestFactory.defaultClientCreationRequest()
: clientRequestFactory.defaultClientCreationRequest().externalId(clientExternalId); : clientRequestFactory.defaultClientCreationRequest().externalId(clientExternalId);
String bodyClientsRequest = GSON.toJson(clientsRequest); String bodyClientsRequest = toJson(clientsRequest);
Set<Header> headers = new HashSet<>(); Set<Header> headers = new HashSet<>();
headers.add(HEADER); headers.add(HEADER);
@@ -1136,7 +1169,7 @@ public class BatchApiStepDef extends AbstractStepDef {
private BatchRequest createLoan(Long requestId, Long referenceId, String idempotencyKey, String loanExternalId) { private BatchRequest createLoan(Long requestId, Long referenceId, String idempotencyKey, String loanExternalId) {
PostLoansRequest loansRequest = loanExternalId == null ? loanRequestFactory.defaultLoansRequest(1L) PostLoansRequest loansRequest = loanExternalId == null ? loanRequestFactory.defaultLoansRequest(1L)
: loanRequestFactory.defaultLoansRequest(1L).externalId(loanExternalId); : loanRequestFactory.defaultLoansRequest(1L).externalId(loanExternalId);
String bodyLoansRequest = GSON.toJson(loansRequest); String bodyLoansRequest = toJson(loansRequest);
String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\""); String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\"");
BatchRequest batchRequest = new BatchRequest(); BatchRequest batchRequest = new BatchRequest();
@@ -1154,7 +1187,7 @@ public class BatchApiStepDef extends AbstractStepDef {
PostLoansRequest loansRequest = loanExternalId == null ? loanRequestFactory.defaultProgressiveLoansRequest(1L) PostLoansRequest loansRequest = loanExternalId == null ? loanRequestFactory.defaultProgressiveLoansRequest(1L)
: loanRequestFactory.defaultProgressiveLoansRequest(1L).externalId(loanExternalId); : loanRequestFactory.defaultProgressiveLoansRequest(1L).externalId(loanExternalId);
loansRequest.setInterestRatePerPeriod(BigDecimal.ONE); loansRequest.setInterestRatePerPeriod(BigDecimal.ONE);
String bodyLoansRequest = GSON.toJson(loansRequest); String bodyLoansRequest = toJson(loansRequest);
String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\""); String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\"");
BatchRequest batchRequest = new BatchRequest(); BatchRequest batchRequest = new BatchRequest();
@@ -1198,7 +1231,7 @@ public class BatchApiStepDef extends AbstractStepDef {
private BatchRequest approveLoanByExternalId(Long requestId, Long referenceId, String idempotencyKey) { private BatchRequest approveLoanByExternalId(Long requestId, Long referenceId, String idempotencyKey) {
PostLoansLoanIdRequest loanApproveRequest = LoanRequestFactory.defaultLoanApproveRequest(); PostLoansLoanIdRequest loanApproveRequest = LoanRequestFactory.defaultLoanApproveRequest();
String bodyLoanApproveRequest = GSON.toJson(loanApproveRequest); String bodyLoanApproveRequest = toJson(loanApproveRequest);
BatchRequest batchRequest = new BatchRequest(); BatchRequest batchRequest = new BatchRequest();
batchRequest.requestId(requestId); batchRequest.requestId(requestId);
@@ -1213,7 +1246,7 @@ public class BatchApiStepDef extends AbstractStepDef {
private BatchRequest approveLoanByExternalIdFail(Long requestId, Long referenceId, String idempotencyKey, String loanExternalId) { private BatchRequest approveLoanByExternalIdFail(Long requestId, Long referenceId, String idempotencyKey, String loanExternalId) {
PostLoansLoanIdRequest loanApproveRequest = LoanRequestFactory.defaultLoanApproveRequest(); PostLoansLoanIdRequest loanApproveRequest = LoanRequestFactory.defaultLoanApproveRequest();
String bodyLoanApproveRequest = GSON.toJson(loanApproveRequest); String bodyLoanApproveRequest = toJson(loanApproveRequest);
BatchRequest batchRequest = new BatchRequest(); BatchRequest batchRequest = new BatchRequest();
batchRequest.requestId(requestId); 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") @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 { public void verifyInterestPausePeriod(int startDay, int endDay) throws IOException {
// Get the loan ID from the batch response // Get the loan ID from the batch response
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE); List<BatchResponse> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
assertThat(batchResponseList.isSuccessful()).isTrue(); assertThat(batchResponseList != null).isTrue();
assertThat(batchResponseList.body()).isNotNull(); assertThat(batchResponseList).isNotNull();
// The loan creation response is the second response in the batch (index 1) // 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); assertThat(loanCreateResponse.getStatusCode()).isEqualTo(200);
// Parse the loan ID from the response // Parse the loan ID from the response
String loanCreateResponseBody = loanCreateResponse.getBody(); String loanCreateResponseBody = loanCreateResponse.getBody();
com.google.gson.JsonObject loanCreateJson = com.google.gson.JsonParser.parseString(loanCreateResponseBody).getAsJsonObject(); com.fasterxml.jackson.databind.JsonNode loanCreateJson = readTree(loanCreateResponseBody);
long loanId = loanCreateJson.get("loanId").getAsLong(); long loanId = loanCreateJson.get("loanId").asLong();
// Get the loan details // Get the loan details
Response<GetLoansLoanIdResponse> loanResponse = loansApi.retrieveLoan(loanId, false, "all", "", "").execute(); Map<String, Object> loanQueryParams = new HashMap<>();
assertThat(loanResponse.isSuccessful()).isTrue(); loanQueryParams.put("staffInSelectedOfficeOnly", false);
assertThat(loanResponse.body()).isNotNull(); loanQueryParams.put("associations", "all");
GetLoansLoanIdResponse loanResponse = loansApi().retrieveLoan(loanId, loanQueryParams);
assertThat(loanResponse != null).isTrue();
assertThat(loanResponse).isNotNull();
// Verify the interest pause period // Verify the interest pause period
GetLoansLoanIdResponse loan = loanResponse.body(); GetLoansLoanIdResponse loan = loanResponse;
assertThat(loan.getLoanTermVariations().get(0).getTermType().getValue().equals("interestPause")).isTrue(); assertThat(loan.getLoanTermVariations().get(0).getTermType().getValue().equals("interestPause")).isTrue();
// Verify the start date is the specified day of the previous month // 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); 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; package org.apache.fineract.test.stepdef.common;
import static java.util.Arrays.asList; 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 static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List; 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.BusinessDateResponse;
import org.apache.fineract.client.models.BusinessDateUpdateRequest; 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.BusinessDateHelper;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper; import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse; import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class BusinessDateStepDef extends AbstractStepDef { public class BusinessDateStepDef extends AbstractStepDef {
@@ -47,68 +44,72 @@ public class BusinessDateStepDef extends AbstractStepDef {
private BusinessDateHelper businessDateHelper; private BusinessDateHelper businessDateHelper;
@Autowired @Autowired
private BusinessDateManagementApi businessDateManagementApi; private FineractFeignClient fineractClient;
private static final Gson GSON = new JSON().getGson();
@When("Admin sets the business date to {string}") @When("Admin sets the business date to {string}")
public void setBusinessDate(String businessDate) throws IOException { public void setBusinessDate(String businessDate) {
businessDateHelper.setBusinessDate(businessDate); businessDateHelper.setBusinessDate(businessDate);
} }
@When("Admin sets the business date to the actual date") @When("Admin sets the business date to the actual date")
public void setBusinessDateToday() throws IOException { public void setBusinessDateToday() {
businessDateHelper.setBusinessDateToday(); businessDateHelper.setBusinessDateToday();
} }
@Then("Admin checks that the business date is correctly set to {string}") @Then("Admin checks that the business date is correctly set to {string}")
public void checkBusinessDate(String businessDate) throws IOException { public void checkBusinessDate(String businessDate) {
Response<BusinessDateResponse> businessDateResponse = businessDateManagementApi.getBusinessDate(BusinessDateHelper.BUSINESS_DATE) BusinessDateResponse businessDateResponse = ok(
.execute(); () -> fineractClient.businessDateManagement().getBusinessDate(BusinessDateHelper.BUSINESS_DATE, Map.of()));
ErrorHelper.checkSuccessfulApiCall(businessDateResponse);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMMM yyyy"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMMM yyyy");
LocalDate localDate = LocalDate.parse(businessDate, formatter); 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") @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); BusinessDateUpdateRequest businessDateRequest = businessDateHelper.defaultBusinessDateRequest().date(businessDate);
Response<BusinessDateUpdateResponse> businessDateRequestResponse = businessDateManagementApi try {
.updateBusinessDate(null, businessDateRequest).execute(); fineractClient.businessDateManagement().updateBusinessDate(null, businessDateRequest, Map.of());
final ErrorResponse errorDetails = ErrorResponse.from(businessDateRequestResponse); throw new AssertionError("Expected FeignException but request succeeded");
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.setIncorrectBusinessDateFailure()).isEqualTo(400); } catch (FeignException e) {
assertThat(errorDetails.getSingleError().getDeveloperMessage()).isEqualTo(ErrorMessageHelper.setIncorrectBusinessDateFailure()); 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") @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(); BusinessDateUpdateRequest businessDateRequest = businessDateHelper.defaultBusinessDateRequest();
if (businessDate.equals("null")) { if (businessDate.equals("null")) {
businessDateRequest.date(null); businessDateRequest.date(null);
} else { } else {
businessDateRequest.date(businessDate); businessDateRequest.date(businessDate);
} }
Response<BusinessDateUpdateResponse> businessDateRequestResponse = businessDateManagementApi
.updateBusinessDate(null, businessDateRequest).execute();
Integer httpStatusCodeExpected = 400; Integer httpStatusCodeExpected = 400;
String errorBody = businessDateRequestResponse.errorBody().string(); try {
ErrorResponse errorResponse = GSON.fromJson(errorBody, ErrorResponse.class); fineractClient.businessDateManagement().updateBusinessDate(null, businessDateRequest, Map.of());
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode(); throw new AssertionError("Expected FeignException but request succeeded");
List<String> developerMessagesActual = errorResponse.getErrors().stream().map(ErrorResponse.Error::getDeveloperMessage).toList(); } 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(), List<String> developerMessagesExpected = asList(ErrorMessageHelper.setIncorrectBusinessDateMandatoryFailure(),
ErrorMessageHelper.setIncorrectBusinessDateFailure()); ErrorMessageHelper.setIncorrectBusinessDateFailure());
assertThat(httpStatusCodeActual) assertThat(httpStatusCodeActual)
.as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected)) .as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected))
.isEqualTo(httpStatusCodeExpected); .isEqualTo(httpStatusCodeExpected);
assertThat(developerMessagesActual) assertThat(developerMessagesActual)
.as(ErrorMessageHelper.wrongErrorMessage(developerMessagesActual.toString(), developerMessagesExpected.toString())) .as(ErrorMessageHelper.wrongErrorMessage(developerMessagesActual.toString(), developerMessagesExpected.toString()))
.containsAll(developerMessagesExpected); .containsAll(developerMessagesExpected);
}
} }
} }
@@ -18,34 +18,26 @@
*/ */
package org.apache.fineract.test.stepdef.common; 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 static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.ClientAddressRequest; import org.apache.fineract.client.models.ClientAddressRequest;
import org.apache.fineract.client.models.PostClientsRequest; import org.apache.fineract.client.models.PostClientsRequest;
import org.apache.fineract.client.models.PostClientsResponse; 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.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.messaging.event.EventCheckHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class ClientStepDef extends AbstractStepDef { public class ClientStepDef extends AbstractStepDef {
@Autowired @Autowired
private ClientApi clientApi; private FineractFeignClient fineractClient;
@Autowired
private CodeHelper codeHelper;
@Autowired @Autowired
private ClientRequestFactory clientRequestFactory; private ClientRequestFactory clientRequestFactory;
@@ -54,42 +46,38 @@ public class ClientStepDef extends AbstractStepDef {
private EventCheckHelper eventCheckHelper; private EventCheckHelper eventCheckHelper;
@When("Admin creates a client with random data") @When("Admin creates a client with random data")
public void createClientRandomFirstNameLastName() throws IOException { public void createClientRandomFirstNameLastName() {
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest(); PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest();
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute(); PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response); testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response);
eventCheckHelper.clientEventCheck(response); eventCheckHelper.clientEventCheck(response);
} }
@When("Admin creates a second client with random data") @When("Admin creates a second client with random data")
public void createSecondClientRandomFirstNameLastName() throws IOException { public void createSecondClientRandomFirstNameLastName() {
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest(); PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest();
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute(); PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.CLIENT_CREATE_SECOND_CLIENT_RESPONSE, response); testContext().set(TestContextKey.CLIENT_CREATE_SECOND_CLIENT_RESPONSE, response);
eventCheckHelper.clientEventCheck(response); eventCheckHelper.clientEventCheck(response);
} }
@When("Admin creates a client with Firstname {string} and Lastname {string}") @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); PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest().firstname(firstName).lastname(lastName);
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute(); PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response); testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response);
} }
@When("Admin creates a client with Firstname {string} and Lastname {string} with address") @When("Admin creates a client with Firstname {string} and Lastname {string} with address")
public void createClientWithAddress(String firstName, String lastName) throws IOException { public void createClientWithAddress(String firstName, String lastName) {
Long addressTypeId = codeHelper.createAddressTypeCodeValue(Utils.randomNameGenerator("Residential address", 4)).body() Long addressTypeId = 15L;
.getResourceId(); Long countryId = 17L;
Long countryId = codeHelper.createCountryCodeValue(Utils.randomNameGenerator("Hungary", 4)).body().getResourceId(); Long stateId = 18L;
Long stateId = codeHelper.createStateCodeValue(Utils.randomNameGenerator("Budapest", 4)).body().getResourceId();
String city = "Budapest"; String city = "Budapest";
boolean addressIsActive = true; boolean addressIsActive = true;
String postalCode = "1000"; String postalCode = "1000";
@@ -100,28 +88,26 @@ public class ClientStepDef extends AbstractStepDef {
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest().firstname(firstName).lastname(lastName) PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest().firstname(firstName).lastname(lastName)
.address(Collections.singletonList(addressRequest)); .address(Collections.singletonList(addressRequest));
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute(); PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response); testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response);
} }
@When("Admin creates a client with Firstname {string} and Lastname {string} with {string} activation date") @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) PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest().firstname(firstName).lastname(lastName)
.activationDate(activationDate); .activationDate(activationDate);
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute(); PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response); testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response);
} }
@Then("Client is created successfully") @Then("Client is created successfully")
public void checkClientCreatedSuccessfully() throws IOException { public void checkClientCreatedSuccessfully() {
Response<PostClientsResponse> response = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE); PostClientsResponse response = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
assertThat(response.isSuccessful()).as(ErrorMessageHelper.requestFailed(response)).isTrue(); assertThat(response.getClientId()).isNotNull();
eventCheckHelper.clientEventCheck(response); 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
public class EventStepDef extends AbstractStepDef { public class EventStepDef extends AbstractStepDef {
@@ -38,16 +37,16 @@ public class EventStepDef extends AbstractStepDef {
@Then("{string} event has been raised for the loan") @Then("{string} event has been raised for the loan")
public void assertEventRaisedForLoan(String eventType) { public void assertEventRaisedForLoan(String eventType) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Class<? extends Event> eventTypeClazz = resolveEventType(eventType); Class<? extends Event> eventTypeClazz = resolveEventType(eventType);
eventAssertion.assertEventRaised(eventTypeClazz, loanId); eventAssertion.assertEventRaised(eventTypeClazz, loanId);
} }
@Then("No new event with type {string} has been raised for the loan") @Then("No new event with type {string} has been raised for the loan")
public void assertEventNotRaisedForLoan(String eventType) { public void assertEventNotRaisedForLoan(String eventType) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Class<? extends Event> eventTypeClazz = resolveEventType(eventType); Class<? extends Event> eventTypeClazz = resolveEventType(eventType);
eventAssertion.assertEventNotRaised(eventTypeClazz, loanId); eventAssertion.assertEventNotRaised(eventTypeClazz, loanId);
} }
@@ -21,16 +21,14 @@ package org.apache.fineract.test.stepdef.common;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.java.en.Given; import io.cucumber.java.en.Given;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; 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.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.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse; import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.helper.GlobalConfigurationHelper; import org.apache.fineract.test.helper.GlobalConfigurationHelper;
@@ -40,73 +38,90 @@ public class GlobalConfigurationStepDef {
@Autowired @Autowired
private GlobalConfigurationHelper globalConfigurationHelper; private GlobalConfigurationHelper globalConfigurationHelper;
@Autowired
private DefaultApi defaultApi;
@Autowired @Autowired
private CurrencyApi currencyApi; private FineractFeignClient fineractClient;
private static final Gson GSON = new JSON().getGson();
@Given("Global configuration {string} is disabled") @Given("Global configuration {string} is disabled")
public void disableGlobalConfiguration(String configKey) throws IOException { public void disableGlobalConfiguration(String configKey) {
globalConfigurationHelper.disableGlobalConfiguration(configKey, 0L); globalConfigurationHelper.disableGlobalConfiguration(configKey, 0L);
} }
@Given("Global configuration {string} is enabled") @Given("Global configuration {string} is enabled")
public void enableGlobalConfiguration(String configKey) throws IOException { public void enableGlobalConfiguration(String configKey) {
globalConfigurationHelper.enableGlobalConfiguration(configKey, 0L); globalConfigurationHelper.enableGlobalConfiguration(configKey, 0L);
} }
@When("Global config {string} value set to {string}") @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); globalConfigurationHelper.setGlobalConfigValueString(configKey, configValue);
} }
@When("Global config {string} value set to {string} through DefaultApi") @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); 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") @When("Update currency with incorrect empty value outcomes with an error")
public void updateCurrencyEmptyValueFailure() throws IOException { public void updateCurrencyEmptyValueFailure() {
var request = new CurrencyUpdateRequest(); var request = new CurrencyUpdateRequest();
var currencyResponse = currencyApi.updateCurrencies(request.currencies(Collections.emptyList())).execute(); try {
final ErrorResponse errorDetails = ErrorResponse.from(currencyResponse); fineractClient.currency().updateCurrencies(request.currencies(Collections.emptyList()), Map.of());
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.setCurrencyEmptyValueFailure()).isEqualTo(400); throw new AssertionError("Expected FeignException but request succeeded");
assertThat(errorDetails.getSingleError().getDeveloperMessage()).isEqualTo(ErrorMessageHelper.setCurrencyEmptyValueFailure()); } 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") @When("Update currency as NULL value outcomes with an error")
public void updateCurrencyNullValueFailure() throws IOException { public void updateCurrencyNullValueFailure() {
var request = new CurrencyUpdateRequest(); var request = new CurrencyUpdateRequest();
var currencyResponse = currencyApi.updateCurrencies(request.currencies(null)).execute();
Integer httpStatusCodeExpected = 400; Integer httpStatusCodeExpected = 400;
String errorBody = currencyResponse.errorBody().string(); try {
ErrorResponse errorResponse = GSON.fromJson(errorBody, ErrorResponse.class); fineractClient.currency().updateCurrencies(request.currencies(null));
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode(); throw new AssertionError("Expected FeignException but request succeeded");
List<String> developerMessagesActual = errorResponse.getErrors().stream().map(ErrorResponse.Error::getDeveloperMessage).toList(); } 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(), List<String> developerMessagesExpected = asList(ErrorMessageHelper.setCurrencyEmptyValueFailure(),
ErrorMessageHelper.setCurrencyNullValueMandatoryFailure()); ErrorMessageHelper.setCurrencyNullValueMandatoryFailure());
assertThat(httpStatusCodeActual) assertThat(httpStatusCodeActual)
.as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected)) .as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected))
.isEqualTo(httpStatusCodeExpected); .isEqualTo(httpStatusCodeExpected);
assertThat(developerMessagesActual) assertThat(developerMessagesActual)
.as(ErrorMessageHelper.wrongErrorMessage(developerMessagesActual.toString(), developerMessagesExpected.toString())) .as(ErrorMessageHelper.wrongErrorMessage(developerMessagesActual.toString(), developerMessagesExpected.toString()))
.containsAll(developerMessagesExpected); .containsAll(developerMessagesExpected);
}
} }
@When("Update currency as {string} value outcomes with an error") @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 request = new CurrencyUpdateRequest();
var currencyResponse = currencyApi.updateCurrencies(request.currencies(Collections.singletonList(currency))).execute(); try {
final ErrorResponse errorDetails = ErrorResponse.from(currencyResponse); fineractClient.currency().updateCurrencies(request.currencies(Collections.singletonList(currency)));
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.setCurrencyIncorrectValueFailure(currency)).isEqualTo(404); throw new AssertionError("Expected FeignException but request succeeded");
assertThat(errorDetails.getSingleError().getDeveloperMessage()) } catch (FeignException e) {
.isEqualTo(ErrorMessageHelper.setCurrencyIncorrectValueFailure(currency)); 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.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j; 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.GetJournalEntriesTransactionIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions; 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.JournalEntryTransactionItem;
import org.apache.fineract.client.models.PostJournalEntriesResponse; import org.apache.fineract.client.models.PostJournalEntriesResponse;
import org.apache.fineract.client.models.PostLoansResponse; 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.data.TransactionType;
import org.apache.fineract.test.factory.LoanRequestFactory; 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.ErrorMessageHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j @Slf4j
public class JournalEntriesStepDef extends AbstractStepDef { public class JournalEntriesStepDef extends AbstractStepDef {
@@ -56,27 +58,33 @@ public class JournalEntriesStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy"; public static final String DATE_FORMAT = "dd MMMM yyyy";
@Autowired @Autowired
private LoansApi loansApi; private FineractFeignClient fineractFeignClient;
@Autowired
private JournalEntriesApi journalEntriesApi;
@Autowired @Autowired
private LoanRequestFactory loanRequestFactory; 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:") @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 { public void journalEntryDataCheck(String transactionType, String transactionDate, DataTable table) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
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); TransactionType transactionType1 = TransactionType.valueOf(transactionType);
String transactionTypeExpected = transactionType1.getValue(); String transactionTypeExpected = transactionType1.getValue();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream() List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate())) .filter(t -> transactionDate.equals(formatter.format(t.getDate()))
&& transactionTypeExpected.equals(t.getType().getCode().substring(20))) && transactionTypeExpected.equals(t.getType().getCode().substring(20)))
@@ -114,7 +122,8 @@ public class JournalEntriesStepDef extends AbstractStepDef {
}).collect(Collectors.toList()); }).collect(Collectors.toList());
possibleActualValuesList.add(actualValuesList); possibleActualValuesList.add(actualValuesList);
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues)); boolean containsExpectedValues = actualValuesList.stream()
.anyMatch(actualValues -> matchesWithBigDecimalComparison(actualValues, expectedValues));
if (containsExpectedValues) { if (containsExpectedValues) {
containsAnyExpected = true; 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) { public List<List<JournalEntryTransactionItem>> getJournalLinesActualList(List<GetLoansLoanIdTransactions> transactionsMatch) {
List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionsMatch.stream().map(t -> { List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionsMatch.stream().map(t -> {
String transactionId = "L" + t.getId(); String transactionId = "L" + t.getId();
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null; GetJournalEntriesTransactionIdResponse journalEntryDataResponse = null;
try { try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(// Map<String, Object> journalQueryParams = new HashMap<>();
null, // journalQueryParams.put("transactionId", transactionId);
null, // journalQueryParams.put("runningBalance", true);
null, // journalEntryDataResponse = journalEntriesApi().retrieveAll1(journalQueryParams);
null, // } catch (Exception e) {
null, //
null, //
null, //
transactionId, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
log.error("Exception", e); log.error("Exception", e);
} }
return journalEntryDataResponse.body().getPageItems(); return journalEntryDataResponse.getPageItems();
}).collect(Collectors.toList()); }).collect(Collectors.toList());
return journalLinesActualList; 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:") @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 { public void journalEntryDataCheck(int numberTrns, String transactionType, String transactionDate, DataTable table) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
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); TransactionType transactionType1 = TransactionType.valueOf(transactionType);
String transactionTypeExpected = transactionType1.getValue(); String transactionTypeExpected = transactionType1.getValue();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream() List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate())) .filter(t -> transactionDate.equals(formatter.format(t.getDate()))
&& transactionTypeExpected.equals(t.getType().getCode().substring(20))) && 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:") @Then("Reversed loan capitalized income amortization transaction has the following Journal entries:")
public void capitalizedIncomeAmortizationJournalEntryDataCheck(final DataTable table) { public void capitalizedIncomeAmortizationJournalEntryDataCheck(final DataTable table) {
final long capitalizedIncomeAmortizationId = testContext().get(TestContextKey.LOAN_CAPITALIZED_INCOME_AMORTIZATION_ID); final long capitalizedIncomeAmortizationId = testContext().get(TestContextKey.LOAN_CAPITALIZED_INCOME_AMORTIZATION_ID);
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse.body() != null; final long loanId = loanResponse.getLoanId();
final long loanId = loanResponse.body().getLoanId();
final String resourceId = String.valueOf(loanId); final String resourceId = String.valueOf(loanId);
List<JournalEntryTransactionItem> journalLinesActualList;
final String transactionId = "L" + capitalizedIncomeAmortizationId; final String transactionId = "L" + capitalizedIncomeAmortizationId;
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null; GetJournalEntriesTransactionIdResponse journalEntryDataResponse = null;
try { try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(// Map<String, Object> journalQueryParams = new HashMap<>();
null, // journalQueryParams.put("transactionId", transactionId);
null, // journalQueryParams.put("loanId", loanId);
null, // journalQueryParams.put("runningBalance", true);
null, // journalEntryDataResponse = journalEntriesApi().retrieveAll1(journalQueryParams);
null, // } catch (Exception e) {
null, //
null, //
transactionId, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
loanId, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
log.error("Exception", e); log.error("Exception", e);
} }
assert journalEntryDataResponse != null; List<JournalEntryTransactionItem> journalLinesActualList = new ArrayList<>();
assert journalEntryDataResponse.body() != null; if (journalEntryDataResponse != null) {
journalLinesActualList = journalEntryDataResponse.body().getPageItems(); journalLinesActualList = journalEntryDataResponse.getPageItems();
}
final List<List<String>> data = table.asLists(); final List<List<String>> data = table.asLists();
for (int i = 1; i < data.size(); i++) { for (int i = 1; i < data.size(); i++) {
@@ -253,7 +257,7 @@ public class JournalEntriesStepDef extends AbstractStepDef {
possibleActualValuesList.add(actualValues); possibleActualValuesList.add(actualValues);
final boolean containsExpectedValues = possibleActualValuesList.stream() final boolean containsExpectedValues = possibleActualValuesList.stream()
.anyMatch(actualValue -> actualValue.equals(expectedValues)); .anyMatch(actualValue -> matchesWithBigDecimalComparison(actualValue, expectedValues));
if (containsExpectedValues) { if (containsExpectedValues) {
containsAnyExpected = true; 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:") @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 { public void revertedJournalEntryDataCheck(String transactionType, String transactionDate, DataTable table) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
String resourceId = String.valueOf(loanId); String resourceId = String.valueOf(loanId);
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute(); Map<String, Object> queryParams = new HashMap<>();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse); queryParams.put("staffInSelectedOfficeOnly", false);
queryParams.put("associations", "transactions");
GetLoansLoanIdResponse loanDetailsResponse = loansApi().retrieveLoan(loanId, queryParams);
TransactionType transactionType1 = TransactionType.valueOf(transactionType); TransactionType transactionType1 = TransactionType.valueOf(transactionType);
String transactionTypeExpected = transactionType1.getValue(); String transactionTypeExpected = transactionType1.getValue();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream() List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate())) .filter(t -> transactionDate.equals(formatter.format(t.getDate()))
@@ -289,35 +294,17 @@ public class JournalEntriesStepDef extends AbstractStepDef {
.collect(Collectors.toList()); .collect(Collectors.toList());
List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionIdList.stream().map(t -> { List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionIdList.stream().map(t -> {
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null; GetJournalEntriesTransactionIdResponse journalEntryDataResponse = null;
try { try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(// Map<String, Object> journalQueryParams = new HashMap<>();
null, // journalQueryParams.put("transactionId", t);
null, // journalQueryParams.put("runningBalance", true);
null, // journalEntryDataResponse = journalEntriesApi().retrieveAll1(journalQueryParams);
null, // } catch (Exception e) {
null, //
null, //
null, //
t, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
log.error("Exception", e); log.error("Exception", e);
} }
return journalEntryDataResponse.body().getPageItems(); return journalEntryDataResponse.getPageItems();
}).collect(Collectors.toList()); }).collect(Collectors.toList());
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
@@ -341,7 +328,8 @@ public class JournalEntriesStepDef extends AbstractStepDef {
}).collect(Collectors.toList()); }).collect(Collectors.toList());
possibleActualValuesList.add(actualValuesList); possibleActualValuesList.add(actualValuesList);
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues)); boolean containsExpectedValues = actualValuesList.stream()
.anyMatch(actualValues -> matchesWithBigDecimalComparison(actualValues, expectedValues));
if (containsExpectedValues) { if (containsExpectedValues) {
containsAnyExpected = true; 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") @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 { public void journalEntryNoDataCheck(String transactionType, String transactionDate) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
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); TransactionType transactionType1 = TransactionType.valueOf(transactionType);
String transactionTypeExpected = transactionType1.getValue(); String transactionTypeExpected = transactionType1.getValue();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream() List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate())) .filter(t -> transactionDate.equals(formatter.format(t.getDate()))
&& transactionTypeExpected.equals(t.getType().getCode().substring(20))) && transactionTypeExpected.equals(t.getType().getCode().substring(20)))
@@ -372,58 +361,43 @@ public class JournalEntriesStepDef extends AbstractStepDef {
List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionsMatch.stream().map(t -> { List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionsMatch.stream().map(t -> {
String transactionId = "L" + t.getId(); String transactionId = "L" + t.getId();
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null; GetJournalEntriesTransactionIdResponse journalEntryDataResponse = null;
try { try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(// Map<String, Object> journalQueryParams = new HashMap<>();
null, // journalQueryParams.put("transactionId", transactionId);
null, // journalQueryParams.put("runningBalance", true);
null, // journalEntryDataResponse = journalEntriesApi().retrieveAll1(journalQueryParams);
null, // } catch (Exception e) {
null, //
null, //
null, //
transactionId, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
log.error("Exception", e); log.error("Exception", e);
} }
return journalEntryDataResponse.body().getPageItems(); return journalEntryDataResponse.getPageItems();
}).collect(Collectors.toList()); }).collect(Collectors.toList());
assertThat(journalLinesActualList.stream().findFirst().get().size()).isZero(); assertThat(journalLinesActualList.stream().findFirst().get().size()).isZero();
} }
public Response<PostJournalEntriesResponse> addManualJournalEntryWithoutExternalAssetOwner(String amount, String date) public PostJournalEntriesResponse addManualJournalEntryWithoutExternalAssetOwner(String amount, String date) throws IOException {
throws IOException {
LocalDate transactionDate = LocalDate.parse(date, FORMATTER_EN); LocalDate transactionDate = LocalDate.parse(date, FORMATTER_EN);
JournalEntryCommand journalEntriesRequest = loanRequestFactory.defaultManualJournalEntryRequest(new BigDecimal(amount)) JournalEntryCommand journalEntriesRequest = loanRequestFactory.defaultManualJournalEntryRequest(new BigDecimal(amount))
.transactionDate(transactionDate); .transactionDate(transactionDate);
Response<PostJournalEntriesResponse> journalEntriesResponse = journalEntriesApi.createGLJournalEntry("", journalEntriesRequest) Map<String, Object> createJournalQueryParams = new HashMap<>();
.execute(); createJournalQueryParams.put("command", "");
PostJournalEntriesResponse journalEntriesResponse = journalEntriesApi().createGLJournalEntry(journalEntriesRequest,
createJournalQueryParams);
testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST, journalEntriesRequest); testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST, journalEntriesRequest);
return journalEntriesResponse; return journalEntriesResponse;
} }
public Response<PostJournalEntriesResponse> addManualJournalEntryWithExternalAssetOwner(String amount, String date, public PostJournalEntriesResponse addManualJournalEntryWithExternalAssetOwner(String amount, String date, String externalAssetOwner)
String externalAssetOwner) throws IOException { throws IOException {
LocalDate transactionDate = LocalDate.parse(date, FORMATTER_EN); LocalDate transactionDate = LocalDate.parse(date, FORMATTER_EN);
JournalEntryCommand journalEntriesRequest = loanRequestFactory JournalEntryCommand journalEntriesRequest = loanRequestFactory
.defaultManualJournalEntryRequest(new BigDecimal(amount), externalAssetOwner).transactionDate(transactionDate); .defaultManualJournalEntryRequest(new BigDecimal(amount), externalAssetOwner).transactionDate(transactionDate);
Response<PostJournalEntriesResponse> journalEntriesResponse = journalEntriesApi.createGLJournalEntry("", journalEntriesRequest) Map<String, Object> createJournalQueryParams = new HashMap<>();
.execute(); createJournalQueryParams.put("command", "");
PostJournalEntriesResponse journalEntriesResponse = journalEntriesApi().createGLJournalEntry(journalEntriesRequest,
createJournalQueryParams);
testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST, journalEntriesRequest); testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST, journalEntriesRequest);
return journalEntriesResponse; 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") @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 { public void createManualJournalEntryWithExternalAssetOwner(String amount, String date) throws IOException {
String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID); String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
Response<PostJournalEntriesResponse> journalEntriesResponse = addManualJournalEntryWithExternalAssetOwner(amount, date, PostJournalEntriesResponse journalEntriesResponse = addManualJournalEntryWithExternalAssetOwner(amount, date,
ownerExternalIdStored); ownerExternalIdStored);
testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE, journalEntriesResponse); 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") @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 { 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); 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") @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 { 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); 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:") @Then("Verify manual Journal entry with External Asset Owner {string} and with the following Journal entries:")
public void checkManualJournalEntry(String externalAssetOwnerEnabled, DataTable table) { public void checkManualJournalEntry(String externalAssetOwnerEnabled, DataTable table) {
Response<PostJournalEntriesResponse> journalEnriesResponse = testContext().get(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE); PostJournalEntriesResponse journalEnriesResponse = testContext().get(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE);
PostJournalEntriesResponse journalEntriesResponseBody = journalEnriesResponse.body(); PostJournalEntriesResponse journalEntriesResponseBody = journalEnriesResponse;
String transactionId = journalEntriesResponseBody.getTransactionId(); String transactionId = journalEntriesResponseBody.getTransactionId();
JournalEntryCommand journalEntriesRequest = testContext().get(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST); JournalEntryCommand journalEntriesRequest = testContext().get(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST);
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null; GetJournalEntriesTransactionIdResponse journalEntryDataResponse = null;
try { try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(// Map<String, Object> journalQueryParams = new HashMap<>();
null, // journalQueryParams.put("transactionId", transactionId);
null, // journalQueryParams.put("runningBalance", true);
null, // journalEntryDataResponse = journalEntriesApi().retrieveAll1(journalQueryParams);
null, // } catch (Exception e) {
null, //
null, //
null, //
transactionId, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
log.error("Exception", e); log.error("Exception", e);
} }
@@ -500,7 +453,7 @@ public class JournalEntriesStepDef extends AbstractStepDef {
} }
boolean containsAnyExpected = false; boolean containsAnyExpected = false;
GetJournalEntriesTransactionIdResponse journalEntryData = journalEntryDataResponse.body(); GetJournalEntriesTransactionIdResponse journalEntryData = journalEntryDataResponse;
List<JournalEntryTransactionItem> journalLinesActual = journalEntryData.getPageItems(); List<JournalEntryTransactionItem> journalLinesActual = journalEntryData.getPageItems();
@@ -511,7 +464,7 @@ public class JournalEntriesStepDef extends AbstractStepDef {
actualValues.add(t.getGlAccountName() == null ? null : t.getGlAccountName()); actualValues.add(t.getGlAccountName() == null ? null : t.getGlAccountName());
actualValues.add("DEBIT".equals(t.getEntryType().getValue()) ? String.valueOf(t.getAmount()) : null); 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("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)) { if (Boolean.parseBoolean(externalAssetOwnerEnabled)) {
actualValues.add(t.getExternalAssetOwner() == null ? null : t.getExternalAssetOwner()); actualValues.add(t.getExternalAssetOwner() == null ? null : t.getExternalAssetOwner());
} }
@@ -521,7 +474,8 @@ public class JournalEntriesStepDef extends AbstractStepDef {
possibleActualValuesList.add(actualValuesList); possibleActualValuesList.add(actualValuesList);
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues)); boolean containsExpectedValues = actualValuesList.stream()
.anyMatch(actualValues -> matchesWithBigDecimalComparison(actualValues, expectedValues));
if (containsExpectedValues) { if (containsExpectedValues) {
containsAnyExpected = true; containsAnyExpected = true;
} }
@@ -18,57 +18,47 @@
*/ */
package org.apache.fineract.test.stepdef.common; package org.apache.fineract.test.stepdef.common;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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.PostRolesRequest;
import org.apache.fineract.client.models.PostRolesResponse; import org.apache.fineract.client.models.PostRolesResponse;
import org.apache.fineract.client.models.PostUsersRequest; import org.apache.fineract.client.models.PostUsersRequest;
import org.apache.fineract.client.models.PostUsersResponse; import org.apache.fineract.client.models.PostUsersResponse;
import org.apache.fineract.client.models.PutRolesRoleIdPermissionsRequest; 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.helper.Utils;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class UserStepDef extends AbstractStepDef { public class UserStepDef extends AbstractStepDef {
private static final String EMAIL = "test@test.com"; private static final String EMAIL = "test@test.com";
@Autowired @Autowired
private RolesApi rolesApi; private FineractFeignClient fineractClient;
@Autowired
private UsersApi usersApi;
private static final String PWD_USER_WITH_ROLE = "1234567890Aa!"; private static final String PWD_USER_WITH_ROLE = "1234567890Aa!";
@When("Admin creates new user with {string} username, {string} role name and given permissions:") @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 { public void createUserWithUsernameAndRoles(String username, String roleName, List<String> permissions) {
Response<List<GetRolesResponse>> retrieveAllRolesResponse = rolesApi.retrieveAllRoles().execute(); ok(() -> fineractClient.roles().retrieveAllRoles());
ErrorHelper.checkSuccessfulApiCall(retrieveAllRolesResponse);
PostRolesRequest newRoleRequest = new PostRolesRequest().name(Utils.randomNameGenerator(roleName, 8)).description(roleName); PostRolesRequest newRoleRequest = new PostRolesRequest().name(Utils.randomNameGenerator(roleName, 8)).description(roleName);
Response<PostRolesResponse> createNewRole = rolesApi.createRole(newRoleRequest).execute(); PostRolesResponse createNewRole = ok(() -> fineractClient.roles().createRole(newRoleRequest));
ErrorHelper.checkSuccessfulApiCall(createNewRole); Long roleId = createNewRole.getResourceId();
Long roleId = createNewRole.body().getResourceId();
Map<String, Boolean> permissionMap = new HashMap<>(); Map<String, Boolean> permissionMap = new HashMap<>();
permissions.forEach(role -> permissionMap.put(role, true)); permissions.forEach(role -> permissionMap.put(role, true));
PutRolesRoleIdPermissionsRequest putRolesRoleIdPermissionsRequest = new PutRolesRoleIdPermissionsRequest() PutRolesRoleIdPermissionsRequest putRolesRoleIdPermissionsRequest = new PutRolesRoleIdPermissionsRequest()
.permissions(permissionMap); .permissions(permissionMap);
Response<PutRolesRoleIdPermissionsResponse> updateRolePermissionResponse = rolesApi ok(() -> fineractClient.roles().updateRolePermissions(roleId, putRolesRoleIdPermissionsRequest));
.updateRolePermissions(roleId, putRolesRoleIdPermissionsRequest).execute();
ErrorHelper.checkSuccessfulApiCall(updateRolePermissionResponse);
String generatedUsername = Utils.randomNameGenerator(username, 8);
PostUsersRequest postUsersRequest = new PostUsersRequest() // PostUsersRequest postUsersRequest = new PostUsersRequest() //
.username(Utils.randomNameGenerator(username, 8)) // .username(generatedUsername) //
.email(EMAIL) // .email(EMAIL) //
.firstname(username) // .firstname(username) //
.lastname(username) // .lastname(username) //
@@ -78,8 +68,9 @@ public class UserStepDef extends AbstractStepDef {
.repeatPassword(PWD_USER_WITH_ROLE) // .repeatPassword(PWD_USER_WITH_ROLE) //
.roles(List.of(roleId)); .roles(List.of(roleId));
Response<PostUsersResponse> createUserResponse = usersApi.create15(postUsersRequest).execute(); PostUsersResponse createUserResponse = ok(() -> fineractClient.users().create15(postUsersRequest));
ErrorHelper.checkSuccessfulApiCall(createUserResponse);
testContext().set(TestContextKey.CREATED_SIMPLE_USER_RESPONSE, createUserResponse); 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; package org.apache.fineract.test.stepdef.datatable;
import static java.util.function.Function.identity; 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 static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.datatable.DataTable; import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils; 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.GetDataTablesResponse;
import org.apache.fineract.client.models.PostColumnHeaderData; import org.apache.fineract.client.models.PostColumnHeaderData;
import org.apache.fineract.client.models.PostDataTablesRequest; import org.apache.fineract.client.models.PostDataTablesRequest;
import org.apache.fineract.client.models.PostDataTablesResponse; import org.apache.fineract.client.models.PostDataTablesResponse;
import org.apache.fineract.client.models.ResultsetColumnHeaderData; 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.DatatableColumnType;
import org.apache.fineract.test.data.datatable.DatatableEntityType; import org.apache.fineract.test.data.datatable.DatatableEntityType;
import org.apache.fineract.test.data.datatable.DatatableNameGenerator; import org.apache.fineract.test.data.datatable.DatatableNameGenerator;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class DatatablesStepDef extends AbstractStepDef { public class DatatablesStepDef extends AbstractStepDef {
@@ -50,39 +49,35 @@ public class DatatablesStepDef extends AbstractStepDef {
public static final String DATATABLE_QUERY_RESPONSE = "DatatableQueryResponse"; public static final String DATATABLE_QUERY_RESPONSE = "DatatableQueryResponse";
@Autowired @Autowired
private DataTablesApi dataTablesApi; private FineractFeignClient fineractClient;
@Autowired @Autowired
private DatatableNameGenerator datatableNameGenerator; private DatatableNameGenerator datatableNameGenerator;
@When("A datatable for {string} is created") @When("A datatable for {string} is created")
public void whenDatatableCreated(String entityTypeStr) throws IOException { public void whenDatatableCreated(String entityTypeStr) {
DatatableEntityType entityType = DatatableEntityType.fromString(entityTypeStr); DatatableEntityType entityType = DatatableEntityType.fromString(entityTypeStr);
List<PostColumnHeaderData> columns = createRandomDatatableColumnsRequest(); List<PostColumnHeaderData> columns = createRandomDatatableColumnsRequest();
PostDataTablesRequest request = createDatatableRequest(entityType, columns); PostDataTablesRequest request = createDatatableRequest(entityType, columns);
Response<PostDataTablesResponse> response = dataTablesApi.createDatatable(request).execute(); PostDataTablesResponse response = ok(() -> fineractClient.dataTables().createDatatable(request, Map.of()));
checkSuccessfulApiCall(response);
PostDataTablesResponse responseBody = response.body(); testContext().set(CREATE_DATATABLE_RESULT_KEY, response);
testContext().set(CREATE_DATATABLE_RESULT_KEY, responseBody); testContext().set(DATATABLE_NAME, response.getResourceIdentifier());
testContext().set(DATATABLE_NAME, responseBody.getResourceIdentifier());
} }
@When("A datatable for {string} is created with the following extra columns:") @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); DatatableEntityType entityType = DatatableEntityType.fromString(entityTypeStr);
List<List<String>> rows = dataTable.asLists(); List<List<String>> rows = dataTable.asLists();
List<List<String>> rowsWithoutHeader = rows.subList(1, rows.size()); List<List<String>> rowsWithoutHeader = rows.subList(1, rows.size());
List<PostColumnHeaderData> columns = createDatatableColumnsRequest(rowsWithoutHeader); List<PostColumnHeaderData> columns = createDatatableColumnsRequest(rowsWithoutHeader);
PostDataTablesRequest request = createDatatableRequest(entityType, columns); PostDataTablesRequest request = createDatatableRequest(entityType, columns);
Response<PostDataTablesResponse> response = dataTablesApi.createDatatable(request).execute(); PostDataTablesResponse response = ok(() -> fineractClient.dataTables().createDatatable(request, Map.of()));
checkSuccessfulApiCall(response);
PostDataTablesResponse responseBody = response.body(); testContext().set(CREATE_DATATABLE_RESULT_KEY, response);
testContext().set(CREATE_DATATABLE_RESULT_KEY, responseBody); testContext().set(DATATABLE_NAME, response.getResourceIdentifier());
testContext().set(DATATABLE_NAME, responseBody.getResourceIdentifier());
} }
private List<PostColumnHeaderData> createDatatableColumnsRequest(List<List<String>> rowsWithoutHeader) { 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") @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); DatatableEntityType entityType = DatatableEntityType.fromString(entityTypeStr);
List<PostColumnHeaderData> columns = createRandomDatatableColumnsRequest(); List<PostColumnHeaderData> columns = createRandomDatatableColumnsRequest();
PostDataTablesRequest request = createDatatableRequest(entityType, columns, true); PostDataTablesRequest request = createDatatableRequest(entityType, columns, true);
Response<PostDataTablesResponse> response = dataTablesApi.createDatatable(request).execute(); PostDataTablesResponse response = ok(() -> fineractClient.dataTables().createDatatable(request, Map.of()));
checkSuccessfulApiCall(response);
PostDataTablesResponse responseBody = response.body(); testContext().set(CREATE_DATATABLE_RESULT_KEY, response);
testContext().set(CREATE_DATATABLE_RESULT_KEY, responseBody); testContext().set(DATATABLE_NAME, response.getResourceIdentifier());
testContext().set(DATATABLE_NAME, responseBody.getResourceIdentifier());
} }
private List<PostColumnHeaderData> createRandomDatatableColumnsRequest() { private List<PostColumnHeaderData> createRandomDatatableColumnsRequest() {
@@ -145,12 +138,10 @@ public class DatatablesStepDef extends AbstractStepDef {
} }
@Then("The following column definitions match:") @Then("The following column definitions match:")
public void thenColumnsMatch(DataTable dataTable) throws IOException { public void thenColumnsMatch(DataTable dataTable) {
String datatableName = testContext().get(DATATABLE_NAME); String datatableName = testContext().get(DATATABLE_NAME);
Response<GetDataTablesResponse> httpResponse = dataTablesApi.getDatatable(datatableName).execute(); GetDataTablesResponse response = ok(() -> fineractClient.dataTables().getDatatable(datatableName, Map.of()));
checkSuccessfulApiCall(httpResponse);
GetDataTablesResponse response = httpResponse.body();
Map<String, ResultsetColumnHeaderData> columnMap = response.getColumnHeaderData().stream() Map<String, ResultsetColumnHeaderData> columnMap = response.getColumnHeaderData().stream()
.collect(Collectors.toMap(ResultsetColumnHeaderData::getColumnName, identity())); .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") @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 { public void thenColum23nsMatch(String columnFilter, String valueFilter) {
Response<String> response = dataTablesApi.queryValues(testContext().get(DATATABLE_NAME), columnFilter, valueFilter, columnFilter) try {
.execute(); fineractClient.dataTables().queryValues(testContext().get(DATATABLE_NAME),
testContext().set(DATATABLE_QUERY_RESPONSE, response); 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}") @Then("The status of the HTTP response should be {int}")
public void thenStatusCodeMatch(int statusCode) { public void thenStatusCodeMatch(int statusCode) {
Response<String> response = testContext().get(DATATABLE_QUERY_RESPONSE); FeignException exception = testContext().get(DATATABLE_QUERY_RESPONSE);
assertThat(response.code()).isEqualTo(statusCode); assertThat(exception.status()).isEqualTo(statusCode);
} }
@Then("The response body should contain the following message: {string}") @Then("The response body should contain the following message: {string}")
public void thenColumnsMatch(String json) throws IOException { public void thenColumnsMatch(String json) {
Response<String> response = testContext().get(DATATABLE_QUERY_RESPONSE); FeignException exception = testContext().get(DATATABLE_QUERY_RESPONSE);
String jsonResponse = response.errorBody().string(); String jsonResponse = exception.responseBodyAsString();
assertThat(jsonResponse).contains(json); assertThat(jsonResponse).contains(json);
} }
} }
@@ -18,16 +18,50 @@
*/ */
package org.apache.fineract.test.stepdef.hook; package org.apache.fineract.test.stepdef.hook;
import static org.awaitility.Awaitility.await;
import io.cucumber.java.Before; 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.apache.fineract.test.messaging.store.EventStore;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.config.JmsListenerEndpointRegistry;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
@Slf4j
public class MessagingHook { public class MessagingHook {
@Autowired @Autowired
private EventStore eventStore; 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() { public void emptyEventStore() {
eventStore.reset(); eventStore.reset();
} }
@@ -18,25 +18,23 @@
*/ */
package org.apache.fineract.test.stepdef.loan; package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import io.cucumber.java.en.When; 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.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.ChargeCalculationType;
import org.apache.fineract.test.data.ChargeProductType; import org.apache.fineract.test.data.ChargeProductType;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class ChargeStepDef extends AbstractStepDef { public class ChargeStepDef extends AbstractStepDef {
@Autowired @Autowired
private ChargesApi chargesApi; private FineractFeignClient fineractClient;
@When("Admin updates charge {string} with {string} calculation type and {double} % of transaction amount") @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(); ChargeRequest disbursementChargeUpdateRequest = new ChargeRequest();
ChargeCalculationType chargeProductTypeValue = ChargeCalculationType.valueOf(chargeCalculationType); ChargeCalculationType chargeProductTypeValue = ChargeCalculationType.valueOf(chargeCalculationType);
disbursementChargeUpdateRequest.chargeCalculationType(chargeProductTypeValue.value).amount(amount).locale("en"); disbursementChargeUpdateRequest.chargeCalculationType(chargeProductTypeValue.value).amount(amount).locale("en");
@@ -44,13 +42,11 @@ public class ChargeStepDef extends AbstractStepDef {
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType); ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeId = chargeProductType.getValue(); Long chargeId = chargeProductType.getValue();
Response<PutChargesChargeIdResponse> responseDisbursementCharge = chargesApi.updateCharge(chargeId, disbursementChargeUpdateRequest) ok(() -> fineractClient.charges().updateCharge(chargeId, disbursementChargeUpdateRequest));
.execute();
ErrorHelper.checkSuccessfulApiCall(responseDisbursementCharge);
} }
@When("Admin updates charge {string} with {string} calculation type and {double} EUR amount") @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(); ChargeRequest disbursementChargeUpdateRequest = new ChargeRequest();
ChargeCalculationType chargeProductTypeValue = ChargeCalculationType.valueOf(chargeCalculationType); ChargeCalculationType chargeProductTypeValue = ChargeCalculationType.valueOf(chargeCalculationType);
disbursementChargeUpdateRequest.chargeCalculationType(chargeProductTypeValue.value).amount(flatAmount).locale("en"); disbursementChargeUpdateRequest.chargeCalculationType(chargeProductTypeValue.value).amount(flatAmount).locale("en");
@@ -58,8 +54,6 @@ public class ChargeStepDef extends AbstractStepDef {
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType); ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeId = chargeProductType.getValue(); Long chargeId = chargeProductType.getValue();
Response<PutChargesChargeIdResponse> responseDisbursementCharge = chargesApi.updateCharge(chargeId, disbursementChargeUpdateRequest) ok(() -> fineractClient.charges().updateCharge(chargeId, disbursementChargeUpdateRequest));
.execute();
ErrorHelper.checkSuccessfulApiCall(responseDisbursementCharge);
} }
} }
@@ -18,17 +18,16 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; 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.InlineJobRequest;
import org.apache.fineract.client.models.InlineJobResponse;
import org.apache.fineract.client.models.PostLoansResponse; 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.EventAssertion;
import org.apache.fineract.test.messaging.event.assetexternalization.LoanAccountCustomSnapshotEvent; import org.apache.fineract.test.messaging.event.assetexternalization.LoanAccountCustomSnapshotEvent;
import org.apache.fineract.test.messaging.event.loan.repayment.LoanRepaymentDueEvent; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class InlineCOBStepDef extends AbstractStepDef { public class InlineCOBStepDef extends AbstractStepDef {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy"); private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy");
@Autowired @Autowired
private InlineJobApi inlineJobApi; private FineractFeignClient fineractClient;
@Autowired @Autowired
private EventAssertion eventAssertion; private EventAssertion eventAssertion;
@Autowired
ExternalEventConfigurationApi eventConfigurationApi;
@When("Admin runs inline COB job for Loan") @When("Admin runs inline COB job for Loan")
public void runInlineCOB() throws IOException { public void runInlineCOB() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
InlineJobRequest inlineJobRequest = new InlineJobRequest().addLoanIdsItem(loanId); InlineJobRequest inlineJobRequest = new InlineJobRequest().addLoanIdsItem(loanId);
Response<InlineJobResponse> inlineJobResponse = inlineJobApi.executeInlineJob("LOAN_COB", inlineJobRequest).execute(); ok(() -> fineractClient.inlineJob().executeInlineJob("LOAN_COB", inlineJobRequest));
ErrorHelper.checkSuccessfulApiCall(inlineJobResponse);
} }
@Then("Loan Repayment Due Business Event is created") @Then("Loan Repayment Due Business Event is created")
public void checkLoanRepaymentDueBusinessEventCreated() { public void checkLoanRepaymentDueBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanRepaymentDueEvent.class, loanId); eventAssertion.assertEventRaised(LoanRepaymentDueEvent.class, loanId);
} }
@Then("Loan Repayment Overdue Business Event is created") @Then("Loan Repayment Overdue Business Event is created")
public void checkLoanRepaymentOverdueBusinessEventCreated() { public void checkLoanRepaymentOverdueBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanRepaymentOverdueEvent.class, loanId); eventAssertion.assertEventRaised(LoanRepaymentOverdueEvent.class, loanId);
} }
@Then("LoanAccountCustomSnapshotBusinessEvent is created with business date {string}") @Then("LoanAccountCustomSnapshotBusinessEvent is created with business date {string}")
public void checkLoanRepaymentDueBusinessEventCreatedWithBusinessDate(String expectedBusinessDate) { public void checkLoanRepaymentDueBusinessEventCreatedWithBusinessDate(String expectedBusinessDate) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
LocalDate expectedBusinessDateParsed = LocalDate.parse(expectedBusinessDate, FORMATTER); LocalDate expectedBusinessDateParsed = LocalDate.parse(expectedBusinessDate, FORMATTER);
eventAssertion.assertEvent(LoanAccountCustomSnapshotEvent.class, loanId).isRaisedOnBusinessDate(expectedBusinessDateParsed); eventAssertion.assertEvent(LoanAccountCustomSnapshotEvent.class, loanId).isRaisedOnBusinessDate(expectedBusinessDateParsed);
@@ -18,6 +18,8 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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 static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
@@ -25,41 +27,31 @@ import io.cucumber.java.en.When;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j; 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.LoanAccountLock;
import org.apache.fineract.client.models.LoanAccountLockResponseDTO; import org.apache.fineract.client.models.LoanAccountLockResponseDTO;
import org.apache.fineract.client.models.OldestCOBProcessedLoanDTO; import org.apache.fineract.client.models.OldestCOBProcessedLoanDTO;
import org.apache.fineract.client.models.PostLoansResponse; 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.helper.ErrorMessageHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j @Slf4j
public class LoanCOBStepDef extends AbstractStepDef { public class LoanCOBStepDef extends AbstractStepDef {
@Autowired @Autowired
private LoanCobCatchUpApi loanCobCatchUpApi; private FineractFeignClient fineractClient;
@Autowired
private LoanAccountLockApi loanAccountLockApi;
@Autowired
private DefaultApi defaultApi;
@Then("The cobProcessedDate of the oldest loan processed by COB is more than 1 day earlier than cobBusinessDate") @Then("The cobProcessedDate of the oldest loan processed by COB is more than 1 day earlier than cobBusinessDate")
public void checkOldestCOBProcessed() throws IOException { public void checkOldestCOBProcessed() throws IOException {
Response<OldestCOBProcessedLoanDTO> response = loanCobCatchUpApi.getOldestCOBProcessedLoan().execute(); OldestCOBProcessedLoanDTO response = ok(() -> fineractClient.loanCobCatchUp().getOldestCOBProcessedLoan());
ErrorHelper.checkSuccessfulApiCall(response);
LocalDate cobDate = response.body().getCobBusinessDate(); LocalDate cobDate = response.getCobBusinessDate();
LocalDate cobDateMinusOne = cobDate.minusDays(1); LocalDate cobDateMinusOne = cobDate.minusDays(1);
LocalDate cobProcessedDate = response.body().getCobProcessedDate(); LocalDate cobProcessedDate = response.getCobProcessedDate();
log.debug("cobDateMinusOne: {}", cobDateMinusOne); log.debug("cobDateMinusOne: {}", cobDateMinusOne);
log.debug("cobProcessedDate: {}", cobProcessedDate); log.debug("cobProcessedDate: {}", cobProcessedDate);
@@ -69,23 +61,23 @@ public class LoanCOBStepDef extends AbstractStepDef {
@Then("There are no locked loan accounts") @Then("There are no locked loan accounts")
public void listOfLockedLoansEmpty() throws IOException { public void listOfLockedLoansEmpty() throws IOException {
Response<LoanAccountLockResponseDTO> response = loanAccountLockApi.retrieveLockedAccounts(0, 1000).execute(); LoanAccountLockResponseDTO response = ok(
ErrorHelper.checkSuccessfulApiCall(response); () -> 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); assertThat(size).as(ErrorMessageHelper.listOfLockedLoansNotEmpty(response)).isEqualTo(0);
log.debug("Size of List of the locked loans: {}", size); log.debug("Size of List of the locked loans: {}", size);
} }
@Then("The loan account is not locked") @Then("The loan account is not locked")
public void loanIsNotInListOfLockedLoans() throws IOException { public void loanIsNotInListOfLockedLoans() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long targetLoanId = loanResponse.body().getLoanId(); Long targetLoanId = loanResponse.getLoanId();
Response<LoanAccountLockResponseDTO> response = loanAccountLockApi.retrieveLockedAccounts(0, 1000).execute(); LoanAccountLockResponseDTO response = ok(
ErrorHelper.checkSuccessfulApiCall(response); () -> 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()// boolean contains = content.stream()//
.map(LoanAccountLock::getLoanId)// .map(LoanAccountLock::getLoanId)//
.anyMatch(targetLoanId::equals);// .anyMatch(targetLoanId::equals);//
@@ -95,19 +87,17 @@ public class LoanCOBStepDef extends AbstractStepDef {
@When("Admin places a lock on loan account with an error message") @When("Admin places a lock on loan account with an error message")
public void placeLockOnLoanAccountWithErrorMessage() throws IOException { public void placeLockOnLoanAccountWithErrorMessage() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<Void> response = defaultApi.placeLockOnLoanAccount(loanId, "LOAN_COB_CHUNK_PROCESSING", "TestError").execute(); executeVoid(() -> fineractClient.defaultApi().placeLockOnLoanAccount(loanId, "LOAN_COB_CHUNK_PROCESSING", "TestError"));
ErrorHelper.checkSuccessfulApiCall(response);
} }
@When("Admin places a lock on loan account WITHOUT an error message") @When("Admin places a lock on loan account WITHOUT an error message")
public void placeLockOnLoanAccountNoErrorMessage() throws IOException { public void placeLockOnLoanAccountNoErrorMessage() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<Void> response = defaultApi.placeLockOnLoanAccount(loanId, "LOAN_COB_CHUNK_PROCESSING").execute(); executeVoid(() -> fineractClient.defaultApi().placeLockOnLoanAccount(loanId, "LOAN_COB_CHUNK_PROCESSING", ""));
ErrorHelper.checkSuccessfulApiCall(response);
} }
} }
@@ -18,22 +18,27 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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 io.cucumber.java.en.Then;
import java.io.IOException; import java.math.BigDecimal;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j; 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.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions; import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.PostLoansResponse; import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.LoansApi; import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.messaging.EventAssertion; import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanCapitalizedIncomeAmortizationTransactionCreatedEvent; import org.apache.fineract.test.messaging.event.loan.transaction.LoanCapitalizedIncomeAmortizationTransactionCreatedEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j @Slf4j
public class LoanCapitalizedIncomeStepDef extends AbstractStepDef { public class LoanCapitalizedIncomeStepDef extends AbstractStepDef {
@@ -42,20 +47,20 @@ public class LoanCapitalizedIncomeStepDef extends AbstractStepDef {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT); private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
@Autowired @Autowired
LoansApi loansApi; FineractFeignClient fineractClient;
@Autowired @Autowired
EventAssertion eventAssertion; EventAssertion eventAssertion;
@Then("Loan Capitalized Income Amortization Transaction Created Business Event is created on {string}") @Then("Loan Capitalized Income Amortization Transaction Created Business Event is created on {string}")
public void checkLoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEventCreated(String date) throws IOException { public void checkLoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEventCreated(String date) {
Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.body().getLoanId(); long loanId = loanCreateResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute(); GetLoansLoanIdResponse loanDetailsResponse = ok(
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse); () -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "transactions")));
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
GetLoansLoanIdTransactions capitalizedIncomeAmortizationTransaction = transactions.stream() GetLoansLoanIdTransactions capitalizedIncomeAmortizationTransaction = transactions.stream()
.filter(t -> date.equals(FORMATTER.format(t.getDate())) && "Capitalized Income Amortization".equals(t.getType().getValue())) .filter(t -> date.equals(FORMATTER.format(t.getDate())) && "Capitalized Income Amortization".equals(t.getType().getValue()))
.reduce((first, second) -> second).orElseThrow( .reduce((first, second) -> second).orElseThrow(
@@ -66,4 +71,90 @@ public class LoanCapitalizedIncomeStepDef extends AbstractStepDef {
capitalizedIncomeAmortizationTransactionId); 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; 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.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException; import java.io.IOException;
@@ -28,24 +28,20 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j; 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.BusinessDateResponse;
import org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData; import org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions; import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse; 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.PostLoansLoanIdTransactionsTransactionIdRequest;
import org.apache.fineract.client.models.PostLoansResponse; 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.data.ChargeProductType;
import org.apache.fineract.test.factory.LoanRequestFactory; 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.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse; import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.messaging.event.EventCheckHelper; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j @Slf4j
public class LoanChargeAdjustmentStepDef extends AbstractStepDef { public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy"; 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 @Autowired
private EventCheckHelper eventCheckHelper; private EventCheckHelper eventCheckHelper;
@Autowired @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}") @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) public void makeLoanChargeAdjustment(String chargeTypeEnum, String date, Double transactionAmount, String externalId)
throws IOException { throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "charges", "", "").execute(); GetLoansLoanIdResponse loanDetailsResponse = ok(
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse); () -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "charges")));
Long transactionId = getTransactionIdForLastChargeMetConditions(chargeTypeEnum, date, loanDetailsResponse); Long transactionId = getTransactionIdForLastChargeMetConditions(chargeTypeEnum, date, loanDetailsResponse);
makeChargeAdjustmentCall(loanId, transactionId, externalId, transactionAmount); 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") @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 { public void loanChargeAdjustmentFailedOnWrongAmount(String chargeTypeEnum, String date, double amount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "charges", "", "").execute(); GetLoansLoanIdResponse loanDetailsResponse = ok(
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse); () -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "charges")));
Long transactionId = getTransactionIdForLastChargeMetConditions(chargeTypeEnum, date, loanDetailsResponse); Long transactionId = getTransactionIdForLastChargeMetConditions(chargeTypeEnum, date, loanDetailsResponse);
PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest().amount(amount) PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest().amount(amount)
.externalId(""); .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; Integer httpStatusCodeExpected = 403;
String developerMessageExpected = "Transaction amount cannot be higher than the available charge amount for adjustment: 7.000000"; String developerMessageExpected = "Transaction amount cannot be higher than the available charge amount for adjustment: 7.000000";
assertThat(httpStatusCodeActual) try {
.as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected)) fineractClient.loanCharges().executeLoanCharge2(loanId, transactionId, chargeAdjustmentRequest,
.isEqualTo(httpStatusCodeExpected); Map.<String, Object>of("command", "adjustment"));
assertThat(developerMessageActual) throw new AssertionError("Expected FeignException but request succeeded");
.as(ErrorMessageHelper.wrongErrorMessageInFailedChargeAdjustment(developerMessageActual, developerMessageExpected)) } catch (FeignException e) {
.isEqualTo(developerMessageExpected); ErrorResponse errorResponse = ErrorResponse.fromFeignException(e);
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
String developerMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
log.debug("Error code: {}", httpStatusCodeActual); assertThat(httpStatusCodeActual)
log.debug("Error message: {}", developerMessageActual); .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") @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 { public void loanChargeAdjustmentUndo(String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute(); GetLoansLoanIdResponse loanDetailsResponse = ok(
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse); () -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "transactions")));
Long transactionId = getTransactionIdForTransactionMetConditions(transactionDate, transactionAmount, loanDetailsResponse); Long transactionId = getTransactionIdForTransactionMetConditions(transactionDate, transactionAmount, loanDetailsResponse);
Response<List<BusinessDateResponse>> businessDateResponse = businessDateManagementApi.getBusinessDates().execute(); BusinessDateResponse businessDateResponse = ok(
LocalDate businessDate = businessDateResponse.body().get(0).getDate(); () -> fineractClient.businessDateManagement().getBusinessDate("BUSINESS_DATE", Map.of()));
LocalDate businessDate = businessDateResponse.getDate();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String businessDateActual = formatter.format(businessDate); String businessDateActual = formatter.format(businessDate);
PostLoansLoanIdTransactionsTransactionIdRequest chargeAdjustmentUndoRequest = LoanRequestFactory PostLoansLoanIdTransactionsTransactionIdRequest chargeAdjustmentUndoRequest = LoanRequestFactory
.defaultChargeAdjustmentTransactionUndoRequest().transactionDate(businessDateActual); .defaultChargeAdjustmentTransactionUndoRequest().transactionDate(businessDateActual);
Response<PostLoansLoanIdTransactionsResponse> chargeAdjustmentUndoResponse = loanTransactionsApi ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId, transactionId, chargeAdjustmentUndoRequest,
.adjustLoanTransaction(loanId, transactionId, chargeAdjustmentUndoRequest, "").execute(); Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(chargeAdjustmentUndoResponse);
} }
@Then("Charge adjustment response has the subResourceExternalId") @Then("Charge adjustment response has the subResourceExternalId")
public void checkChargeAdjustmentResponse() { public void checkChargeAdjustmentResponse() {
final Response<PostLoansLoanIdChargesChargeIdResponse> response = testContext().get(TestContextKey.LOAN_CHARGE_ADJUSTMENT_RESPONSE); final PostLoansLoanIdChargesChargeIdResponse response = testContext().get(TestContextKey.LOAN_CHARGE_ADJUSTMENT_RESPONSE);
final PostLoansLoanIdChargesChargeIdResponse body = response.body(); assertThat(response.getSubResourceExternalId()).isNotNull();
assertThat(body.getSubResourceExternalId()).isNotNull();
} }
private Long getTransactionIdForTransactionMetConditions(String transactionDate, double transactionAmount, private Long getTransactionIdForTransactionMetConditions(String transactionDate, double transactionAmount,
Response<GetLoansLoanIdResponse> loanDetailsResponse) { GetLoansLoanIdResponse loanDetailsResponse) {
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.getTransactions();
GetLoansLoanIdTransactions transactionMetConditions = new GetLoansLoanIdTransactions(); GetLoansLoanIdTransactions transactionMetConditions = new GetLoansLoanIdTransactions();
for (int i = 0; i < transactions.size(); i++) { for (int i = 0; i < transactions.size(); i++) {
LocalDate date = transactions.get(i).getDate(); LocalDate date = transactions.get(i).getDate();
@@ -176,16 +165,15 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest() PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest()
.amount(transactionAmount).externalId(externalId); .amount(transactionAmount).externalId(externalId);
Response<PostLoansLoanIdChargesChargeIdResponse> chargeAdjustmentResponse = loanChargesApi PostLoansLoanIdChargesChargeIdResponse chargeAdjustmentResponse = ok(() -> fineractClient.loanCharges().executeLoanCharge2(loanId,
.executeLoanCharge2(loanId, transactionId, chargeAdjustmentRequest, "adjustment").execute(); transactionId, chargeAdjustmentRequest, Map.<String, Object>of("command", "adjustment")));
testContext().set(TestContextKey.LOAN_CHARGE_ADJUSTMENT_RESPONSE, chargeAdjustmentResponse); testContext().set(TestContextKey.LOAN_CHARGE_ADJUSTMENT_RESPONSE, chargeAdjustmentResponse);
ErrorHelper.checkSuccessfulApiCall(chargeAdjustmentResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId); eventCheckHelper.loanBalanceChangedEventCheck(loanId);
} }
private Long getTransactionIdForLastChargeMetConditions(String chargeTypeEnum, String date, private Long getTransactionIdForLastChargeMetConditions(String chargeTypeEnum, String date,
Response<GetLoansLoanIdResponse> loanDetailsResponse) { GetLoansLoanIdResponse loanDetailsResponse) {
List<GetLoansLoanIdLoanChargeData> charges = loanDetailsResponse.body().getCharges(); List<GetLoansLoanIdLoanChargeData> charges = loanDetailsResponse.getCharges();
ChargeProductType chargeType = ChargeProductType.valueOf(chargeTypeEnum); ChargeProductType chargeType = ChargeProductType.valueOf(chargeTypeEnum);
Long chargeProductId = chargeType.getValue(); Long chargeProductId = chargeType.getValue();
@@ -18,14 +18,18 @@
*/ */
package org.apache.fineract.test.stepdef.loan; package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1; import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionEnumDataV1; 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.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions; import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse; 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.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
import org.apache.fineract.client.models.PostLoansResponse; 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.DefaultPaymentType;
import org.apache.fineract.test.data.paymenttype.PaymentTypeResolver; import org.apache.fineract.test.data.paymenttype.PaymentTypeResolver;
import org.apache.fineract.test.factory.LoanRequestFactory; 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.EventAssertion;
import org.apache.fineract.test.messaging.event.EventCheckHelper; import org.apache.fineract.test.messaging.event.EventCheckHelper;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanChargebackTransactionEvent; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class LoanChargeBackStepDef extends AbstractStepDef { public class LoanChargeBackStepDef extends AbstractStepDef {
@Autowired @Autowired
private LoanTransactionsApi loanTransactionsApi; private FineractFeignClient fineractClient;
@Autowired
private LoansApi loansApi;
@Autowired @Autowired
private EventAssertion eventAssertion; private EventAssertion eventAssertion;
@@ -70,22 +67,22 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
@When("Admin makes {string} chargeback with {double} EUR transaction amount") @When("Admin makes {string} chargeback with {double} EUR transaction amount")
public void makeLoanChargeback(String repaymentType, double transactionAmount) throws IOException { public void makeLoanChargeback(String repaymentType, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE); PostLoansLoanIdTransactionsResponse repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Long transactionId = Long.valueOf(repaymentResponse.body().getResourceId()); Long transactionId = Long.valueOf(repaymentResponse.getResourceId());
makeChargebackCall(loanId, transactionId, repaymentType, transactionAmount); makeChargebackCall(loanId, transactionId, repaymentType, transactionAmount);
} }
@When("Admin makes {string} chargeback with {double} EUR transaction amount for Payment nr. {double}") @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 { public void makeLoanChargebackForPayment(String repaymentType, double transactionAmount, double paymentNr) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "transactions")));
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
List<Long> transactionIdList = new ArrayList<>(); List<Long> transactionIdList = new ArrayList<>();
for (GetLoansLoanIdTransactions f : transactions) { 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}") @When("Admin makes {string} chargeback with {double} EUR transaction amount for Downpayment nr. {double}")
public void makeLoanChargebackForDownpaymentayment(String repaymentType, double transactionAmount, double paymentNr) public void makeLoanChargebackForDownpaymentayment(String repaymentType, double transactionAmount, double paymentNr)
throws IOException { throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "transactions")));
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
List<Long> transactionIdList = new ArrayList<>(); List<Long> transactionIdList = new ArrayList<>();
for (GetLoansLoanIdTransactions f : transactions) { 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}") @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 { public void makeLoanChargebackForMIR(String repaymentType, double transactionAmount, double paymentNr) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "transactions")));
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions(); List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
List<Long> transactionIdList = new ArrayList<>(); List<Long> transactionIdList = new ArrayList<>();
for (GetLoansLoanIdTransactions f : transactions) { for (GetLoansLoanIdTransactions f : transactions) {
@@ -151,16 +148,14 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest chargebackRequest = LoanRequestFactory.defaultChargebackRequest() PostLoansLoanIdTransactionsTransactionIdRequest chargebackRequest = LoanRequestFactory.defaultChargebackRequest()
.paymentTypeId(paymentTypeValue).transactionAmount(transactionAmount); .paymentTypeId(paymentTypeValue).transactionAmount(transactionAmount);
Response<PostLoansLoanIdTransactionsResponse> chargebackResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse chargebackResponse = ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
.adjustLoanTransaction(loanId, transactionId, chargebackRequest, "chargeback").execute(); transactionId, chargebackRequest, Map.of("command", "chargeback")));
testContext().set(TestContextKey.LOAN_CHARGEBACK_RESPONSE, chargebackResponse); testContext().set(TestContextKey.LOAN_CHARGEBACK_RESPONSE, chargebackResponse);
ErrorHelper.checkSuccessfulApiCall(chargebackResponse);
checkEvents(chargebackResponse); checkEvents(chargebackResponse);
} }
private void checkEvents(Response<PostLoansLoanIdTransactionsResponse> chargebackResponse) throws IOException { private void checkEvents(PostLoansLoanIdTransactionsResponse chargebackResponse) throws IOException {
PostLoansLoanIdTransactionsResponse responseBody = chargebackResponse.body(); PostLoansLoanIdTransactionsResponse responseBody = chargebackResponse;
Long loanId = responseBody.getLoanId(); Long loanId = responseBody.getLoanId();
eventCheckHelper.loanBalanceChangedEventCheck(loanId); eventCheckHelper.loanBalanceChangedEventCheck(loanId);
@@ -174,11 +169,8 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
long transactionId = Long.valueOf(chargebackResponse.getResourceId()); long transactionId = Long.valueOf(chargebackResponse.getResourceId());
// retrieve transaction details // retrieve transaction details
Response<GetLoansLoanIdTransactionsTransactionIdResponse> transactionResponse = loanTransactionsApi GetLoansLoanIdTransactionsTransactionIdResponse transactionResponseBody = ok(
.retrieveTransaction(loanId, transactionId, "").execute(); () -> fineractClient.loanTransactions().retrieveTransaction(loanId, transactionId, Map.of()));
ErrorHelper.checkSuccessfulApiCall(transactionResponse);
GetLoansLoanIdTransactionsTransactionIdResponse transactionResponseBody = transactionResponse.body();
// Get transaction type from response // Get transaction type from response
GetLoansType transactionType = transactionResponseBody.getType(); GetLoansType transactionType = transactionResponseBody.getType();
@@ -18,9 +18,9 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable; import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.And; import io.cucumber.java.en.And;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
@@ -29,8 +29,11 @@ import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.avro.loan.v1.LoanChargeDataV1; 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.GetLoansLoanIdChargesChargeIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions; 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.PostLoansResponse;
import org.apache.fineract.client.models.PutChargeTransactionChangesRequest; import org.apache.fineract.client.models.PutChargeTransactionChangesRequest;
import org.apache.fineract.client.models.PutChargeTransactionChangesResponse; 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.ChargeProductType;
import org.apache.fineract.test.data.ErrorMessageType; import org.apache.fineract.test.data.ErrorMessageType;
import org.apache.fineract.test.factory.LoanChargeRequestFactory; 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.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse; import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.messaging.EventAssertion; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j @Slf4j
public class LoanChargeStepDef extends AbstractStepDef { 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 DEFAULT_DATE_FORMAT = "dd MMMM yyyy";
public static final String DATE_FORMAT_EVENTS = "yyyy-MM-dd"; public static final String DATE_FORMAT_EVENTS = "yyyy-MM-dd";
public static final Double DEFAULT_CHARGE_FEE_FLAT = 10D; public static final Double DEFAULT_CHARGE_FEE_FLAT = 10D;
private static final Gson GSON = new JSON().getGson();
@Autowired @Autowired
private LoanChargesApi loanChargesApi; private FineractFeignClient fineractClient;
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private LoansApi loansApi;
@Autowired @Autowired
private EventAssertion eventAssertion; private EventAssertion eventAssertion;
@Autowired @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") @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 { public void addChargeDueDate(String chargeType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType); ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeTypeId = chargeProductType.getValue(); Long chargeTypeId = chargeProductType.getValue();
@@ -97,9 +90,8 @@ public class LoanChargeStepDef extends AbstractStepDef {
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId) PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.dueDate(transactionDate).amount(transactionAmount); .dueDate(transactionDate).amount(transactionAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "") PostLoansLoanIdChargesResponse loanChargeResponse = ok(
.execute(); () -> fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse); testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse);
testContext().set(TestContextKey.ADD_NSF_FEE_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") @When("Admin adds {string} charge with {double} % of transaction amount")
public void addChargePercentage(String chargeType, double transactionPercentageAmount) throws IOException { public void addChargePercentage(String chargeType, double transactionPercentageAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType); ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeTypeId = chargeProductType.getValue(); Long chargeTypeId = chargeProductType.getValue();
@@ -123,17 +115,16 @@ public class LoanChargeStepDef extends AbstractStepDef {
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId) PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.amount(transactionPercentageAmount); .amount(transactionPercentageAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "") PostLoansLoanIdChargesResponse loanChargeResponse = ok(
.execute(); () -> fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse); testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse);
} }
@When("Admin adds {string} installment charge with {double} amount") @When("Admin adds {string} installment charge with {double} amount")
public void addInstallmentFeeCharge(final String chargeType, final double amount) throws IOException { public void addInstallmentFeeCharge(final String chargeType, final double amount) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse.body() != null; assert loanResponse != null;
final long loanId = loanResponse.body().getLoanId(); final long loanId = loanResponse.getLoanId();
final ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType); final ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
final Long chargeTypeId = chargeProductType.getValue(); final Long chargeTypeId = chargeProductType.getValue();
@@ -148,39 +139,40 @@ public class LoanChargeStepDef extends AbstractStepDef {
final PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest() final PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
.chargeId(chargeTypeId).amount(amount); .chargeId(chargeTypeId).amount(amount);
final Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi final PostLoansLoanIdChargesResponse loanChargeResponse = ok(
.executeLoanCharge(loanId, loanIdChargesRequest, "").execute(); () -> fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_INSTALLMENT_FEE_CHARGE_RESPONSE, loanChargeResponse); 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") @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 { public void addInstallmentFeeChargeFails(final String chargeType, final double amount) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse.body() != null; assert loanResponse != null;
final long loanId = loanResponse.body().getLoanId(); final long loanId = loanResponse.getLoanId();
final ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType); final ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
final Long chargeTypeId = chargeProductType.getValue(); final Long chargeTypeId = chargeProductType.getValue();
final PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest() final PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
.chargeId(chargeTypeId).amount(amount); .chargeId(chargeTypeId).amount(amount);
final Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi try {
.executeLoanCharge(loanId, loanIdChargesRequest, "").execute(); fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of());
testContext().set(TestContextKey.ADD_INSTALLMENT_FEE_CHARGE_RESPONSE, loanChargeResponse); throw new AssertionError("Expected FeignException but request succeeded");
final ErrorResponse errorDetails = ErrorResponse.from(loanChargeResponse); } catch (FeignException e) {
assertThat(errorDetails.getHttpStatusCode()).isEqualTo(400); final ErrorResponse errorDetails = ErrorResponse.fromFeignException(e);
assertThat(errorDetails.getSingleError().getDeveloperMessage()) assertThat(errorDetails.getHttpStatusCode()).isEqualTo(400);
.isEqualTo(chargeProductType.equals(ChargeProductType.LOAN_INSTALLMENT_FEE_PERCENTAGE_INTEREST) String expectedMessage = chargeTypeId.equals(ChargeProductType.LOAN_INSTALLMENT_FEE_PERCENTAGE_INTEREST.getValue())
? ErrorMessageHelper.addInstallmentFeeInterestPercentageChargeFailure() ? ErrorMessageHelper.addInstallmentFeeInterestPercentageChargeFailure()
: ErrorMessageHelper.addInstallmentFeePrincipalPercentageChargeFailure()); : 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") @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 { public void addChargeDueDateOnChargedOff(String chargeType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType); ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeTypeId = chargeProductType.getValue(); Long chargeTypeId = chargeProductType.getValue();
@@ -188,27 +180,28 @@ public class LoanChargeStepDef extends AbstractStepDef {
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId) PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.dueDate(transactionDate).amount(transactionAmount); .dueDate(transactionDate).amount(transactionAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "") try {
.execute(); fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of());
testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse); throw new AssertionError("Expected FeignException but request succeeded");
ErrorResponse errorDetails = ErrorResponse.from(loanChargeResponse); } catch (FeignException e) {
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addChargeForChargeOffLoanCodeMsg()).isEqualTo(403); ErrorResponse errorDetails = ErrorResponse.fromFeignException(e);
assertThat(errorDetails.getSingleError().getDeveloperMessage()) assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addChargeForChargeOffLoanCodeMsg()).isEqualTo(403);
.isEqualTo(ErrorMessageHelper.addChargeForChargeOffLoanFailure(loanId)); assertThat(errorDetails.getSingleError().getDeveloperMessage())
.contains(ErrorMessageHelper.addChargeForChargeOffLoanFailure(loanId));
}
} }
@And("Admin adds a {double} % Processing charge to the loan with {string} locale on date: {string}") @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 { public void addProcessingFee(double chargeAmount, String locale, String date) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest() PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
.chargeId(ChargeProductType.LOAN_PERCENTAGE_PROCESSING_FEE.value).amount(chargeAmount).dueDate(date) .chargeId(ChargeProductType.LOAN_PERCENTAGE_PROCESSING_FEE.value).amount(chargeAmount).dueDate(date)
.dateFormat(DEFAULT_DATE_FORMAT).locale(locale); .dateFormat(DEFAULT_DATE_FORMAT).locale(locale);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "") PostLoansLoanIdChargesResponse loanChargeResponse = ok(
.execute(); () -> fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_PROCESSING_FEE_RESPONSE, loanChargeResponse); testContext().set(TestContextKey.ADD_PROCESSING_FEE_RESPONSE, loanChargeResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId); 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") @And("Admin adds an NSF fee because of payment bounce with {string} transaction date")
public void addNSFfee(String date) throws IOException { public void addNSFfee(String date) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest() PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
.chargeId(ChargeProductType.LOAN_NSF_FEE.value).amount(DEFAULT_CHARGE_FEE_FLAT).dueDate(date) .chargeId(ChargeProductType.LOAN_NSF_FEE.value).amount(DEFAULT_CHARGE_FEE_FLAT).dueDate(date)
.dateFormat(DEFAULT_DATE_FORMAT); .dateFormat(DEFAULT_DATE_FORMAT);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "") PostLoansLoanIdChargesResponse loanChargeResponse = ok(
.execute(); () -> fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_NSF_FEE_RESPONSE, loanChargeResponse); testContext().set(TestContextKey.ADD_NSF_FEE_RESPONSE, loanChargeResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId); eventCheckHelper.loanBalanceChangedEventCheck(loanId);
} }
@And("Admin waives charge") @And("Admin waives charge")
public void waiveCharge() throws IOException { public void waiveCharge() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = testContext().get(TestContextKey.ADD_NSF_FEE_RESPONSE); PostLoansLoanIdChargesResponse loanChargeResponse = testContext().get(TestContextKey.ADD_NSF_FEE_RESPONSE);
Long chargeId = Long.valueOf(loanChargeResponse.body().getResourceId()); Long chargeId = Long.valueOf(loanChargeResponse.getResourceId());
PostLoansLoanIdChargesChargeIdRequest waiveRequest = new PostLoansLoanIdChargesChargeIdRequest(); PostLoansLoanIdChargesChargeIdRequest waiveRequest = new PostLoansLoanIdChargesChargeIdRequest();
Response<PostLoansLoanIdChargesChargeIdResponse> waiveResponse = loanChargesApi PostLoansLoanIdChargesChargeIdResponse waiveResponse = ok(() -> fineractClient.loanCharges().executeLoanCharge2(loanId, chargeId,
.executeLoanCharge2(loanId, chargeId, waiveRequest, "waive").execute(); waiveRequest, Map.<String, Object>of("command", "waive")));
ErrorHelper.checkSuccessfulApiCall(waiveResponse);
testContext().set(TestContextKey.WAIVE_CHARGE_RESPONSE, waiveResponse); testContext().set(TestContextKey.WAIVE_CHARGE_RESPONSE, waiveResponse);
} }
@And("Admin waives due date charge") @And("Admin waives due date charge")
public void waiveDueDateCharge() throws IOException { public void waiveDueDateCharge() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = testContext().get(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE); PostLoansLoanIdChargesResponse loanChargeResponse = testContext().get(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE);
Long chargeId = Long.valueOf(loanChargeResponse.body().getResourceId()); Long chargeId = Long.valueOf(loanChargeResponse.getResourceId());
PostLoansLoanIdChargesChargeIdRequest waiveRequest = new PostLoansLoanIdChargesChargeIdRequest(); PostLoansLoanIdChargesChargeIdRequest waiveRequest = new PostLoansLoanIdChargesChargeIdRequest();
Response<PostLoansLoanIdChargesChargeIdResponse> waiveResponse = loanChargesApi PostLoansLoanIdChargesChargeIdResponse waiveResponse = ok(() -> fineractClient.loanCharges().executeLoanCharge2(loanId, chargeId,
.executeLoanCharge2(loanId, chargeId, waiveRequest, "waive").execute(); waiveRequest, Map.<String, Object>of("command", "waive")));
ErrorHelper.checkSuccessfulApiCall(waiveResponse);
testContext().set(TestContextKey.WAIVE_CHARGE_RESPONSE, waiveResponse); testContext().set(TestContextKey.WAIVE_CHARGE_RESPONSE, waiveResponse);
} }
@And("Admin makes waive undone for charge") @And("Admin makes waive undone for charge")
public void undoWaiveForCharge() throws IOException { public void undoWaiveForCharge() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions(); () -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")));
List<GetLoansLoanIdTransactions> transactions = loanDetails.getTransactions();
Long transactionId = 0L; final Long transactionId = transactions.stream().filter(t -> "loanTransactionType.waiveCharges".equals(t.getType().getCode()))
for (GetLoansLoanIdTransactions f : transactions) { .findFirst().map(GetLoansLoanIdTransactions::getId).orElse(0L);
String code = f.getType().getCode();
if (code.equals("loanTransactionType.waiveCharges")) {
transactionId = f.getId();
}
}
PutChargeTransactionChangesRequest undoWaiveRequest = new PutChargeTransactionChangesRequest(); PutChargeTransactionChangesRequest undoWaiveRequest = new PutChargeTransactionChangesRequest();
Response<PutChargeTransactionChangesResponse> undoWaiveResponse = loanTransactionsApi PutChargeTransactionChangesResponse undoWaiveResponse = ok(
.undoWaiveCharge(loanId, transactionId, undoWaiveRequest).execute(); () -> fineractClient.loanTransactions().undoWaiveCharge(loanId, transactionId, undoWaiveRequest));
ErrorHelper.checkSuccessfulApiCall(undoWaiveResponse);
testContext().set(TestContextKey.UNDO_WAIVE_RESPONSE, undoWaiveResponse); testContext().set(TestContextKey.UNDO_WAIVE_RESPONSE, undoWaiveResponse);
} }
@Then("Charge is successfully added to the loan") @Then("Charge is successfully added to the loan")
public void loanChargeStatus() throws IOException { 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).as("Charge response should not be null").isNotNull();
assertThat(response.code()).as(ErrorMessageHelper.requestFailedWithCode(response)).isEqualTo(200); assertThat(response.getResourceId()).as("Charge resource ID should be present").isNotNull();
} }
@Then("Charge is successfully added to the loan with {float} EUR") @Then("Charge is successfully added to the loan with {float} EUR")
public void checkLoanChargeAmount(float chargeAmount) throws IOException { public void checkLoanChargeAmount(float chargeAmount) throws IOException {
Response<PostLoansLoanIdChargesResponse> response = testContext().get(TestContextKey.ADD_PROCESSING_FEE_RESPONSE); PostLoansLoanIdChargesResponse response = testContext().get(TestContextKey.ADD_PROCESSING_FEE_RESPONSE);
Response<GetLoansLoanIdChargesChargeIdResponse> loanChargeAmount = loanChargesApi GetLoansLoanIdChargesChargeIdResponse loanChargeAmount = ok(
.retrieveLoanCharge(response.body().getLoanId(), Long.valueOf(response.body().getResourceId())).execute(); () -> fineractClient.loanCharges().retrieveLoanCharge(response.getLoanId(), Long.valueOf(response.getResourceId())));
assertThat(loanChargeAmount.getAmount()).as("Charge amount is wrong").isEqualByComparingTo(Double.valueOf(chargeAmount));
ErrorHelper.checkSuccessfulApiCall(response);
assertThat(loanChargeAmount.body().getAmount()).as("Charge amount is wrong").isEqualTo(chargeAmount);
} }
private void addChargeEventCheck(Response<PostLoansLoanIdChargesResponse> loanChargeResponse) throws IOException { private void addChargeEventCheck(PostLoansLoanIdChargesResponse loanChargeResponse) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT_EVENTS); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT_EVENTS);
Response<GetLoansLoanIdChargesChargeIdResponse> chargeDetails = loanChargesApi GetLoansLoanIdChargesChargeIdResponse chargeDetails = ok(
.retrieveLoanCharge(loanChargeResponse.body().getLoanId(), loanChargeResponse.body().getResourceId()).execute(); () -> fineractClient.loanCharges().retrieveLoanCharge(loanChargeResponse.getLoanId(), loanChargeResponse.getResourceId()));
GetLoansLoanIdChargesChargeIdResponse body = chargeDetails.body(); GetLoansLoanIdChargesChargeIdResponse body = chargeDetails;
eventAssertion.assertEvent(LoanAddChargeEvent.class, loanChargeResponse.body().getResourceId()) eventAssertion.assertEvent(LoanAddChargeEvent.class, loanChargeResponse.getResourceId()).extractingData(LoanChargeDataV1::getName)
.extractingData(LoanChargeDataV1::getName).isEqualTo(body.getName()).extractingBigDecimal(LoanChargeDataV1::getAmount) .isEqualTo(body.getName()).extractingBigDecimal(LoanChargeDataV1::getAmount).isEqualTo(BigDecimal.valueOf(body.getAmount()))
.isEqualTo(BigDecimal.valueOf(body.getAmount())).extractingData(LoanChargeDataV1::getDueDate) .extractingData(LoanChargeDataV1::getDueDate).isEqualTo(formatter.format(body.getDueDate()));
.isEqualTo(formatter.format(body.getDueDate()));
} }
@Then("Loan charge transaction with the following data results a {int} error and {string} error message") @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); String transactionDate = chargeData.get(1);
Double transactionAmount = Double.valueOf(chargeData.get(2)); Double transactionAmount = Double.valueOf(chargeData.get(2));
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
ErrorMessageType errorMsgType = ErrorMessageType.valueOf(errorMessageType); ErrorMessageType errorMsgType = ErrorMessageType.valueOf(errorMessageType);
String errorMessageExpectedRaw = errorMsgType.getValue(); String errorMessageExpectedRaw = errorMsgType.getValue();
@@ -340,18 +322,21 @@ public class LoanChargeStepDef extends AbstractStepDef {
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId) PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.dueDate(transactionDate).amount(transactionAmount); .dueDate(transactionDate).amount(transactionAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "") try {
.execute(); fineractClient.loanCharges().executeLoanCharge(loanId, loanIdChargesRequest, Map.<String, Object>of());
int errorCodeActual = loanChargeResponse.code(); throw new AssertionError("Expected FeignException but request succeeded");
String errorBody = loanChargeResponse.errorBody().string(); } catch (FeignException e) {
ErrorResponse errorResponse = GSON.fromJson(errorBody, ErrorResponse.class); ErrorResponse errorResponse = ErrorResponse.fromFeignException(e);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage(); int errorCodeActual = errorResponse.getHttpStatusCode();
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected); assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected))
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected)) .isEqualTo(errorCodeExpected);
.isEqualTo(errorMessageExpected); assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.contains(errorMessageExpected);
log.debug("ERROR CODE: {}", errorCodeActual); log.debug("ERROR CODE: {}", errorCodeActual);
log.debug("ERROR MESSAGE: {}", errorMessageActual); log.debug("ERROR MESSAGE: {}", errorMessageActual);
}
} }
} }
@@ -18,27 +18,27 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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 static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable; import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j; 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.LoanAccountDelinquencyRangeDataV1;
import org.apache.fineract.avro.loan.v1.LoanInstallmentDelinquencyBucketDataV1; 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.DelinquencyRangeData;
import org.apache.fineract.client.models.GetDelinquencyActionsResponse; import org.apache.fineract.client.models.GetDelinquencyActionsResponse;
import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse; 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.GetLoansLoanIdDelinquencySummary;
import org.apache.fineract.client.models.GetLoansLoanIdLoanInstallmentLevelDelinquency; import org.apache.fineract.client.models.GetLoansLoanIdLoanInstallmentLevelDelinquency;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; 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.PostLoansDelinquencyActionRequest;
import org.apache.fineract.client.models.PostLoansDelinquencyActionResponse; import org.apache.fineract.client.models.PostLoansDelinquencyActionResponse;
import org.apache.fineract.client.models.PostLoansResponse; import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUsersResponse; import org.apache.fineract.test.api.ApiProperties;
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.DelinquencyRange; import org.apache.fineract.test.data.DelinquencyRange;
import org.apache.fineract.test.data.LoanStatus; 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.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.helper.Utils; import org.apache.fineract.test.helper.Utils;
import org.apache.fineract.test.messaging.EventAssertion; import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.EventCheckHelper; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j @Slf4j
public class LoanDelinquencyStepDef extends AbstractStepDef { 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 DATE_FORMAT = "dd MMMM yyyy";
public static final String DEFAULT_LOCALE = "en"; public static final String DEFAULT_LOCALE = "en";
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT); 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!"; private static final String PWD_USER_WITH_ROLE = "1234567890Aa!";
@Autowired @Autowired
private LoansApi loansApi; private FineractFeignClient fineractClient;
@Autowired
private ApiProperties apiProperties;
@Autowired @Autowired
private EventAssertion eventAssertion; private EventAssertion eventAssertion;
@@ -86,17 +81,24 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Autowired @Autowired
private EventCheckHelper eventCheckHelper; private EventCheckHelper eventCheckHelper;
@Autowired private FineractFeignClient createClientForUser(String username, String password) {
private UsersApi usersApi; 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}") @Then("Admin checks that delinquency range is: {string} and has delinquentDate {string}")
public void checkDelinquencyRange(String range, String delinquentDateExpected) throws IOException { public void checkDelinquencyRange(String range, String delinquentDateExpected) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
ErrorHelper.checkSuccessfulApiCall(loanDetails); Integer loanStatus = loanDetails.getStatus().getId();
Integer loanStatus = loanDetails.body().getStatus().getId();
if (!LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.value.equals(loanStatus) && !LoanStatus.APPROVED.value.equals(loanStatus)) { if (!LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.value.equals(loanStatus) && !LoanStatus.APPROVED.value.equals(loanStatus)) {
String delinquentDateExpectedValue = "".equals(delinquentDateExpected) ? null : delinquentDateExpected; String delinquentDateExpectedValue = "".equals(delinquentDateExpected) ? null : delinquentDateExpected;
@@ -109,7 +111,7 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue(); String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
String actualDelinquencyRangeValue = DelinquencyRange.NO_DELINQUENCY.value; String actualDelinquencyRangeValue = DelinquencyRange.NO_DELINQUENCY.value;
DelinquencyRangeData actualDelinquencyRange = loanDetails.body().getDelinquencyRange(); DelinquencyRangeData actualDelinquencyRange = loanDetails.getDelinquencyRange();
if (actualDelinquencyRange != null) { if (actualDelinquencyRange != null) {
actualDelinquencyRangeValue = actualDelinquencyRange.getClassification(); actualDelinquencyRangeValue = actualDelinquencyRange.getClassification();
} }
@@ -123,8 +125,8 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
public void checkDelinquencyRange(String nthInList, String range, String addedOnDate, String delinquentDateExpected) public void checkDelinquencyRange(String nthInList, String range, String addedOnDate, String delinquentDateExpected)
throws IOException { throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
String delinquentDateExpectedValue = "".equals(delinquentDateExpected) ? null : delinquentDateExpected; String delinquentDateExpectedValue = "".equals(delinquentDateExpected) ? null : delinquentDateExpected;
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId) eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)
@@ -133,13 +135,13 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(range); DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(range);
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue(); String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
Response<List<GetDelinquencyTagHistoryResponse>> delinquencyHistoryDetails = loansApi.getDelinquencyTagHistory(loanId).execute(); List<GetDelinquencyTagHistoryResponse> delinquencyHistoryDetails = ok(
ErrorHelper.checkSuccessfulApiCall(delinquencyHistoryDetails); () -> fineractClient.loans().getDelinquencyTagHistory(loanId));
String actualDelinquencyRangeValue = DelinquencyRange.NO_DELINQUENCY.value; String actualDelinquencyRangeValue = DelinquencyRange.NO_DELINQUENCY.value;
String actualDelinquencyAddedOnDate = ""; String actualDelinquencyAddedOnDate = "";
int i = Integer.parseInt(nthInList) - 1; int i = Integer.parseInt(nthInList) - 1;
GetDelinquencyTagHistoryResponse delinquencyTag = delinquencyHistoryDetails.body().get(i); GetDelinquencyTagHistoryResponse delinquencyTag = delinquencyHistoryDetails.get(i);
if (delinquencyTag != null) { if (delinquencyTag != null) {
actualDelinquencyRangeValue = delinquencyTag.getDelinquencyRange().getClassification(); actualDelinquencyRangeValue = delinquencyTag.getDelinquencyRange().getClassification();
actualDelinquencyAddedOnDate = formatter.format(delinquencyTag.getAddedOnDate()); actualDelinquencyAddedOnDate = formatter.format(delinquencyTag.getAddedOnDate());
@@ -157,12 +159,10 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
List<List<String>> dataExpected = table.asLists(); List<List<String>> dataExpected = table.asLists();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<List<GetDelinquencyTagHistoryResponse>> delinquencyHistoryDetails = loansApi.getDelinquencyTagHistory(loanId).execute(); List<GetDelinquencyTagHistoryResponse> body = ok(() -> fineractClient.loans().getDelinquencyTagHistory(loanId));
ErrorHelper.checkSuccessfulApiCall(delinquencyHistoryDetails);
List<GetDelinquencyTagHistoryResponse> body = delinquencyHistoryDetails.body();
for (int i = 0; i < body.size(); i++) { for (int i = 0; i < body.size(); i++) {
List<String> line = dataExpected.get(i + 1); 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}") @When("Admin initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}")
public void delinquencyPause(String startDate, String endDate) throws IOException { public void delinquencyPause(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")// .action("pause")//
@@ -199,17 +199,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .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); testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId); eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
} }
@When("Created user with CREATE_DELINQUENCY_ACTION permission initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}") @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 { public void delinquencyPauseWithCreatedUser(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")// .action("pause")//
@@ -218,27 +216,19 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Map<String, String> headerMap = new HashMap<>(); String username = testContext().get(TestContextKey.CREATED_SIMPLE_USER_USERNAME);
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE); String password = testContext().get(TestContextKey.CREATED_SIMPLE_USER_PASSWORD);
Long createdUserId = createUserResponse.body().getResourceId(); FineractFeignClient userClient = createClientForUser(username, password);
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));
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); testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId); 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}") @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 { public void delinquencyPauseWithCreatedUserNOPermissionError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
int errorCodeExpected = 403; int errorCodeExpected = 403;
String errorMessageExpected = "User has no authority to CREATE delinquency_actions"; String errorMessageExpected = "User has no authority to CREATE delinquency_actions";
@@ -250,35 +240,26 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Map<String, String> headerMap = new HashMap<>(); String username = testContext().get(TestContextKey.CREATED_SIMPLE_USER_USERNAME);
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE); String password = testContext().get(TestContextKey.CREATED_SIMPLE_USER_PASSWORD);
Long createdUserId = createUserResponse.body().getResourceId(); FineractFeignClient userClient = createClientForUser(username, password);
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));
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request, headerMap).execute(); CallFailedRuntimeException exception = fail(() -> userClient.loans().createLoanDelinquencyAction(loanId, request));
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();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected); assertThat(exception.getStatus()).as(ErrorMessageHelper.wrongErrorCode(exception.getStatus(), errorCodeExpected))
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected)) .isEqualTo(errorCodeExpected);
.isEqualTo(errorMessageExpected); assertThat(exception.getDeveloperMessage())
.as(ErrorMessageHelper.wrongErrorMessage(exception.getDeveloperMessage(), errorMessageExpected))
.contains(errorMessageExpected);
log.debug("ERROR CODE: {}", errorCodeActual); log.debug("ERROR CODE: {}", exception.getStatus());
log.debug("ERROR MESSAGE: {}", errorMessageActual); log.debug("ERROR MESSAGE: {}", exception.getDeveloperMessage());
} }
@When("Admin initiate a DELINQUENCY RESUME with startDate: {string}") @When("Admin initiate a DELINQUENCY RESUME with startDate: {string}")
public void delinquencyResume(String startDate) throws IOException { public void delinquencyResume(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")// .action("resume")//
@@ -286,18 +267,16 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .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); testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId); eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
} }
@When("Admin initiate a DELINQUENCY PAUSE by loanExternalId with startDate: {string} and endDate: {string}") @When("Admin initiate a DELINQUENCY PAUSE by loanExternalId with startDate: {string} and endDate: {string}")
public void delinquencyPauseByLoanExternalId(String startDate, String endDate) throws IOException { public void delinquencyPauseByLoanExternalId(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId(); String loanExternalId = loanResponse.getResourceExternalId();
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")// .action("pause")//
@@ -306,18 +285,17 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .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); testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId); eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
} }
@When("Admin initiate a DELINQUENCY RESUME by loanExternalId with startDate: {string}") @When("Admin initiate a DELINQUENCY RESUME by loanExternalId with startDate: {string}")
public void delinquencyResumeByLoanExternalId(String startDate) throws IOException { public void delinquencyResumeByLoanExternalId(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId(); String loanExternalId = loanResponse.getResourceExternalId();
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")// .action("resume")//
@@ -325,23 +303,22 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .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); testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId); eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
} }
@Then("Delinquency-actions have the following data:") @Then("Delinquency-actions have the following data:")
public void getDelinquencyActionData(DataTable table) throws IOException { public void getDelinquencyActionData(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
int nrOfLinesExpected = data.size() - 1; int nrOfLinesExpected = data.size() - 1;
Response<List<GetDelinquencyActionsResponse>> response = loansApi.getLoanDelinquencyActions(loanId).execute(); List<GetDelinquencyActionsResponse> response = ok(() -> fineractClient.loans().getLoanDelinquencyActions(loanId));
int nrOfLinesActual = response.body().size(); int nrOfLinesActual = response.size();
assertThat(nrOfLinesActual)// assertThat(nrOfLinesActual)//
.as(ErrorMessageHelper.wrongNumberOfLinesInDelinquencyActions(nrOfLinesActual, nrOfLinesExpected))// .as(ErrorMessageHelper.wrongNumberOfLinesInDelinquencyActions(nrOfLinesActual, nrOfLinesExpected))//
@@ -350,7 +327,7 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
for (int i = 1; i < data.size(); i++) { for (int i = 1; i < data.size(); i++) {
List<String> expectedValues = data.get(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<>(); List<String> actualValues = new ArrayList<>();
actualValues.add(Objects.requireNonNull(lineActual.getAction())); 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}") @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 { public void actionFieldError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("TEST")// .action("TEST")//
@@ -375,16 +352,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400; int errorCodeExpected = 400;
String errorMessageExpected = "Invalid Delinquency Action: TEST"; 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}") @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 { public void delinquencyPauseStartDateError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")// .action("pause")//
@@ -393,16 +369,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400; int errorCodeExpected = 400;
String errorMessageExpected = "Start date of pause period must be in the future"; 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}") @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 { public void delinquencyPauseNonActiveLoanError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")// .action("pause")//
@@ -411,16 +386,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400; int errorCodeExpected = 400;
String errorMessageExpected = "Delinquency actions can be created only for active loans."; 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}") @Then("Initiating a DELINQUENCY RESUME on a non-active loan results an error - startDate: {string}")
public void delinquencyResumeNonActiveLoanError(String startDate) throws IOException { public void delinquencyResumeNonActiveLoanError(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")// .action("resume")//
@@ -428,16 +402,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400; int errorCodeExpected = 400;
String errorMessageExpected = "Delinquency actions can be created only for active loans."; 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}") @Then("Overlapping PAUSE periods result an error - startDate: {string}, endDate: {string}")
public void delinquencyPauseOverlappingError(String startDate, String endDate) throws IOException { public void delinquencyPauseOverlappingError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")// .action("pause")//
@@ -446,16 +419,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400; int errorCodeExpected = 400;
String errorMessageExpected = "Delinquency pause period cannot overlap with another pause period"; 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}") @Then("Initiating a DELINQUENCY RESUME without an active PAUSE period results an error - startDate: {string}")
public void delinquencyResumeWithoutPauseError(String startDate) throws IOException { public void delinquencyResumeWithoutPauseError(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")// .action("resume")//
@@ -463,16 +435,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400; int errorCodeExpected = 400;
String errorMessageExpected = "Resume Delinquency Action can only be created during an active pause"; 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}") @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 { public void delinquencyResumeStartDateError(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")// .action("resume")//
@@ -480,16 +451,15 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400; int errorCodeExpected = 400;
String errorMessageExpected = "Start date of the Resume Delinquency action must be the current business date"; 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}") @Then("Initiating a DELINQUENCY RESUME with an endDate results an error - startDate: {string}, endDate: {string}")
public void delinquencyResumeWithEndDateError(String startDate, String endDate) throws IOException { public void delinquencyResumeWithEndDateError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()// PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")// .action("resume")//
@@ -498,47 +468,44 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
.dateFormat(DATE_FORMAT)// .dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);// .locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400; int errorCodeExpected = 400;
String errorMessageExpected = "Resume Delinquency action can not have end date"; 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") @Then("Installment level delinquency event has correct data")
public void installmentLevelDelinquencyEventCheck() throws IOException { public void installmentLevelDelinquencyEventCheck() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
eventCheckHelper.installmentLevelDelinquencyRangeChangeEventCheck(loanId); eventCheckHelper.installmentLevelDelinquencyRangeChangeEventCheck(loanId);
} }
@Then("INSTALLMENT level delinquency is null") @Then("INSTALLMENT level delinquency is null")
public void installmentLevelDelinquencyNull() throws IOException { public void installmentLevelDelinquencyNull() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
ErrorHelper.checkSuccessfulApiCall(loanDetails); List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.getDelinquent()
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.body().getDelinquent() .getInstallmentLevelDelinquency() == null ? null : loanDetails.getDelinquent().getInstallmentLevelDelinquency();
.getInstallmentLevelDelinquency() == null ? null : loanDetails.body().getDelinquent().getInstallmentLevelDelinquency();
assertThat(installmentLevelDelinquency).isNull(); assertThat(installmentLevelDelinquency).isNull();
} }
@Then("Loan has the following LOAN level delinquency data:") @Then("Loan has the following LOAN level delinquency data:")
public void loanDelinquencyDataCheck(DataTable table) throws IOException { public void loanDelinquencyDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<String> expectedValuesList = table.asLists().get(1); List<String> expectedValuesList = table.asLists().get(1);
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.get(0)); DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.get(0));
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue(); String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
expectedValuesList.set(0, expectedDelinquencyRangeValue); expectedValuesList.set(0, expectedDelinquencyRangeValue);
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
ErrorHelper.checkSuccessfulApiCall(loanDetails); String actualDelinquencyRangeValue = loanDetails.getDelinquencyRange() == null ? "NO_DELINQUENCY"
String actualDelinquencyRangeValue = loanDetails.body().getDelinquencyRange() == null ? "NO_DELINQUENCY" : loanDetails.getDelinquencyRange().getClassification();
: loanDetails.body().getDelinquencyRange().getClassification(); GetLoansLoanIdDelinquencySummary delinquent = loanDetails.getDelinquent();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.body().getDelinquent();
String delinquentAmount = delinquent.getDelinquentAmount() == null ? null String delinquentAmount = delinquent.getDelinquentAmount() == null ? null
: new Utils.DoubleFormatter(delinquent.getDelinquentAmount().doubleValue()).format(); : new Utils.DoubleFormatter(delinquent.getDelinquentAmount().doubleValue()).format();
List<String> actualValuesList = List.of(actualDelinquencyRangeValue, delinquentAmount, 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:") @Then("Loan has the following LOAN level next payment due data:")
public void loanNextPaymentDataCheck(DataTable table) throws IOException { public void loanNextPaymentDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<String> expectedValuesList = table.asLists().get(1); List<String> expectedValuesList = table.asLists().get(1);
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.get(0)); DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.get(0));
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue(); String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
expectedValuesList.set(0, expectedDelinquencyRangeValue); expectedValuesList.set(0, expectedDelinquencyRangeValue);
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
ErrorHelper.checkSuccessfulApiCall(loanDetails);
String actualDelinquencyRangeValue = loanDetails.body().getDelinquencyRange() == null ? "NO_DELINQUENCY" String actualDelinquencyRangeValue = loanDetails.getDelinquencyRange() == null ? "NO_DELINQUENCY"
: loanDetails.body().getDelinquencyRange().getClassification(); : loanDetails.getDelinquencyRange().getClassification();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.body().getDelinquent(); GetLoansLoanIdDelinquencySummary delinquent = loanDetails.getDelinquent();
String delinquentAmount = delinquent.getNextPaymentAmount() == null ? null String delinquentAmount = delinquent.getNextPaymentAmount() == null ? null
: new Utils.DoubleFormatter(delinquent.getNextPaymentAmount().doubleValue()).format(); : 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:") @Then("Loan has the following INSTALLMENT level delinquency data:")
public void loanDelinquencyInstallmentLevelDataCheck(DataTable table) throws IOException { public void loanDelinquencyInstallmentLevelDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
ErrorHelper.checkSuccessfulApiCall(loanDetails); List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.getDelinquent()
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.body().getDelinquent()
.getInstallmentLevelDelinquency(); .getInstallmentLevelDelinquency();
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
@@ -608,15 +573,13 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("Loan Delinquency pause periods has the following data:") @Then("Loan Delinquency pause periods has the following data:")
public void loanDelinquencyPauseDataCheck(DataTable table) throws IOException { public void loanDelinquencyPauseDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<List<String>> expectedData = table.asLists(); List<List<String>> expectedData = table.asLists();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
ErrorHelper.checkSuccessfulApiCall(loanDetails);
List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriods = loanDetails.body().getDelinquent() List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriods = loanDetails.getDelinquent().getDelinquencyPausePeriods();
.getDelinquencyPausePeriods();
assertThat(delinquencyPausePeriods.size()) assertThat(delinquencyPausePeriods.size())
.as(ErrorMessageHelper.nrOfLinesWrongInLoanDelinquencyPauseData(delinquencyPausePeriods.size(), expectedData.size() - 1)) .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}") @Then("Loan details delinquent.nextPaymentDueDate will be {string}")
public void nextPaymentDueDateCheck(String expectedDate) throws IOException { public void nextPaymentDueDateCheck(String expectedDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
ErrorHelper.checkSuccessfulApiCall(loanDetails); String actualDate = FORMATTER.format(loanDetails.getDelinquent().getNextPaymentDueDate());
String actualDate = FORMATTER.format(loanDetails.body().getDelinquent().getNextPaymentDueDate());
assertThat(actualDate).as(ErrorMessageHelper.wrongDataInNextPaymentDueDate(actualDate, expectedDate)).isEqualTo(expectedDate); assertThat(actualDate).as(ErrorMessageHelper.wrongDataInNextPaymentDueDate(actualDate, expectedDate)).isEqualTo(expectedDate);
} }
@Then("LoanAccountDelinquencyRangeDataV1 has delinquencyRange field with value {string}") @Then("LoanAccountDelinquencyRangeDataV1 has delinquencyRange field with value {string}")
public void checkDelinquencyRangeInEvent(String expectedRange) { public void checkDelinquencyRangeInEvent(String expectedRange) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedRange); DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedRange);
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue(); 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") @Then("LoanDelinquencyRangeChangeBusinessEvent has the same Delinquency range, date and amount as in LoanDetails on both loan- and installment-level")
public void checkDelinquencyRangeInEvent() throws IOException { public void checkDelinquencyRangeInEvent() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
DelinquencyRangeData delinquencyRange = loanDetails.body().getDelinquencyRange(); DelinquencyRangeData delinquencyRange = loanDetails.getDelinquencyRange();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.body().getDelinquent(); GetLoansLoanIdDelinquencySummary delinquent = loanDetails.getDelinquent();
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)// eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)//
.extractingData(loanAccountDelinquencyRangeDataV1 -> { .extractingData(loanAccountDelinquencyRangeDataV1 -> {
@@ -738,15 +700,14 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("In Loan details delinquent.lastRepaymentAmount is {int} EUR with lastRepaymentDate {string}") @Then("In Loan details delinquent.lastRepaymentAmount is {int} EUR with lastRepaymentDate {string}")
public void delinquentLastRepaymentAmountCheck(int expectedLastRepaymentAmount, String expectedLastRepaymentDate) throws IOException { public void delinquentLastRepaymentAmountCheck(int expectedLastRepaymentAmount, String expectedLastRepaymentDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "collection", "", "").execute(); GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
ErrorHelper.checkSuccessfulApiCall(loanDetails);
Double expectedLastRepaymentAmount1 = Double.valueOf(expectedLastRepaymentAmount); Double expectedLastRepaymentAmount1 = Double.valueOf(expectedLastRepaymentAmount);
Double actualLastRepaymentAmount = loanDetails.body().getDelinquent().getLastRepaymentAmount().doubleValue(); Double actualLastRepaymentAmount = loanDetails.getDelinquent().getLastRepaymentAmount().doubleValue();
String actualLastRepaymentDate = FORMATTER.format(loanDetails.body().getDelinquent().getLastRepaymentDate()); String actualLastRepaymentDate = FORMATTER.format(loanDetails.getDelinquent().getLastRepaymentDate());
assertThat(actualLastRepaymentAmount)// assertThat(actualLastRepaymentAmount)//
.as(ErrorMessageHelper.wrongDataInDelinquentLastRepaymentAmount(actualLastRepaymentAmount, expectedLastRepaymentAmount1))// .as(ErrorMessageHelper.wrongDataInDelinquentLastRepaymentAmount(actualLastRepaymentAmount, expectedLastRepaymentAmount1))//
@@ -773,25 +734,24 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
return actualValues; return actualValues;
} }
private void errorMessageAssertation(Response<PostLoansDelinquencyActionResponse> response, int errorCodeExpected, private void errorMessageAssertationFeign(long loanId, PostLoansDelinquencyActionRequest request, int errorCodeExpected,
String errorMessageExpected) throws IOException { String errorMessageExpected) {
String errorToString = response.errorBody().string(); CallFailedRuntimeException exception = fail(() -> fineractClient.loans().createLoanDelinquencyAction(loanId, request));
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
int errorCodeActual = response.code();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected); assertThat(exception.getStatus()).as(ErrorMessageHelper.wrongErrorCode(exception.getStatus(), errorCodeExpected))
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected)) .isEqualTo(errorCodeExpected);
.isEqualTo(errorMessageExpected); assertThat(exception.getDeveloperMessage())
.as(ErrorMessageHelper.wrongErrorMessage(exception.getDeveloperMessage(), errorMessageExpected))
.contains(errorMessageExpected);
log.debug("ERROR CODE: {}", errorCodeActual); log.debug("ERROR CODE: {}", exception.getStatus());
log.debug("ERROR MESSAGE: {}", errorMessageActual); log.debug("ERROR MESSAGE: {}", exception.getDeveloperMessage());
} }
@Then("LoanDelinquencyRangeChangeBusinessEvent is created") @Then("LoanDelinquencyRangeChangeBusinessEvent is created")
public void checkLoanDelinquencyRangeChangeBusinessEventCreated() { public void checkLoanDelinquencyRangeChangeBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanDelinquencyRangeChangeEvent.class, loanId); eventAssertion.assertEventRaised(LoanDelinquencyRangeChangeEvent.class, loanId);
} }
@@ -18,6 +18,9 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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 static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
@@ -25,14 +28,13 @@ import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import lombok.RequiredArgsConstructor; 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.CommandProcessingResult;
import org.apache.fineract.client.models.InterestPauseRequestDto; import org.apache.fineract.client.models.InterestPauseRequestDto;
import org.apache.fineract.client.models.PostLoansResponse; 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.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper; 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.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.LoanBalanceChangedEvent; import org.apache.fineract.test.messaging.event.loan.LoanBalanceChangedEvent;
import org.apache.fineract.test.messaging.event.loan.LoanScheduleVariationsAddedEvent; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import retrofit2.Response;
@RequiredArgsConstructor @RequiredArgsConstructor
public class LoanInterestPauseStepDef extends AbstractStepDef { 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 DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
private static final int INTEREST_PAUSE_TERM_TYPE_ID = 11; private static final int INTEREST_PAUSE_TERM_TYPE_ID = 11;
private final LoanInterestPauseApi loanInterestPauseApi;
private final EventAssertion eventAssertion; private final EventAssertion eventAssertion;
private final FineractFeignClient fineractClient;
@Then("Create an interest pause period with start date {string} and end date {string}") @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 { public void interestPauseCreate(final String startDate, final String endDate) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body()); long loanId = loanResponse.getLoanId();
final Long loanId = loanResponse.body().getLoanId();
final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate) final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate)
.endDate(endDate); .endDate(endDate);
final Response<CommandProcessingResult> interestPauseResponse = loanInterestPauseApi final CommandProcessingResult interestPauseResponse = ok(
.createInterestPause(loanId, interestPauseRequest).execute(); () -> fineractClient.loanInterestPause().createInterestPause(loanId, interestPauseRequest));
ErrorHelper.checkSuccessfulApiCall(interestPauseResponse);
Assertions.assertNotNull(interestPauseResponse.body()); Assertions.assertNotNull(interestPauseResponse);
final Long variationId = interestPauseResponse.body().getResourceId(); final Long variationId = interestPauseResponse.getResourceId();
testContext().set(TestContextKey.INTEREST_PAUSE_VARIATION_ID, variationId); testContext().set(TestContextKey.INTEREST_PAUSE_VARIATION_ID, variationId);
} }
@Then("Delete the interest pause period") @Then("Delete the interest pause period")
public void interestPauseDelete() throws IOException { public void interestPauseDelete() throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body()); Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.body().getLoanId(); final Long loanId = loanResponse.getLoanId();
final Long variationId = testContext().get(TestContextKey.INTEREST_PAUSE_VARIATION_ID); final Long variationId = testContext().get(TestContextKey.INTEREST_PAUSE_VARIATION_ID);
Assertions.assertNotNull(variationId, "Interest pause variation ID must be set before deletion"); Assertions.assertNotNull(variationId, "Interest pause variation ID must be set before deletion");
final Response<Void> deleteResponse = loanInterestPauseApi.deleteInterestPause(loanId, variationId).execute(); executeVoid(() -> fineractClient.loanInterestPause().deleteInterestPause(loanId, variationId));
ErrorHelper.checkSuccessfulApiCall(deleteResponse);
} }
@Then("Update the interest pause period with start date {string} and end date {string}") @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 { public void interestPauseUpdate(final String startDate, final String endDate) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body()); Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.body().getLoanId(); final Long loanId = loanResponse.getLoanId();
final Long variationId = testContext().get(TestContextKey.INTEREST_PAUSE_VARIATION_ID); final Long variationId = testContext().get(TestContextKey.INTEREST_PAUSE_VARIATION_ID);
Assertions.assertNotNull(variationId, "Interest pause variation ID must be set before update"); Assertions.assertNotNull(variationId, "Interest pause variation ID must be set before update");
final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate) final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate)
.endDate(endDate); .endDate(endDate);
final Response<CommandProcessingResult> updateResponse = loanInterestPauseApi ok(() -> fineractClient.loanInterestPause().updateInterestPause(loanId, variationId, interestPauseRequest));
.updateInterestPause(loanId, variationId, interestPauseRequest).execute();
ErrorHelper.checkSuccessfulApiCall(updateResponse);
} }
@Then("Admin is not able to add an interest pause period with start date {string} and end date {string}") @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 { public void createInterestPauseFailure(final String startDate, final String endDate) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body()); Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.body().getLoanId(); final Long loanId = loanResponse.getLoanId();
final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate) final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate)
.endDate(endDate); .endDate(endDate);
final Response<CommandProcessingResult> interestPauseResponse = loanInterestPauseApi
.createInterestPause(loanId, interestPauseRequest).execute();
ErrorResponse errorDetails = ErrorResponse.from(interestPauseResponse); CallFailedRuntimeException exception = fail(
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addInterestPauseForNotInterestBearingLoanFailure()) () -> fineractClient.loanInterestPause().createInterestPause(loanId, interestPauseRequest));
.isEqualTo(403); assertThat(exception.getStatus()).as(ErrorMessageHelper.addInterestPauseForNotInterestBearingLoanFailure()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage()) assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.addInterestPauseForNotInterestBearingLoanFailure());
.isEqualTo(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") @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 { public void createInterestPauseForInactiveLoanFailure(final String startDate, final String endDate) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body()); Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.body().getLoanId(); final Long loanId = loanResponse.getLoanId();
final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate) final InterestPauseRequestDto interestPauseRequest = LoanRequestFactory.defaultInterestPauseRequest().startDate(startDate)
.endDate(endDate); .endDate(endDate);
final Response<CommandProcessingResult> interestPauseResponse = loanInterestPauseApi
.createInterestPause(loanId, interestPauseRequest).execute();
final ErrorResponse errorDetails = ErrorResponse.from(interestPauseResponse); CallFailedRuntimeException exception = fail(
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addInterestPauseForNotInactiveLoanFailure()).isEqualTo(403); () -> fineractClient.loanInterestPause().createInterestPause(loanId, interestPauseRequest));
assertThat(errorDetails.getSingleError().getDeveloperMessage()) assertThat(exception.getStatus()).as(ErrorMessageHelper.addInterestPauseForNotInactiveLoanFailure()).isEqualTo(403);
.isEqualTo(ErrorMessageHelper.addInterestPauseForNotInactiveLoanFailure()); assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.addInterestPauseForNotInactiveLoanFailure());
} }
@Then("LoanScheduleVariationsAddedBusinessEvent is created for interest pause from {string} to {string}") @Then("LoanScheduleVariationsAddedBusinessEvent is created for interest pause from {string} to {string}")
public void checkLoanScheduleVariationsAddedBusinessEvent(final String start, final String end) { public void checkLoanScheduleVariationsAddedBusinessEvent(final String start, final String end) {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body()); Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.body().getLoanId(); final Long loanId = loanResponse.getLoanId();
final LocalDate startDateParsed = LocalDate.parse(start, FORMATTER); final LocalDate startDateParsed = LocalDate.parse(start, FORMATTER);
final LocalDate endDateParsed = LocalDate.parse(end, 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}") @Then("LoanScheduleVariationsDeletedBusinessEvent is created for interest pause from {string} to {string}")
public void checkLoanScheduleVariationsDeletedBusinessEvent(final String start, final String end) { public void checkLoanScheduleVariationsDeletedBusinessEvent(final String start, final String end) {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body()); Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.body().getLoanId(); final Long loanId = loanResponse.getLoanId();
final LocalDate startDateParsed = LocalDate.parse(start, FORMATTER); final LocalDate startDateParsed = LocalDate.parse(start, FORMATTER);
final LocalDate endDateParsed = LocalDate.parse(end, 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}") @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, public void checkLoanScheduleVariationsAddedBusinessEventForUpdate(final String oldStart, final String oldEnd, final String newStart,
final String newEnd) { final String newEnd) {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body()); Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.body().getLoanId(); final Long loanId = loanResponse.getLoanId();
final LocalDate oldStartDateParsed = LocalDate.parse(oldStart, FORMATTER); final LocalDate oldStartDateParsed = LocalDate.parse(oldStart, FORMATTER);
final LocalDate oldEndDateParsed = LocalDate.parse(oldEnd, FORMATTER); final LocalDate oldEndDateParsed = LocalDate.parse(oldEnd, FORMATTER);
@@ -226,9 +217,9 @@ public class LoanInterestPauseStepDef extends AbstractStepDef {
@Then("LoanBalanceChangedBusinessEvent is created on {string}") @Then("LoanBalanceChangedBusinessEvent is created on {string}")
public void checkLoanBalanceChangedBusinessEvent(final String date) { public void checkLoanBalanceChangedBusinessEvent(final String date) {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Assertions.assertNotNull(loanResponse.body()); Assertions.assertNotNull(loanResponse);
final Long loanId = loanResponse.body().getLoanId(); final Long loanId = loanResponse.getLoanId();
final LocalDate expectedBusinessDateParsed = LocalDate.parse(date, FORMATTER); final LocalDate expectedBusinessDateParsed = LocalDate.parse(date, FORMATTER);
eventAssertion.assertEvent(LoanBalanceChangedEvent.class, loanId).isRaisedOnBusinessDate(expectedBusinessDateParsed); eventAssertion.assertEvent(LoanBalanceChangedEvent.class, loanId).isRaisedOnBusinessDate(expectedBusinessDateParsed);
@@ -18,6 +18,7 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -28,37 +29,36 @@ import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Map; import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.PostClientsResponse; import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoansRequest; import org.apache.fineract.client.models.PostLoansRequest;
import org.apache.fineract.client.models.PostLoansResponse; 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.DefaultLoanProduct;
import org.apache.fineract.test.data.loanproduct.LoanProductResolver; import org.apache.fineract.test.data.loanproduct.LoanProductResolver;
import org.apache.fineract.test.factory.LoanRequestFactory; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import retrofit2.Response;
@RequiredArgsConstructor @RequiredArgsConstructor
public class LoanOverrideFieldsStepDef extends AbstractStepDef { public class LoanOverrideFieldsStepDef extends AbstractStepDef {
private final FineractFeignClient fineractClient;
private final LoanRequestFactory loanRequestFactory; private final LoanRequestFactory loanRequestFactory;
private final LoanProductResolver loanProductResolver; private final LoanProductResolver loanProductResolver;
private final LoansApi loansApi;
@Then("LoanDetails has {string} field with value: {string}") @Then("LoanDetails has {string} field with value: {string}")
public void checkLoanDetailsFieldWithValue(final String fieldName, final String expectedValue) throws IOException { public void checkLoanDetailsFieldWithValue(final String fieldName, final String expectedValue) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assertNotNull(loanResponse.body()); assertNotNull(loanResponse);
final Long loanId = loanResponse.body().getLoanId(); final Long loanId = loanResponse.getLoanId();
final Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute(); final GetLoansLoanIdResponse loanDetails = ok(
ErrorHelper.checkSuccessfulApiCall(loanDetails); () -> fineractClient.loans().retrieveLoan(loanId, Map.of("staffInSelectedOfficeOnly", "false")));
assertNotNull(loanDetails.body());
verifyFieldValue(loanDetails.body(), fieldName, expectedValue); assertNotNull(loanDetails);
verifyFieldValue(loanDetails, fieldName, expectedValue);
} }
private void verifyFieldValue(final GetLoansLoanIdResponse loanDetails, final String fieldName, final String 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:") @When("Admin creates a new Loan with the following override data:")
public void createLoanWithOverrideData(final DataTable dataTable) throws IOException { public void createLoanWithOverrideData(final DataTable dataTable) throws IOException {
final Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE); final PostClientsResponse clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
assertNotNull(clientResponse.body()); assertNotNull(clientResponse);
final Long clientId = clientResponse.body().getClientId(); final Long clientId = clientResponse.getClientId();
final Map<String, String> overrideData = dataTable.asMap(String.class, String.class); 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); testContext().set(TestContextKey.LOAN_CREATE_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
} }
private void applyOverrideField(final PostLoansRequest request, final String fieldName, final String value) { private void applyOverrideField(final PostLoansRequest request, final String fieldName, final String value) {
@@ -18,6 +18,8 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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 static org.assertj.core.api.Assertions.assertThat;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -38,24 +40,21 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; 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.LoanScheduleData;
import org.apache.fineract.client.models.LoanSchedulePeriodData; import org.apache.fineract.client.models.LoanSchedulePeriodData;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansResponse; 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.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper; 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.helper.Utils;
import org.apache.fineract.test.messaging.EventAssertion; import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.LoanReAgeEvent; import org.apache.fineract.test.messaging.event.loan.LoanReAgeEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import retrofit2.Response;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -64,13 +63,13 @@ public class LoanReAgingStepDef extends AbstractStepDef {
private static final String DATE_FORMAT = "dd MMMM yyyy"; private static final String DATE_FORMAT = "dd MMMM yyyy";
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT); private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
private final LoanTransactionsApi loanTransactionsApi;
private final EventAssertion eventAssertion; private final EventAssertion eventAssertion;
private final FineractFeignClient fineractClient;
@When("Admin creates a Loan re-aging transaction with the following data:") @When("Admin creates a Loan re-aging transaction with the following data:")
public void createReAgingTransaction(DataTable table) throws IOException { public void createReAgingTransaction(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<List<String>> tableRows = table.asLists(); List<List<String>> tableRows = table.asLists();
List<String> headers = tableRows.get(0); List<String> headers = tableRows.get(0);
@@ -96,9 +95,8 @@ public class LoanReAgingStepDef extends AbstractStepDef {
applyAdditionalFields(reAgingRequest, rowData, Set.of("frequencyNumber", "frequencyType", "startDate", "numberOfInstallments")); applyAdditionalFields(reAgingRequest, rowData, Set.of("frequencyNumber", "frequencyType", "startDate", "numberOfInstallments"));
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi.executeLoanTransaction(loanId, reAgingRequest, "reAge") PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
.execute(); reAgingRequest, Map.<String, Object>of("command", "reAge")));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response); 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:") @When("Admin creates a Loan re-aging transaction by Loan external ID with the following data:")
public void createReAgingTransactionByLoanExternalId(DataTable table) throws IOException { public void createReAgingTransactionByLoanExternalId(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId(); String loanExternalId = loanResponse.getResourceExternalId();
PostLoansLoanIdTransactionsRequest reAgingRequest = setReAgeingRequestProperties(// PostLoansLoanIdTransactionsRequest reAgingRequest = setReAgeingRequestProperties(//
LoanRequestFactory.defaultReAgingRequest(), // LoanRequestFactory.defaultReAgingRequest(), //
@@ -201,27 +199,25 @@ public class LoanReAgingStepDef extends AbstractStepDef {
table.row(1) // table.row(1) //
); );
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction1(loanExternalId,
.executeLoanTransaction1(loanExternalId, reAgingRequest, "reAge").execute(); reAgingRequest, Map.<String, Object>of("command", "reAge")));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response); testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response);
} }
@When("Admin successfully undo Loan re-aging transaction") @When("Admin successfully undo Loan re-aging transaction")
public void undoReAgingTransaction() throws IOException { public void undoReAgingTransaction() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
.executeLoanTransaction(loanId, new PostLoansLoanIdTransactionsRequest(), "undoReAge").execute(); new PostLoansLoanIdTransactionsRequest(), Map.<String, Object>of("command", "undoReAge")));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAGING_UNDO_RESPONSE, response); testContext().set(TestContextKey.LOAN_REAGING_UNDO_RESPONSE, response);
} }
@Then("LoanReAgeBusinessEvent is created") @Then("LoanReAgeBusinessEvent is created")
public void checkLoanReAmortizeBusinessEventCreated() { public void checkLoanReAmortizeBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanReAgeEvent.class, loanId); 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:") @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) public void adminFailsToCreateReAgingTransactionWithError(final int statusCode, final String expectedError, final DataTable table)
throws IOException { throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
final long loanId = loanResponse.body().getLoanId(); final long loanId = loanResponse.getLoanId();
final List<String> data = table.asLists().get(1); final List<String> data = table.asLists().get(1);
final int frequencyNumber = Integer.parseInt(data.get(0)); final int frequencyNumber = Integer.parseInt(data.get(0));
@@ -245,22 +241,24 @@ public class LoanReAgingStepDef extends AbstractStepDef {
.startDate(startDate)// .startDate(startDate)//
.numberOfInstallments(numberOfInstallments);// .numberOfInstallments(numberOfInstallments);//
final Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, reAgingRequest,
.executeLoanTransaction(loanId, reAgingRequest, "reAge").execute(); Map.<String, Object>of("command", "reAge")));
try (ResponseBody errorBody = response.errorBody()) { assertThat(exception.getStatus()).isEqualTo(statusCode);
Assertions.assertNotNull(errorBody); String developerMessage = exception.getDeveloperMessage();
assertThat(errorBody.string()).contains(expectedError); 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:") @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 { public void reAgeChargedOffLoanFailure(final DataTable table) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse.body() != null; Assertions.assertNotNull(loanResponse);
final long loanId = loanResponse.body().getLoanId(); final long loanId = loanResponse.getLoanId();
final List<String> data = table.asLists().get(1); final List<String> data = table.asLists().get(1);
@@ -271,21 +269,17 @@ public class LoanReAgingStepDef extends AbstractStepDef {
.startDate(data.get(2))// .startDate(data.get(2))//
.numberOfInstallments(Integer.parseInt(data.get(3)));// .numberOfInstallments(Integer.parseInt(data.get(3)));//
final Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, reAgingRequest,
.executeLoanTransaction(loanId, reAgingRequest, "reAge").execute(); Map.<String, Object>of("command", "reAge")));
testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response); assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
final ErrorResponse errorDetails = ErrorResponse.from(response); assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.reAgeChargedOffLoanFailure());
final String developerMessage = errorDetails.getSingleError().getDeveloperMessage();
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(developerMessage).matches(ErrorMessageHelper.reAgeChargedOffLoanFailure());
} }
@Then("Admin fails to create a Loan re-aging transaction with the following data because loan was contract terminated:") @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 { public void reAgeContractTerminatedLoanFailure(final DataTable table) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assert loanResponse.body() != null; Assertions.assertNotNull(loanResponse);
final long loanId = loanResponse.body().getLoanId(); final long loanId = loanResponse.getLoanId();
final List<String> data = table.asLists().get(1); final List<String> data = table.asLists().get(1);
@@ -296,20 +290,16 @@ public class LoanReAgingStepDef extends AbstractStepDef {
.startDate(data.get(2))// .startDate(data.get(2))//
.numberOfInstallments(Integer.parseInt(data.get(3)));// .numberOfInstallments(Integer.parseInt(data.get(3)));//
final Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, reAgingRequest,
.executeLoanTransaction(loanId, reAgingRequest, "reAge").execute(); Map.<String, Object>of("command", "reAge")));
testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response); assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
final ErrorResponse errorDetails = ErrorResponse.from(response); assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.reAgeContractTerminatedLoanFailure());
final String developerMessage = errorDetails.getSingleError().getDeveloperMessage();
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(developerMessage).matches(ErrorMessageHelper.reAgeContractTerminatedLoanFailure());
} }
@When("Admin creates a Loan re-aging preview with the following data:") @When("Admin creates a Loan re-aging preview with the following data:")
public void createReAgingPreview(DataTable table) throws IOException { public void createReAgingPreview(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<String> data = table.asLists().get(1); List<String> data = table.asLists().get(1);
int frequencyNumber = Integer.parseInt(data.get(0)); int frequencyNumber = Integer.parseInt(data.get(0));
@@ -317,9 +307,9 @@ public class LoanReAgingStepDef extends AbstractStepDef {
String startDate = data.get(2); String startDate = data.get(2);
int numberOfInstallments = Integer.parseInt(data.get(3)); int numberOfInstallments = Integer.parseInt(data.get(3));
Response<LoanScheduleData> response = loanTransactionsApi Map<String, Object> queryParams = Map.of("frequencyNumber", frequencyNumber, "frequencyType", frequencyType, "startDate", startDate,
.previewReAgeSchedule(loanId, frequencyNumber, frequencyType, startDate, numberOfInstallments, DATE_FORMAT, "en").execute(); "numberOfInstallments", numberOfInstallments, "dateFormat", DATE_FORMAT, "locale", "en");
ErrorHelper.checkSuccessfulApiCall(response); LoanScheduleData response = ok(() -> fineractClient.loanTransactions().previewReAgeSchedule(loanId, queryParams));
testContext().set(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE, response); testContext().set(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE, response);
log.info( log.info(
@@ -327,9 +317,9 @@ public class LoanReAgingStepDef extends AbstractStepDef {
loanId, frequencyNumber, frequencyType, startDate, numberOfInstallments); loanId, frequencyNumber, frequencyType, startDate, numberOfInstallments);
} }
public Response<LoanScheduleData> reAgingPreviewByLoanExternalId(DataTable table) throws IOException { public LoanScheduleData reAgingPreviewByLoanExternalId(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId(); String loanExternalId = loanResponse.getResourceExternalId();
List<String> data = table.asLists().get(1); List<String> data = table.asLists().get(1);
int frequencyNumber = Integer.parseInt(data.get(0)); int frequencyNumber = Integer.parseInt(data.get(0));
@@ -337,19 +327,18 @@ public class LoanReAgingStepDef extends AbstractStepDef {
String startDate = data.get(2); String startDate = data.get(2);
int numberOfInstallments = Integer.parseInt(data.get(3)); int numberOfInstallments = Integer.parseInt(data.get(3));
Response<LoanScheduleData> response = loanTransactionsApi Map<String, Object> queryParams = Map.of("frequencyNumber", frequencyNumber, "frequencyType", frequencyType, "startDate", startDate,
.previewReAgeSchedule1(loanExternalId, frequencyNumber, frequencyType, startDate, numberOfInstallments, DATE_FORMAT, "en") "numberOfInstallments", numberOfInstallments, "dateFormat", DATE_FORMAT, "locale", "en");
.execute(); LoanScheduleData result = ok(() -> fineractClient.loanTransactions().previewReAgeSchedule1(loanExternalId, queryParams));
log.info( log.info(
"Re-aging preview is requested to be created with loan external ID: {} with parameters: frequencyNumber={}, frequencyType={}, startDate={}, numberOfInstallments={}", "Re-aging preview is requested to be created with loan external ID: {} with parameters: frequencyNumber={}, frequencyType={}, startDate={}, numberOfInstallments={}",
loanExternalId, 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:") @When("Admin creates a Loan re-aging preview by Loan external ID with the following data:")
public void createReAgingPreviewByLoanExternalId(DataTable table) throws IOException { public void createReAgingPreviewByLoanExternalId(DataTable table) throws IOException {
Response<LoanScheduleData> response = reAgingPreviewByLoanExternalId(table); LoanScheduleData response = reAgingPreviewByLoanExternalId(table);
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE, response); testContext().set(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE, response);
log.info("Re-aging preview is created with loan externalId."); 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:") @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 { public void reAgePreviewChargedOffLoanFailure(final DataTable table) throws IOException {
Response<LoanScheduleData> response = reAgingPreviewByLoanExternalId(table); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
final ErrorResponse errorDetails = ErrorResponse.from(response); String loanExternalId = loanResponse.getResourceExternalId();
final String developerMessage = errorDetails.getSingleError().getDeveloperMessage();
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403); List<String> data = table.asLists().get(1);
assertThat(developerMessage).matches(ErrorMessageHelper.reAgeChargedOffLoanFailure()); 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:") @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 { public void reAgePreviewContractTerminatedLoanFailure(final DataTable table) throws IOException {
Response<LoanScheduleData> response = reAgingPreviewByLoanExternalId(table); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
final ErrorResponse errorDetails = ErrorResponse.from(response); String loanExternalId = loanResponse.getResourceExternalId();
final String developerMessage = errorDetails.getSingleError().getDeveloperMessage();
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403); List<String> data = table.asLists().get(1);
assertThat(developerMessage).matches(ErrorMessageHelper.reAgeContractTerminatedLoanFailure()); 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:") @Then("Loan Repayment schedule preview has {int} periods, with the following data for periods:")
public void loanRepaymentSchedulePreviewPeriodsCheck(int linesExpected, DataTable table) { 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); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String resourceId = String.valueOf(loanResponse.body().getLoanId()); String resourceId = String.valueOf(loanResponse.getLoanId());
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
int nrLines = data.size(); int nrLines = data.size();
@@ -409,8 +418,8 @@ public class LoanReAgingStepDef extends AbstractStepDef {
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
List<String> header = data.get(0); List<String> header = data.get(0);
List<String> expectedValues = data.get(1); List<String> expectedValues = data.get(1);
Response<LoanScheduleData> scheduleResponse = testContext().get(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE); LoanScheduleData scheduleResponse = testContext().get(TestContextKey.LOAN_REAGING_PREVIEW_RESPONSE);
validateRepaymentScheduleTotal(header, scheduleResponse.body(), expectedValues); validateRepaymentScheduleTotal(header, scheduleResponse, expectedValues);
} }
private List<String> fetchValuesOfRepaymentSchedule(List<String> header, LoanSchedulePeriodData repaymentPeriod) { private List<String> fetchValuesOfRepaymentSchedule(List<String> header, LoanSchedulePeriodData repaymentPeriod) {
@@ -469,46 +478,46 @@ public class LoanReAgingStepDef extends AbstractStepDef {
String headerName = header.get(i); String headerName = header.get(i);
String expectedValue = expectedAmounts.get(i); String expectedValue = expectedAmounts.get(i);
switch (headerName) { switch (headerName) {
case "Principal due" -> assertThat(repaymentSchedule.getTotalPrincipalExpected().doubleValue())// case "Principal due" -> assertThat(repaymentSchedule.getTotalPrincipalExpected())//
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePrincipal( .as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePrincipal(
repaymentSchedule.getTotalPrincipalExpected().doubleValue(), Double.valueOf(expectedValue)))// repaymentSchedule.getTotalPrincipalExpected().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));// .isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Interest" -> assertThat(repaymentSchedule.getTotalInterestCharged().doubleValue())// case "Interest" -> assertThat(repaymentSchedule.getTotalInterestCharged())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInterest( .as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInterest(
repaymentSchedule.getTotalInterestCharged().doubleValue(), Double.valueOf(expectedValue)))// repaymentSchedule.getTotalInterestCharged().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));// .isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Fees" -> assertThat(repaymentSchedule.getTotalFeeChargesCharged().doubleValue())// case "Fees" -> assertThat(repaymentSchedule.getTotalFeeChargesCharged())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleFees( .as(ErrorMessageHelper.wrongAmountInRepaymentScheduleFees(
repaymentSchedule.getTotalFeeChargesCharged().doubleValue(), Double.valueOf(expectedValue)))// repaymentSchedule.getTotalFeeChargesCharged().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));// .isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Penalties" -> assertThat(repaymentSchedule.getTotalPenaltyChargesCharged().doubleValue())// case "Penalties" -> assertThat(repaymentSchedule.getTotalPenaltyChargesCharged())//
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePenalties( .as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePenalties(
repaymentSchedule.getTotalPenaltyChargesCharged().doubleValue(), Double.valueOf(expectedValue)))// repaymentSchedule.getTotalPenaltyChargesCharged().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));// .isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Due" -> assertThat(repaymentSchedule.getTotalRepaymentExpected().doubleValue())// case "Due" -> assertThat(repaymentSchedule.getTotalRepaymentExpected())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleDue( .as(ErrorMessageHelper.wrongAmountInRepaymentScheduleDue(
repaymentSchedule.getTotalRepaymentExpected().doubleValue(), Double.valueOf(expectedValue)))// repaymentSchedule.getTotalRepaymentExpected().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));// .isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Paid" -> assertThat(paidActualBd.doubleValue())// case "Paid" -> assertThat(paidActualBd)//
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePaid(paidActualBd.doubleValue(), .as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePaid(paidActualBd.doubleValue(),
Double.valueOf(expectedValue)))// Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));// .isEqualByComparingTo(new BigDecimal(expectedValue));//
case "In advance" -> assertThat(repaymentSchedule.getTotalPaidInAdvance().doubleValue())// case "In advance" -> assertThat(repaymentSchedule.getTotalPaidInAdvance())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInAdvance( .as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInAdvance(
repaymentSchedule.getTotalPaidInAdvance().doubleValue(), Double.valueOf(expectedValue)))// repaymentSchedule.getTotalPaidInAdvance().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));// .isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Late" -> assertThat(repaymentSchedule.getTotalPaidLate().doubleValue())// case "Late" -> assertThat(repaymentSchedule.getTotalPaidLate())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleLate(repaymentSchedule.getTotalPaidLate().doubleValue(), .as(ErrorMessageHelper.wrongAmountInRepaymentScheduleLate(repaymentSchedule.getTotalPaidLate().doubleValue(),
Double.valueOf(expectedValue)))// Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));// .isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Waived" -> assertThat(repaymentSchedule.getTotalWaived().doubleValue())// case "Waived" -> assertThat(repaymentSchedule.getTotalWaived())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleWaived(repaymentSchedule.getTotalWaived().doubleValue(), .as(ErrorMessageHelper.wrongAmountInRepaymentScheduleWaived(repaymentSchedule.getTotalWaived().doubleValue(),
Double.valueOf(expectedValue)))// Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));// .isEqualByComparingTo(new BigDecimal(expectedValue));//
case "Outstanding" -> assertThat(repaymentSchedule.getTotalOutstanding().doubleValue())// case "Outstanding" -> assertThat(repaymentSchedule.getTotalOutstanding())//
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleOutstanding( .as(ErrorMessageHelper.wrongAmountInRepaymentScheduleOutstanding(
repaymentSchedule.getTotalOutstanding().doubleValue(), Double.valueOf(expectedValue)))// repaymentSchedule.getTotalOutstanding().doubleValue(), Double.valueOf(expectedValue)))//
.isEqualTo(Double.valueOf(expectedValue));// .isEqualByComparingTo(new BigDecimal(expectedValue));//
} }
} }
return actualValues; return actualValues;
@@ -18,71 +18,69 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException; 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.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansResponse; 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.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.messaging.EventAssertion; import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.LoanReAmortizeEvent; import org.apache.fineract.test.messaging.event.loan.LoanReAmortizeEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class LoanReAmortizationStepDef extends AbstractStepDef { public class LoanReAmortizationStepDef extends AbstractStepDef {
@Autowired @Autowired
private LoanTransactionsApi loanTransactionsApi; private FineractFeignClient fineractClient;
@Autowired @Autowired
private EventAssertion eventAssertion; private EventAssertion eventAssertion;
@When("When Admin creates a Loan re-amortization transaction on current business date") @When("When Admin creates a Loan re-amortization transaction on current business date")
public void createLoanReAmortization() throws IOException { public void createLoanReAmortization() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansLoanIdTransactionsRequest reAmortizationRequest = LoanRequestFactory.defaultLoanReAmortizationRequest(); PostLoansLoanIdTransactionsRequest reAmortizationRequest = LoanRequestFactory.defaultLoanReAmortizationRequest();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
.executeLoanTransaction(loanId, reAmortizationRequest, "reAmortize").execute(); reAmortizationRequest, Map.<String, Object>of("command", "reAmortize")));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAMORTIZATION_RESPONSE, response); testContext().set(TestContextKey.LOAN_REAMORTIZATION_RESPONSE, response);
} }
@When("When Admin creates a Loan re-amortization transaction on current business date by loan external ID") @When("When Admin creates a Loan re-amortization transaction on current business date by loan external ID")
public void createLoanReAmortizationByLoanExternalId() throws IOException { public void createLoanReAmortizationByLoanExternalId() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId(); String loanExternalId = loanResponse.getResourceExternalId();
PostLoansLoanIdTransactionsRequest reAmortizationRequest = LoanRequestFactory.defaultLoanReAmortizationRequest(); PostLoansLoanIdTransactionsRequest reAmortizationRequest = LoanRequestFactory.defaultLoanReAmortizationRequest();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction1(loanExternalId,
.executeLoanTransaction1(loanExternalId, reAmortizationRequest, "reAmortize").execute(); reAmortizationRequest, Map.<String, Object>of("command", "reAmortize")));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAMORTIZATION_RESPONSE, response); testContext().set(TestContextKey.LOAN_REAMORTIZATION_RESPONSE, response);
} }
@When("When Admin undo Loan re-amortization transaction on current business date") @When("When Admin undo Loan re-amortization transaction on current business date")
public void undoLoanReAmortization() throws IOException { public void undoLoanReAmortization() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi PostLoansLoanIdTransactionsResponse response = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
.executeLoanTransaction(loanId, new PostLoansLoanIdTransactionsRequest(), "undoReAmortize").execute(); new PostLoansLoanIdTransactionsRequest(), Map.<String, Object>of("command", "undoReAmortize")));
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAMORTIZATION_UNDO_RESPONSE, response); testContext().set(TestContextKey.LOAN_REAMORTIZATION_UNDO_RESPONSE, response);
} }
@Then("LoanReAmortizeBusinessEvent is created") @Then("LoanReAmortizeBusinessEvent is created")
public void checkLoanReAmortizeBusinessEventCreated() { public void checkLoanReAmortizeBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanReAmortizeEvent.class, loanId); eventAssertion.assertEventRaised(LoanReAmortizeEvent.class, loanId);
} }
@@ -18,26 +18,26 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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.apache.fineract.test.data.paymenttype.DefaultPaymentType.AUTOPAY;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.java.en.And; import io.cucumber.java.en.And;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import lombok.extern.slf4j.Slf4j; 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.LoanTransactionAdjustmentDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1; 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.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions; 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.PostLoansLoanIdTransactionsTransactionIdRequest;
import org.apache.fineract.client.models.PostLoansResponse; import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUsersResponse; 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.TransactionType;
import org.apache.fineract.test.data.paymenttype.DefaultPaymentType; import org.apache.fineract.test.data.paymenttype.DefaultPaymentType;
import org.apache.fineract.test.data.paymenttype.PaymentTypeResolver; import org.apache.fineract.test.data.paymenttype.PaymentTypeResolver;
import org.apache.fineract.test.factory.LoanRequestFactory; 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.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse; import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.messaging.EventAssertion; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j @Slf4j
public class LoanRepaymentStepDef extends AbstractStepDef { public class LoanRepaymentStepDef extends AbstractStepDef {
@@ -81,20 +75,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
public static final String DEFAULT_REPAYMENT_TYPE = "AUTOPAY"; public static final String DEFAULT_REPAYMENT_TYPE = "AUTOPAY";
private static final String PWD_USER_WITH_ROLE = "1234567890Aa!"; private static final String PWD_USER_WITH_ROLE = "1234567890Aa!";
private static final Gson GSON = new JSON().getGson();
@Autowired @Autowired
private LoanTransactionsApi loanTransactionsApi; private FineractFeignClient fineractClient;
@Autowired
private LoansApi loansApi;
@Autowired @Autowired
private EventAssertion eventAssertion; private EventAssertion eventAssertion;
@Autowired
private UsersApi usersApi;
@Autowired @Autowired
private PaymentTypeResolver paymentTypeResolver; private PaymentTypeResolver paymentTypeResolver;
@@ -104,6 +90,9 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@Autowired @Autowired
private EventStore eventStore; private EventStore eventStore;
@Autowired
private org.apache.fineract.test.api.ApiProperties apiProperties;
@And("Customer makes {string} repayment on {string} with {double} EUR transaction amount") @And("Customer makes {string} repayment on {string} with {double} EUR transaction amount")
public void makeLoanRepayment(String repaymentType, String transactionDate, double transactionAmount) throws IOException { public void makeLoanRepayment(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
makeRepayment(repaymentType, transactionDate, transactionAmount, null); 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) private void makeRepayment(String repaymentType, String transactionDate, double transactionAmount, String transferExternalOwnerId)
throws IOException { throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType); DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
long paymentTypeValue = paymentTypeResolver.resolve(paymentType); long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -127,15 +116,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate) PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString(); String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey); testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse repaymentResponse = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
.executeLoanTransaction(loanId, repaymentRequest, "repayment", headerMap).execute(); repaymentRequest, Map.<String, Object>of("command", "repayment")));
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse); testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> transactionEvent = eventCheckHelper EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> transactionEvent = eventCheckHelper
.transactionEventCheck(repaymentResponse, TransactionType.REPAYMENT, transferExternalOwnerId); .transactionEventCheck(repaymentResponse, TransactionType.REPAYMENT, transferExternalOwnerId);
testContext().set(TestContextKey.TRANSACTION_EVENT, transactionEvent); 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") @And("Created user makes {string} repayment on {string} with {double} EUR transaction amount")
public void makeRepaymentWithGivenUser(String repaymentType, String transactionDate, double transactionAmount) throws IOException { public void makeRepaymentWithGivenUser(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType); DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType); Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -154,33 +140,30 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate) PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString(); String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey); testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE); PostUsersResponse createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId(); Long createdUserId = createUserResponse.getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute(); GetUsersUserIdResponse user = ok(() -> fineractClient.users().retrieveOne31(createdUserId));
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));
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi String apiBaseUrl = apiProperties.getBaseUrl() + "/fineract-provider/api/";
.executeLoanTransaction(loanId, repaymentRequest, "repayment", headerMap).execute(); 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); testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId); eventCheckHelper.loanBalanceChangedEventCheck(loanId);
} }
@And("Customer makes externalID controlled {string} repayment on {string} with {double} EUR transaction amount") @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 { public void makeRepaymentByExternalId(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
String resourceExternalId = loanResponse.body().getResourceExternalId(); String resourceExternalId = loanResponse.getResourceExternalId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType); DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType); Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -188,16 +171,13 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate) PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString(); String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey); testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse repaymentResponse = ok(() -> fineractClient.loanTransactions()
.executeLoanTransaction1(resourceExternalId, repaymentRequest, "repayment", headerMap).execute(); .executeLoanTransaction1(resourceExternalId, repaymentRequest, Map.<String, Object>of("command", "repayment")));
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse); testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId); eventCheckHelper.loanBalanceChangedEventCheck(loanId);
} }
@@ -205,9 +185,9 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
public void makeRepaymentWithGivenUserByExternalId(String repaymentType, String transactionDate, double transactionAmount) public void makeRepaymentWithGivenUserByExternalId(String repaymentType, String transactionDate, double transactionAmount)
throws IOException { throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
String resourceExternalId = loanResponse.body().getResourceExternalId(); String resourceExternalId = loanResponse.getResourceExternalId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType); DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType); Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -215,31 +195,28 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate) PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString(); String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey); testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE); PostUsersResponse createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId(); Long createdUserId = createUserResponse.getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute(); GetUsersUserIdResponse user = ok(() -> fineractClient.users().retrieveOne31(createdUserId));
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));
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi String apiBaseUrl = apiProperties.getBaseUrl() + "/fineract-provider/api/";
.executeLoanTransaction1(resourceExternalId, repaymentRequest, "repayment", headerMap).execute(); 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); testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId); eventCheckHelper.loanBalanceChangedEventCheck(loanId);
} }
@And("Customer not able to make {string} repayment on {string} with {double} EUR transaction amount") @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 { public void makeLoanRepaymentFails(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType); DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType); Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -247,37 +224,46 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate) PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi try {
.executeLoanTransaction(loanId, repaymentRequest, "repayment").execute(); ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, repaymentRequest,
ErrorResponse errorDetails = ErrorResponse.from(repaymentResponse); Map.<String, Object>of("command", "repayment")));
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(400); throw new IllegalStateException("Expected FeignException but call succeeded");
assertThat(errorDetails.getSingleError().getDeveloperMessage()).isEqualTo(ErrorMessageHelper.loanRepaymentOnClosedLoanFailureMsg()); } 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") @Then("Customer not able to make a repayment undo on {string} due to charge off")
public void makeLoanRepaymentUndoAfterChargeOff(String transactionDate) throws IOException { public void makeLoanRepaymentUndoAfterChargeOff(String transactionDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Response<PostLoansLoanIdTransactionsResponse> transactionResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE); PostLoansLoanIdTransactionsResponse transactionResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Long loanId = loanResponse.body().getLoanId(); Long loanId = loanResponse.getLoanId();
Long transactionId = transactionResponse.body().getResourceId(); 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() PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi try {
.adjustLoanTransaction(loanId, repaymentResponse.body().getResourceId(), repaymentUndoRequest, "").execute(); ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId, repaymentResponse.getResourceId(),
ErrorResponse errorDetails = ErrorResponse.from(repaymentUndoResponse); repaymentUndoRequest, Map.<String, Object>of()));
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.repaymentUndoFailureDueToChargeOffCodeMsg()).isEqualTo(403); throw new IllegalStateException("Expected FeignException but call succeeded");
assertThat(errorDetails.getSingleError().getDeveloperMessage()) } catch (feign.FeignException e) {
.isEqualTo(ErrorMessageHelper.repaymentUndoFailureDueToChargeOff(transactionId)); 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)") @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 { public void makeLoanRepaymentWithWrongDate(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType); DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType); Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -285,24 +271,24 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate) PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId, repaymentRequest,
.executeLoanTransaction(loanId, repaymentRequest, "repayment").execute(); Map.<String, Object>of("command", "repayment")));
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse); testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, null);
testContext().set(TestContextKey.ERROR_RESPONSE, exception);
} }
@When("Refund happens on {string} with {double} EUR transaction amount") @When("Refund happens on {string} with {double} EUR transaction amount")
public void makeRefund(String transactionDate, double transactionAmount) throws IOException { public void makeRefund(String transactionDate, double transactionAmount) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
PostLoansLoanIdTransactionsRequest refundRequest = LoanRequestFactory.defaultRefundRequest().transactionDate(transactionDate) PostLoansLoanIdTransactionsRequest refundRequest = LoanRequestFactory.defaultRefundRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeResolver.resolve(AUTOPAY)).dateFormat(DATE_FORMAT) .transactionAmount(transactionAmount).paymentTypeId(paymentTypeResolver.resolve(AUTOPAY)).dateFormat(DATE_FORMAT)
.locale(DEFAULT_LOCALE).accountNumber(DEFAULT_ACCOUNT_NB).checkNumber(DEFAULT_CHECK_NB).receiptNumber(DEFAULT_RECEIPT_NB) .locale(DEFAULT_LOCALE).accountNumber(DEFAULT_ACCOUNT_NB).checkNumber(DEFAULT_CHECK_NB).receiptNumber(DEFAULT_RECEIPT_NB)
.bankNumber(DEFAULT_BANK_NB); .bankNumber(DEFAULT_BANK_NB);
Response<PostLoansLoanIdTransactionsResponse> refundResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse refundResponse = ok(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
.executeLoanTransaction(loanId, refundRequest, "payoutRefund").execute(); refundRequest, Map.<String, Object>of("command", "payoutRefund")));
ErrorHelper.checkSuccessfulApiCall(refundResponse);
testContext().set(TestContextKey.LOAN_REFUND_RESPONSE, refundResponse); testContext().set(TestContextKey.LOAN_REFUND_RESPONSE, refundResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId); eventCheckHelper.loanBalanceChangedEventCheck(loanId);
} }
@@ -310,22 +296,21 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Refund undo happens on {string}") @When("Refund undo happens on {string}")
public void makeRefundUndo(String transactionDate) throws IOException { public void makeRefundUndo(String transactionDate) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdTransactionsResponse> refundResponse = testContext().get(TestContextKey.LOAN_REFUND_RESPONSE); PostLoansLoanIdTransactionsResponse refundResponse = testContext().get(TestContextKey.LOAN_REFUND_RESPONSE);
PostLoansLoanIdTransactionsTransactionIdRequest refundUndoRequest = LoanRequestFactory.defaultRefundUndoRequest() PostLoansLoanIdTransactionsTransactionIdRequest refundUndoRequest = LoanRequestFactory.defaultRefundUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> refundUndoResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse refundUndoResponse = ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
.adjustLoanTransaction(loanId, refundResponse.body().getResourceId(), refundUndoRequest, "").execute(); refundResponse.getResourceId(), refundUndoRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(refundUndoResponse);
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, refundUndoResponse); testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, refundUndoResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
.assertEvent(LoanAdjustTransactionBusinessEvent.class, refundResponse.body().getResourceId()); .assertEvent(LoanAdjustTransactionBusinessEvent.class, refundResponse.getResourceId());
eventAssertionBuilder eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId()) .extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId())
.isEqualTo(refundResponse.body().getResourceId()); .isEqualTo(refundResponse.getResourceId());
eventAssertionBuilder eventAssertionBuilder
.extractingData( .extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed()) loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
@@ -337,22 +322,21 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Customer makes a repayment undo on {string}") @When("Customer makes a repayment undo on {string}")
public void makeLoanRepaymentUndo(String transactionDate) throws IOException { public void makeLoanRepaymentUndo(String transactionDate) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE); PostLoansLoanIdTransactionsResponse repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest() PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse repaymentUndoResponse = ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
.adjustLoanTransaction(loanId, repaymentResponse.body().getResourceId(), repaymentUndoRequest, "").execute(); repaymentResponse.getResourceId(), repaymentUndoRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(repaymentUndoResponse);
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentUndoResponse); testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentUndoResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
.assertEvent(LoanAdjustTransactionBusinessEvent.class, repaymentResponse.body().getResourceId()); .assertEvent(LoanAdjustTransactionBusinessEvent.class, repaymentResponse.getResourceId());
eventAssertionBuilder eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId()) .extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId())
.isEqualTo(repaymentResponse.body().getResourceId()); .isEqualTo(repaymentResponse.getResourceId());
eventAssertionBuilder eventAssertionBuilder
.extractingData( .extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed()) loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
@@ -363,24 +347,26 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@Then("Loan {string} transaction adjust amount {double} must return {int} code") @Then("Loan {string} transaction adjust amount {double} must return {int} code")
public void makeLoanRepaymentAdjustFail(String transactionType, double transactionAmount, int codeExpected) throws IOException { public void makeLoanRepaymentAdjustFail(String transactionType, double transactionAmount, int codeExpected) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(transactionType); PostLoansLoanIdTransactionsResponse repaymentResponse = testContext().get(transactionType);
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest() PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionAmount(transactionAmount); .transactionAmount(transactionAmount);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
.adjustLoanTransaction(loanId, repaymentResponse.body().getResourceId(), repaymentUndoRequest, "").execute(); repaymentResponse.getResourceId(), repaymentUndoRequest, Map.<String, Object>of()));
assertThat(repaymentUndoResponse.code()).isEqualTo(codeExpected); assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(codeExpected);
assertThat(exception.getDeveloperMessage()).isNotEmpty();
} }
@When("Customer undo {string}th repayment on {string}") @When("Customer undo {string}th repayment on {string}")
public void undoNthRepayment(String nthItemStr, String transactionDate) throws IOException { public void undoNthRepayment(String nthItemStr, String transactionDate) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body() List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions(); .getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1; int nthItem = Integer.parseInt(nthItemStr) - 1;
@@ -390,9 +376,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest() PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse repaymentUndoResponse = ok(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
.adjustLoanTransaction(loanId, targetTransaction.getId(), repaymentUndoRequest, "").execute(); targetTransaction.getId(), repaymentUndoRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(repaymentUndoResponse);
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentUndoResponse); testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentUndoResponse);
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction); eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
eventCheckHelper.loanBalanceChangedEventCheck(loanId); eventCheckHelper.loanBalanceChangedEventCheck(loanId);
@@ -401,9 +386,10 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Customer undo {string}th capitalized income adjustment on {string}") @When("Customer undo {string}th capitalized income adjustment on {string}")
public void undoNthCapitalizedIncomeAdjustment(String nthItemStr, String transactionDate) throws IOException { public void undoNthCapitalizedIncomeAdjustment(String nthItemStr, String transactionDate) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body() List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions(); .getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1; int nthItem = Integer.parseInt(nthItemStr) - 1;
@@ -413,9 +399,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest capitalizedIncomeUndoRequest = LoanRequestFactory PostLoansLoanIdTransactionsTransactionIdRequest capitalizedIncomeUndoRequest = LoanRequestFactory
.defaultCapitalizedIncomeAdjustmentUndoRequest().transactionDate(transactionDate); .defaultCapitalizedIncomeAdjustmentUndoRequest().transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> capitalizedIncomeUndoResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse capitalizedIncomeUndoResponse = ok(() -> fineractClient.loanTransactions()
.adjustLoanTransaction(loanId, targetTransaction.getId(), capitalizedIncomeUndoRequest, "").execute(); .adjustLoanTransaction(loanId, targetTransaction.getId(), capitalizedIncomeUndoRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(capitalizedIncomeUndoResponse);
testContext().set(TestContextKey.LOAN_CAPITALIZED_INCOME_ADJUSTMENT_UNDO_RESPONSE, capitalizedIncomeUndoResponse); testContext().set(TestContextKey.LOAN_CAPITALIZED_INCOME_ADJUSTMENT_UNDO_RESPONSE, capitalizedIncomeUndoResponse);
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction); eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
eventCheckHelper.loanBalanceChangedEventCheck(loanId); eventCheckHelper.loanBalanceChangedEventCheck(loanId);
@@ -425,9 +410,10 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
public void undoNthTransaction(String nthItemStr, String transactionDate) throws IOException { public void undoNthTransaction(String nthItemStr, String transactionDate) throws IOException {
eventStore.reset(); eventStore.reset();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body() List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions(); .getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1; int nthItem = Integer.parseInt(nthItemStr) - 1;
@@ -437,9 +423,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest() PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate); .transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse transactionUndoResponse = ok(() -> fineractClient.loanTransactions()
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute(); .adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(transactionUndoResponse);
testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse); testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse);
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction); eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
@@ -449,9 +434,10 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Customer undo {string}th {string} transaction made on {string}") @When("Customer undo {string}th {string} transaction made on {string}")
public void undoNthTransactionType(String nthItemStr, String transactionType, String transactionDate) throws IOException { public void undoNthTransactionType(String nthItemStr, String transactionType, String transactionDate) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body() List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions(); .getTransactions();
GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.getNthTransactionType(nthItemStr, transactionType, transactionDate, GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.getNthTransactionType(nthItemStr, transactionType, transactionDate,
@@ -460,9 +446,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest() PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate); .transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse transactionUndoResponse = ok(() -> fineractClient.loanTransactions()
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute(); .adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(transactionUndoResponse);
testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse); testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse);
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction); eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
eventCheckHelper.loanBalanceChangedEventCheck(loanId); 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}") @Then("Customer is forbidden to undo {string}th {string} transaction made on {string}")
public void makeTransactionUndoForbidden(String nthItemStr, String transactionType, String transactionDate) throws IOException { public void makeTransactionUndoForbidden(String nthItemStr, String transactionType, String transactionDate) throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.findNthTransaction(nthItemStr, transactionType, transactionDate, GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.findNthTransaction(nthItemStr, transactionType, transactionDate,
loanId); loanId);
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest() PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate); .transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute(); targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
Integer httpStatusCodeExpected = 403; assertThat(exception.getStatus()).isEqualTo(403);
String developerMessageExpected = String.format("Interest refund transaction: %s cannot be reversed or adjusted directly", assertThat(exception.getDeveloperMessage()).contains("Interest refund transaction")
targetTransaction.getId()); .contains("cannot be reversed or adjusted directly");
checkMakeTransactionForbidden(transactionUndoResponse, httpStatusCodeExpected, developerMessageExpected);
} }
public void checkMakeTransactionForbidden(Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse, public void checkMakeTransactionForbidden(feign.FeignException e, Integer httpStatusCodeExpected, String developerMessageExpected)
Integer httpStatusCodeExpected, String developerMessageExpected) throws IOException { throws IOException {
String string = transactionUndoResponse.errorBody().string(); ErrorResponse errorResponse = ErrorResponse.fromFeignException(e);
ErrorResponse errorResponse = GSON.fromJson(string, ErrorResponse.class);
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode(); Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
String developerMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage(); 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) public void makeTransactionUndoForbiddenNonReversal(String nthItemStr, String transactionType, String transactionDate)
throws IOException { throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.findNthTransaction(nthItemStr, transactionType, transactionDate, GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.findNthTransaction(nthItemStr, transactionType, transactionDate,
loanId); loanId);
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest() PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate); .transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute(); targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
checkMakeTransactionForbidden(transactionUndoResponse, 403,
ErrorMessageHelper.addCapitalizedIncomeUndoFailureTransactionTypeNonReversal()); 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") @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) public void makeTransactionUndoForbiddenAdjustmentExiists(String nthItemStr, String transactionType, String transactionDate)
throws IOException { throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.findNthTransaction(nthItemStr, transactionType, transactionDate, GetLoansLoanIdTransactions targetTransaction = eventCheckHelper.findNthTransaction(nthItemStr, transactionType, transactionDate,
loanId); loanId);
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest() PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate); .transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().adjustLoanTransaction(loanId,
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute(); targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
assertThat(exception.getStatus()).isEqualTo(403);
if (transactionType.equals("Buy Down Fee")) { if (transactionType.equals("Buy Down Fee")) {
checkMakeTransactionForbidden(transactionUndoResponse, 403, ErrorMessageHelper.buyDownFeeUndoFailureAdjustmentExists()); assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.buyDownFeeUndoFailureAdjustmentExists());
} else if (transactionType.equals("Capitalized Income")) { } else if (transactionType.equals("Capitalized Income")) {
checkMakeTransactionForbidden(transactionUndoResponse, 403, assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.addCapitalizedIncomeUndoFailureAdjustmentExists());
ErrorMessageHelper.addCapitalizedIncomeUndoFailureAdjustmentExists());
} }
} }
@@ -550,9 +536,10 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
public void checkNthTransactionType(String nthItemStr, String transactionType, String transactionDate, String linkedTransactionType) public void checkNthTransactionType(String nthItemStr, String transactionType, String transactionDate, String linkedTransactionType)
throws IOException { throws IOException {
eventStore.reset(); eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body() List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions(); .getTransactions();
// check that here are 2 transactions - target and linked // check that here are 2 transactions - target and linked
@@ -562,9 +549,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
transactions); transactions);
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest() PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate); .transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse transactionUndoResponse = ok(() -> fineractClient.loanTransactions()
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute(); .adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(transactionUndoResponse);
testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse); testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse);
eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction); eventCheckHelper.checkTransactionWithLoanTransactionAdjustmentBizEvent(targetTransaction);
@@ -577,36 +563,32 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@Then("Repayment transaction is created with {double} amount and {string} type") @Then("Repayment transaction is created with {double} amount and {string} type")
public void loanRepaymentStatus(double repaymentAmount, String paymentType) throws IOException { public void loanRepaymentStatus(double repaymentAmount, String paymentType) throws IOException {
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE); PostLoansLoanIdTransactionsResponse repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
Response<GetLoansLoanIdTransactionsTransactionIdResponse> transactionResponse = loanTransactionsApi GetLoansLoanIdTransactionsTransactionIdResponse transactionResponse = ok(() -> fineractClient.loanTransactions()
.retrieveTransaction(loanId, repaymentResponse.body().getResourceId(), "").execute(); .retrieveTransaction(loanId, repaymentResponse.getResourceId(), Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(transactionResponse); assertThat(transactionResponse.getAmount()).isEqualTo(repaymentAmount);
assertThat(transactionResponse.body().getAmount()).isEqualTo(repaymentAmount); assertThat(transactionResponse.getPaymentDetailData().getPaymentType().getName()).isEqualTo(paymentType);
assertThat(transactionResponse.body().getPaymentDetailData().getPaymentType().getName()).isEqualTo(paymentType);
} }
@Then("Repayment failed because the repayment date is after the business date") @Then("Repayment failed because the repayment date is after the business date")
public void repaymentDateFailure() { public void repaymentDateFailure() {
Response<PostLoansLoanIdTransactionsResponse> response = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE); CallFailedRuntimeException exception = testContext().get(TestContextKey.ERROR_RESPONSE);
assertThat(exception).isNotNull();
ErrorResponse errorDetails = ErrorResponse.from(response); assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(exception.getDeveloperMessage()).contains("transaction date cannot be in the future");
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage()).isEqualTo(ErrorMessageHelper.transactionDateInFutureFailureMsg());
} }
@Then("Amounts are distributed equally in loan repayment schedule in case of total amount {double}") @Then("Amounts are distributed equally in loan repayment schedule in case of total amount {double}")
public void amountsEquallyDistributedInSchedule(double totalAmount) throws IOException { public void amountsEquallyDistributedInSchedule(double totalAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId1 = loanResponse.body().getLoanId(); long loanId1 = loanResponse.getLoanId();
Response<GetLoansLoanIdResponse> getLoansLoanIdResponseCall = loansApi GetLoansLoanIdResponse getLoansLoanIdResponseCall = ok(() -> fineractClient.loans().retrieveLoan(loanId1,
.retrieveLoan(loanId1, false, "all", "guarantors,futureSchedule", "").execute(); Map.<String, Object>of("staffInSelectedOfficeOnly", false, "associations", "all", "exclude", "guarantors,futureSchedule")));
ErrorHelper.checkSuccessfulApiCall(getLoansLoanIdResponseCall);
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 expectedAmount = new BigDecimal(totalAmount / (periods.size() - 1)).setScale(0, RoundingMode.HALF_DOWN);
BigDecimal lastExpectedAmount = new BigDecimal(totalAmount).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}") @When("Loan Pay-off is made on {string}")
public void makeLoanPayOff(String transactionDate) throws IOException { public void makeLoanPayOff(String transactionDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId1 = loanResponse.body().getLoanId(); long loanId1 = loanResponse.getLoanId();
Response<GetLoansLoanIdTransactionsTemplateResponse> response = loanTransactionsApi GetLoansLoanIdTransactionsTemplateResponse response = ok(
.retrieveTransactionTemplate(loanId1, "prepayLoan", DATE_FORMAT, transactionDate, DEFAULT_LOCALE, null).execute(); () -> fineractClient.loanTransactions().retrieveTransactionTemplate(loanId1, Map.<String, Object>of("command", "prepayLoan",
Double transactionAmount = response.body().getAmount(); "dateFormat", DATE_FORMAT, "transactionDate", transactionDate, "locale", DEFAULT_LOCALE)));
Double transactionAmount = response.getAmount();
log.debug("%n--- Loan Pay-off with amount: {} ---", transactionAmount); log.debug("%n--- Loan Pay-off with amount: {} ---", transactionAmount);
makeRepayment(DEFAULT_REPAYMENT_TYPE, transactionDate, transactionAmount, null); 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) private void adjustNthRepaymentWithExternalOwnerCheck(String nthItemStr, String transactionDate, String amount, String externalOwnerId)
throws IOException { throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body() List<GetLoansLoanIdTransactions> transactions = ok(
() -> fineractClient.loans().retrieveLoan(loanId, Map.<String, Object>of("associations", "transactions")))
.getTransactions(); .getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1; int nthItem = Integer.parseInt(nthItemStr) - 1;
@@ -662,9 +646,8 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentAdjustRequest(amountValue) PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentAdjustRequest(amountValue)
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE); .transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentAdjustmentResponse = loanTransactionsApi PostLoansLoanIdTransactionsResponse repaymentAdjustmentResponse = ok(() -> fineractClient.loanTransactions()
.adjustLoanTransaction(loanId, targetTransaction.getId(), repaymentUndoRequest, "").execute(); .adjustLoanTransaction(loanId, targetTransaction.getId(), repaymentUndoRequest, Map.<String, Object>of()));
ErrorHelper.checkSuccessfulApiCall(repaymentAdjustmentResponse);
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentAdjustmentResponse); testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentAdjustmentResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
@@ -687,7 +670,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
if (amountValue > 0) { if (amountValue > 0) {
eventAssertionBuilder eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getNewTransactionDetail().getId()) .extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getNewTransactionDetail().getId())
.isEqualTo(repaymentAdjustmentResponse.body().getResourceId()); .isEqualTo(repaymentAdjustmentResponse.getResourceId());
eventAssertionBuilder eventAssertionBuilder
.extractingBigDecimal( .extractingBigDecimal(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getNewTransactionDetail().getAmount()) loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getNewTransactionDetail().getAmount())
@@ -18,25 +18,26 @@
*/ */
package org.apache.fineract.test.stepdef.loan; package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.executeVoid;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
import java.io.IOException; import java.io.IOException;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.PostLoansResponse; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class LoanReprocessStepDef extends AbstractStepDef { public class LoanReprocessStepDef extends AbstractStepDef {
@Autowired @Autowired
private InternalCobApi internalCobApi; private FineractFeignClient fineractClient;
@When("Admin runs loan reprocess for Loan") @When("Admin runs loan reprocess for Loan")
public void admin_runs_inline_COB_job_for_loan() throws IOException { public void admin_runs_inline_COB_job_for_loan() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
internalCobApi.loanReprocess(loanId).execute(); executeVoid(() -> fineractClient.internalCob().loanReprocess(loanId));
} }
} }
@@ -18,9 +18,10 @@
*/ */
package org.apache.fineract.test.stepdef.loan; 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 static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable; import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then; import io.cucumber.java.en.Then;
import io.cucumber.java.en.When; import io.cucumber.java.en.When;
@@ -29,39 +30,35 @@ import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j; 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.PostCreateRescheduleLoansRequest;
import org.apache.fineract.client.models.PostCreateRescheduleLoansResponse; import org.apache.fineract.client.models.PostCreateRescheduleLoansResponse;
import org.apache.fineract.client.models.PostLoansResponse; import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUpdateRescheduleLoansRequest; 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.data.LoanRescheduleErrorMessage;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper; 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.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j @Slf4j
public class LoanRescheduleStepDef extends AbstractStepDef { 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_HU = "yyyy-MM-dd";
public static final String DATE_FORMAT_EN = "dd MMMM yyyy"; 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_HU = DateTimeFormatter.ofPattern(DATE_FORMAT_HU);
public static final DateTimeFormatter FORMATTER_EN = DateTimeFormatter.ofPattern(DATE_FORMAT_EN); public static final DateTimeFormatter FORMATTER_EN = DateTimeFormatter.ofPattern(DATE_FORMAT_EN);
@Autowired @Autowired
private RescheduleLoansApi rescheduleLoansApi; private FineractFeignClient fineractClient;
@When("Admin creates and approves Loan reschedule with the following data:") @When("Admin creates and approves Loan reschedule with the following data:")
public void createAndApproveLoanReschedule(DataTable table) throws IOException { public void createAndApproveLoanReschedule(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
List<String> rescheduleData = data.get(1); List<String> rescheduleData = data.get(1);
@@ -91,24 +88,22 @@ public class LoanRescheduleStepDef extends AbstractStepDef {
.dateFormat("dd MMMM yyyy")// .dateFormat("dd MMMM yyyy")//
.locale("en");// .locale("en");//
Response<PostCreateRescheduleLoansResponse> createResponse = rescheduleLoansApi.createLoanRescheduleRequest(request).execute(); PostCreateRescheduleLoansResponse createResponse = ok(() -> fineractClient.rescheduleLoans().createLoanRescheduleRequest(request));
ErrorHelper.checkSuccessfulApiCall(createResponse);
Long scheduleId = createResponse.body().getResourceId(); Long scheduleId = createResponse.getResourceId();
PostUpdateRescheduleLoansRequest approveRequest = new PostUpdateRescheduleLoansRequest()// PostUpdateRescheduleLoansRequest approveRequest = new PostUpdateRescheduleLoansRequest()//
.approvedOnDate(submittedOnDate)// .approvedOnDate(submittedOnDate)//
.dateFormat("dd MMMM yyyy")// .dateFormat("dd MMMM yyyy")//
.locale("en");// .locale("en");//
Response<PostUpdateRescheduleLoansResponse> approveResponse = rescheduleLoansApi ok(() -> fineractClient.rescheduleLoans().updateLoanRescheduleRequest(scheduleId, approveRequest,
.updateLoanRescheduleRequest(scheduleId, approveRequest, "approve").execute(); Map.<String, Object>of("command", "approve")));
ErrorHelper.checkSuccessfulApiCall(approveResponse);
} }
@Then("Loan reschedule with the following data results a {int} error and {string} error message") @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 { public void createLoanRescheduleError(int errorCodeExpected, String errorMessageType, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId(); long loanId = loanResponse.getLoanId();
List<List<String>> data = table.asLists(); List<List<String>> data = table.asLists();
List<String> rescheduleData = data.get(1); List<String> rescheduleData = data.get(1);
@@ -137,8 +132,6 @@ public class LoanRescheduleStepDef extends AbstractStepDef {
.dateFormat("dd MMMM yyyy")// .dateFormat("dd MMMM yyyy")//
.locale("en");// .locale("en");//
Response<PostCreateRescheduleLoansResponse> createResponse = rescheduleLoansApi.createLoanRescheduleRequest(request).execute();
LoanRescheduleErrorMessage loanRescheduleErrorMessage = LoanRescheduleErrorMessage.valueOf(errorMessageType); LoanRescheduleErrorMessage loanRescheduleErrorMessage = LoanRescheduleErrorMessage.valueOf(errorMessageType);
LocalDate localDate = LocalDate.parse(rescheduleFromDate, FORMATTER_EN); 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"); throw new IllegalStateException("Parameter count in Error message does not met the criteria");
} }
String errorToString = createResponse.errorBody().string(); CallFailedRuntimeException exception = fail(() -> fineractClient.rescheduleLoans().createLoanRescheduleRequest(request));
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
int errorCodeActual = createResponse.code();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected); assertThat(exception.getStatus()).as(ErrorMessageHelper.wrongErrorCode(exception.getStatus(), errorCodeExpected))
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected)) .isEqualTo(errorCodeExpected);
.isEqualTo(errorMessageExpected); assertThat(exception.getDeveloperMessage())
.as(ErrorMessageHelper.wrongErrorMessage(exception.getDeveloperMessage(), errorMessageExpected))
.contains(errorMessageExpected);
log.debug("ERROR CODE: {}", errorCodeActual); log.debug("ERROR CODE: {}", exception.getStatus());
log.debug("ERROR MESSAGE: {}", errorMessageActual); log.debug("ERROR MESSAGE: {}", exception.getDeveloperMessage());
} }
} }
@@ -18,9 +18,12 @@
*/ */
package org.apache.fineract.test.stepdef.saving; package org.apache.fineract.test.stepdef.saving;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import io.cucumber.java.en.And; import io.cucumber.java.en.And;
import java.io.IOException;
import java.math.BigDecimal; 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.PostClientsResponse;
import org.apache.fineract.client.models.PostSavingsAccountTransactionsRequest; import org.apache.fineract.client.models.PostSavingsAccountTransactionsRequest;
import org.apache.fineract.client.models.PostSavingsAccountTransactionsResponse; 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.PostSavingsAccountsAccountIdResponse;
import org.apache.fineract.client.models.PostSavingsAccountsRequest; import org.apache.fineract.client.models.PostSavingsAccountsRequest;
import org.apache.fineract.client.models.PostSavingsAccountsResponse; 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.factory.SavingsAccountRequestFactory;
import org.apache.fineract.test.stepdef.AbstractStepDef; import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey; import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class SavingsAccountStepDef extends AbstractStepDef { public class SavingsAccountStepDef extends AbstractStepDef {
@Autowired @Autowired
private SavingsAccountTransactionsApi savingsAccountTransactionsApi; private FineractFeignClient fineractClient;
@Autowired
private SavingsAccountApi savingsAccountApi;
@And("Client creates a new EUR savings account with {string} submitted on date") @And("Client creates a new EUR savings account with {string} submitted on date")
public void createSavingsAccountEUR(String submittedOnDate) throws IOException { public void createSavingsAccountEUR(String submittedOnDate) {
Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE); PostClientsResponse clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
long clientId = clientResponse.body().getClientId(); long clientId = clientResponse.getClientId();
PostSavingsAccountsRequest createSavingsAccountRequest = SavingsAccountRequestFactory.defaultEURSavingsAccountRequest() PostSavingsAccountsRequest createSavingsAccountRequest = SavingsAccountRequestFactory.defaultEURSavingsAccountRequest()
.clientId(clientId).submittedOnDate(submittedOnDate); .clientId(clientId).submittedOnDate(submittedOnDate);
Response<PostSavingsAccountsResponse> createSavingsAccountResponse = savingsAccountApi PostSavingsAccountsResponse createSavingsAccountResponse = ok(
.submitApplication2(createSavingsAccountRequest).execute(); () -> fineractClient.savingsAccount().submitApplication2(createSavingsAccountRequest));
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE, createSavingsAccountResponse); testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE, createSavingsAccountResponse);
} }
@And("Client creates a new USD savings account with {string} submitted on date") @And("Client creates a new USD savings account with {string} submitted on date")
public void createSavingsAccountUSD(String submittedOnDate) throws IOException { public void createSavingsAccountUSD(String submittedOnDate) {
Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE); PostClientsResponse clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
long clientId = clientResponse.body().getClientId(); long clientId = clientResponse.getClientId();
PostSavingsAccountsRequest createSavingsAccountRequest = SavingsAccountRequestFactory.defaultUSDSavingsAccountRequest() PostSavingsAccountsRequest createSavingsAccountRequest = SavingsAccountRequestFactory.defaultUSDSavingsAccountRequest()
.clientId(clientId).submittedOnDate(submittedOnDate); .clientId(clientId).submittedOnDate(submittedOnDate);
Response<PostSavingsAccountsResponse> createSavingsAccountResponse = savingsAccountApi PostSavingsAccountsResponse createSavingsAccountResponse = ok(
.submitApplication2(createSavingsAccountRequest).execute(); () -> fineractClient.savingsAccount().submitApplication2(createSavingsAccountRequest));
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE, createSavingsAccountResponse); testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE, createSavingsAccountResponse);
} }
@And("Approve EUR savings account on {string} date") @And("Approve EUR savings account on {string} date")
public void approveEurSavingsAccount(String approvedOnDate) throws IOException { public void approveEurSavingsAccount(String approvedOnDate) {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext() PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE); long savingsAccountID = savingsAccountResponse.getSavingsId();
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountsAccountIdRequest approveSavingsAccountRequest = SavingsAccountRequestFactory.defaultApproveRequest() PostSavingsAccountsAccountIdRequest approveSavingsAccountRequest = SavingsAccountRequestFactory.defaultApproveRequest()
.approvedOnDate(approvedOnDate); .approvedOnDate(approvedOnDate);
Response<PostSavingsAccountsAccountIdResponse> approveSavingsAccountResponse = savingsAccountApi PostSavingsAccountsAccountIdResponse approveSavingsAccountResponse = ok(() -> fineractClient.savingsAccount()
.handleCommands6(savingsAccountID, approveSavingsAccountRequest, "approve").execute(); .handleCommands6(savingsAccountID, approveSavingsAccountRequest, Map.of("command", "approve")));
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_APPROVE_RESPONSE, approveSavingsAccountResponse); testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_APPROVE_RESPONSE, approveSavingsAccountResponse);
} }
@And("Approve USD savings account on {string} date") @And("Approve USD savings account on {string} date")
public void approveUsdSavingsAccount(String approvedOnDate) throws IOException { public void approveUsdSavingsAccount(String approvedOnDate) {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext() PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE); long savingsAccountID = savingsAccountResponse.getSavingsId();
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountsAccountIdRequest approveSavingsAccountRequest = SavingsAccountRequestFactory.defaultApproveRequest() PostSavingsAccountsAccountIdRequest approveSavingsAccountRequest = SavingsAccountRequestFactory.defaultApproveRequest()
.approvedOnDate(approvedOnDate); .approvedOnDate(approvedOnDate);
Response<PostSavingsAccountsAccountIdResponse> approveSavingsAccountResponse = savingsAccountApi PostSavingsAccountsAccountIdResponse approveSavingsAccountResponse = ok(() -> fineractClient.savingsAccount()
.handleCommands6(savingsAccountID, approveSavingsAccountRequest, "approve").execute(); .handleCommands6(savingsAccountID, approveSavingsAccountRequest, Map.of("command", "approve")));
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_APPROVE_RESPONSE, approveSavingsAccountResponse); testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_APPROVE_RESPONSE, approveSavingsAccountResponse);
} }
@And("Activate EUR savings account on {string} date") @And("Activate EUR savings account on {string} date")
public void activateSavingsAccount(String activatedOnDate) throws IOException { public void activateSavingsAccount(String activatedOnDate) {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext() PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE); long savingsAccountID = savingsAccountResponse.getSavingsId();
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountsAccountIdRequest activateSavingsAccountRequest = SavingsAccountRequestFactory.defaultActivateRequest() PostSavingsAccountsAccountIdRequest activateSavingsAccountRequest = SavingsAccountRequestFactory.defaultActivateRequest()
.activatedOnDate(activatedOnDate); .activatedOnDate(activatedOnDate);
Response<PostSavingsAccountsAccountIdResponse> activateSavingsAccountResponse = savingsAccountApi PostSavingsAccountsAccountIdResponse activateSavingsAccountResponse = ok(() -> fineractClient.savingsAccount()
.handleCommands6(savingsAccountID, activateSavingsAccountRequest, "activate").execute(); .handleCommands6(savingsAccountID, activateSavingsAccountRequest, Map.of("command", "activate")));
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_ACTIVATED_RESPONSE, activateSavingsAccountResponse); testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_ACTIVATED_RESPONSE, activateSavingsAccountResponse);
} }
@And("Activate USD savings account on {string} date") @And("Activate USD savings account on {string} date")
public void activateUsdSavingsAccount(String activatedOnDate) throws IOException { public void activateUsdSavingsAccount(String activatedOnDate) {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext() PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE); long savingsAccountID = savingsAccountResponse.getSavingsId();
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountsAccountIdRequest activateSavingsAccountRequest = SavingsAccountRequestFactory.defaultActivateRequest() PostSavingsAccountsAccountIdRequest activateSavingsAccountRequest = SavingsAccountRequestFactory.defaultActivateRequest()
.activatedOnDate(activatedOnDate); .activatedOnDate(activatedOnDate);
Response<PostSavingsAccountsAccountIdResponse> activateSavingsAccountResponse = savingsAccountApi PostSavingsAccountsAccountIdResponse activateSavingsAccountResponse = ok(() -> fineractClient.savingsAccount()
.handleCommands6(savingsAccountID, activateSavingsAccountRequest, "activate").execute(); .handleCommands6(savingsAccountID, activateSavingsAccountRequest, Map.of("command", "activate")));
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_ACTIVATED_RESPONSE, activateSavingsAccountResponse); testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_ACTIVATED_RESPONSE, activateSavingsAccountResponse);
} }
@And("Client successfully deposits {double} EUR to the savings account on {string} date") @And("Client successfully deposits {double} EUR to the savings account on {string} date")
public void createEurDeposit(double depositAmount, String depositDate) throws IOException { public void createEurDeposit(double depositAmount, String depositDate) {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext() PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE); long savingsAccountID = savingsAccountResponse.getSavingsId();
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountTransactionsRequest depositRequest = SavingsAccountRequestFactory.defaultDepositRequest() PostSavingsAccountTransactionsRequest depositRequest = SavingsAccountRequestFactory.defaultDepositRequest()
.transactionDate(depositDate).transactionAmount(BigDecimal.valueOf(depositAmount)); .transactionDate(depositDate).transactionAmount(BigDecimal.valueOf(depositAmount));
Response<PostSavingsAccountTransactionsResponse> depositResponse = savingsAccountTransactionsApi PostSavingsAccountTransactionsResponse depositResponse = ok(() -> fineractClient.savingsAccountTransactions()
.transaction2(savingsAccountID, depositRequest, "deposit").execute(); .transaction2(savingsAccountID, depositRequest, Map.of("command", "deposit")));
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_DEPOSIT_RESPONSE, depositResponse); testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_DEPOSIT_RESPONSE, depositResponse);
} }
@And("Client successfully deposits {double} USD to the savings account on {string} date") @And("Client successfully deposits {double} USD to the savings account on {string} date")
public void createUsdDeposit(double depositAmount, String depositDate) throws IOException { public void createUsdDeposit(double depositAmount, String depositDate) {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext() PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE); long savingsAccountID = savingsAccountResponse.getSavingsId();
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountTransactionsRequest depositRequest = SavingsAccountRequestFactory.defaultDepositRequest() PostSavingsAccountTransactionsRequest depositRequest = SavingsAccountRequestFactory.defaultDepositRequest()
.transactionDate(depositDate).transactionAmount(BigDecimal.valueOf(depositAmount)); .transactionDate(depositDate).transactionAmount(BigDecimal.valueOf(depositAmount));
Response<PostSavingsAccountTransactionsResponse> depositResponse = savingsAccountTransactionsApi PostSavingsAccountTransactionsResponse depositResponse = ok(() -> fineractClient.savingsAccountTransactions()
.transaction2(savingsAccountID, depositRequest, "deposit").execute(); .transaction2(savingsAccountID, depositRequest, Map.of("command", "deposit")));
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_DEPOSIT_RESPONSE, depositResponse); testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_DEPOSIT_RESPONSE, depositResponse);
} }
@And("Client successfully withdraw {double} EUR from the savings account on {string} date") @And("Client successfully withdraw {double} EUR from the savings account on {string} date")
public void createEurWithdraw(double withdrawAmount, String transcationDate) throws IOException { public void createEurWithdraw(double withdrawAmount, String transcationDate) {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext() PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE); long savingsAccountID = savingsAccountResponse.getSavingsId();
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountTransactionsRequest withdrawRequest = SavingsAccountRequestFactory.defaultWithdrawRequest() PostSavingsAccountTransactionsRequest withdrawRequest = SavingsAccountRequestFactory.defaultWithdrawRequest()
.transactionDate(transcationDate).transactionAmount(BigDecimal.valueOf(withdrawAmount)); .transactionDate(transcationDate).transactionAmount(BigDecimal.valueOf(withdrawAmount));
Response<PostSavingsAccountTransactionsResponse> withdrawalResponse = savingsAccountTransactionsApi PostSavingsAccountTransactionsResponse withdrawalResponse = ok(() -> fineractClient.savingsAccountTransactions()
.transaction2(savingsAccountID, withdrawRequest, "withdrawal").execute(); .transaction2(savingsAccountID, withdrawRequest, Map.of("command", "withdrawal")));
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_WITHDRAW_RESPONSE, withdrawalResponse); testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_WITHDRAW_RESPONSE, withdrawalResponse);
} }
@And("Client successfully withdraw {double} USD from the savings account on {string} date") @And("Client successfully withdraw {double} USD from the savings account on {string} date")
public void createUsdWithdraw(double withdrawAmount, String transcationDate) throws IOException { public void createUsdWithdraw(double withdrawAmount, String transcationDate) {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext() PostSavingsAccountsResponse savingsAccountResponse = testContext().get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE); long savingsAccountID = savingsAccountResponse.getSavingsId();
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountTransactionsRequest withdrawRequest = SavingsAccountRequestFactory.defaultWithdrawRequest() PostSavingsAccountTransactionsRequest withdrawRequest = SavingsAccountRequestFactory.defaultWithdrawRequest()
.transactionDate(transcationDate).transactionAmount(BigDecimal.valueOf(withdrawAmount)); .transactionDate(transcationDate).transactionAmount(BigDecimal.valueOf(withdrawAmount));
Response<PostSavingsAccountTransactionsResponse> withdrawalResponse = savingsAccountTransactionsApi PostSavingsAccountTransactionsResponse withdrawalResponse = ok(() -> fineractClient.savingsAccountTransactions()
.transaction2(savingsAccountID, withdrawRequest, "withdrawal").execute(); .transaction2(savingsAccountID, withdrawRequest, Map.of("command", "withdrawal")));
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_WITHDRAW_RESPONSE, withdrawalResponse); testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_WITHDRAW_RESPONSE, withdrawalResponse);
} }
} }
@@ -19,6 +19,7 @@
package org.apache.fineract.test.support; package org.apache.fineract.test.support;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale;
import java.util.function.Function; import java.util.function.Function;
public final class EnumResolver { 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) { 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_UNDO_DISBURSE_RESPONSE = "loanUndoDisburseResponse";
public static final String LOAN_REPAYMENT_RESPONSE = "loanRepaymentResponse"; public static final String LOAN_REPAYMENT_RESPONSE = "loanRepaymentResponse";
public static final String LOAN_PAYMENT_TRANSACTION_RESPONSE = "loanPaymentTransactionResponse"; 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 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_RESPONSE = "loanReAgingResponse";
public static final String LOAN_REAGING_UNDO_RESPONSE = "loanReAgingUndoResponse"; public static final String LOAN_REAGING_UNDO_RESPONSE = "loanReAgingUndoResponse";
public static final String LOAN_REAGING_PREVIEW_RESPONSE = "loanReAgingPreviewResponse"; 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 LOAN_CHARGE_OFF_UNDO_RESPONSE = "loanChargeOffUndoResponse";
public static final String CHARGE_FOR_LOAN_TRANCHE_DISBURSEMENT_CHARGE_FLAT_CREATE_RESPONSE = "ChargeForLoanTrancheDisbursementChargeCreateResponse"; 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_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_RESPONSE = "assetExternalizationResponse";
public static final String ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED = "assetExternalizationTransferExternalIdUserGenerated"; public static final String ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED = "assetExternalizationTransferExternalIdUserGenerated";
public static final String ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_FROM_RESPONSE = "assetExternalizationTransferExternalIdFromResponse"; public static final String ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_FROM_RESPONSE = "assetExternalizationTransferExternalIdFromResponse";
+1
View File
@@ -32,6 +32,7 @@ repositories {
dependencies { dependencies {
testImplementation(project(':fineract-avro-schemas')) testImplementation(project(':fineract-avro-schemas'))
testImplementation(project(':fineract-client')) testImplementation(project(':fineract-client'))
testImplementation(project(':fineract-client-feign'))
testImplementation(project(':fineract-e2e-tests-core').sourceSets.test.output) testImplementation(project(':fineract-e2e-tests-core').sourceSets.test.output)
testImplementation 'org.springframework:spring-context' testImplementation 'org.springframework:spring-context'
@@ -701,7 +701,7 @@ Feature: LoanReschedule
| rescheduleFromDate | submittedOnDate | adjustedDueDate | approvedOnDate | enclosingTransaction | | rescheduleFromDate | submittedOnDate | adjustedDueDate | approvedOnDate | enclosingTransaction |
| 16 January 2024 | 10 January 2024 | 31 January 2024 | 10 January 2024 | true | | 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 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 sets the business date to "01 January 2024"
When Admin creates a client with random data When Admin creates a client with random data
@@ -45,7 +45,7 @@ public class FeignBusinessDateHelper {
.dateFormat("yyyy-MM-dd")// .dateFormat("yyyy-MM-dd")//
.locale(LoanTestData.LOCALE); .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) { public void runAt(String date, Runnable action) {
@@ -45,7 +45,7 @@ public class FeignLoanHelper {
} }
public Long applyForLoan(PostLoansRequest request) { 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(); return response.getLoanId();
} }