FINERACT-1508: Add Cucumber test suppport

This commit is contained in:
Aleksandar Vidakovic
2022-03-18 09:23:15 +01:00
committed by Arnold Gálovics
parent 56824802b3
commit 5232656a6e
82 changed files with 1238 additions and 2978 deletions
+24 -11
View File
@@ -40,7 +40,7 @@ buildscript {
}
}
repositories {
maven { url "https://plugins.gradle.org/m2/" }
maven { url 'https://plugins.gradle.org/m2/' }
}
dependencies {
@@ -71,11 +71,12 @@ plugins {
id 'org.asciidoctor.jvm.revealjs' version '3.3.2' apply false
id 'org.asciidoctor.jvm.gems' version '3.3.2' apply false
id 'org.asciidoctor.kindlegen.base' version '3.2.0' apply false
id "com.google.cloud.tools.jib" version "3.2.0" apply false
id "com.google.cloud.tools.jib" version '3.2.0' apply false
id 'fr.brouillard.oss.gradle.jgitver' version '0.10.0-rc03'
id "org.sonarqube" version "3.3"
id "com.github.andygoossens.modernizer" version "1.6.2" apply false
id 'org.sonarqube' version '3.3'
id 'com.github.andygoossens.modernizer' version '1.6.2' apply false
id 'com.github.spotbugs' version '5.0.6' apply false
id 'se.thinkcode.cucumber-runner' version '0.0.10' apply false
}
description = '''\
@@ -114,6 +115,7 @@ allprojects {
mavenBom 'org.springframework.boot:spring-boot-dependencies:2.6.4'
mavenBom 'org.junit:junit-bom:5.8.2'
mavenBom 'com.fasterxml.jackson:jackson-bom:2.13.2'
mavenBom 'io.cucumber:cucumber-bom:7.2.3'
}
dependencies {
@@ -153,6 +155,7 @@ allprojects {
dependency 'org.webjars.npm:swagger-ui-dist:4.6.2'
dependency 'org.webjars:webjars-locator-core:0.50'
dependency 'com.icegreen:greenmail-junit5:1.6.7'
dependency 'com.intuit.karate:karate-junit5:1.2.0.RC4'
// fineract client dependencies
dependency "com.squareup.retrofit2:retrofit:$retrofitVersion"
@@ -204,8 +207,8 @@ allprojects {
entry 'poi-ooxml'
entry 'poi-ooxml-schemas'
}
dependencySet(group: 'io.rest-assured', version: '4.5.1')
{
dependencySet(group: 'io.rest-assured', version: '4.5.1') {
entry 'rest-assured'
entry 'json-path'
entry 'xml-path'
@@ -252,7 +255,9 @@ allprojects {
"**/gradlew*",
"**/git.properties",
"**/*.html",
"**/*.mustache",
"**/templates/**",
"**/features/**",
"**/results/**",
"**/package-info.java",
"**/keystore.jks",
"**/static/swagger-ui/**",
@@ -299,8 +304,9 @@ allprojects {
'**/*.txt',
'**/*.log',
'**/fineractdev-eclipse-preferences.epf',
'**/template-expected.html',
'**/template.mustache',
'**/features/**',
'**/templates/**',
'**/results/**',
'**/.classpath',
'**/.project',
'**/.idea/**',
@@ -375,9 +381,10 @@ configure(project.fineractJavaProjects) {
apply plugin: 'eclipse'
apply plugin: 'checkstyle'
apply plugin: 'jacoco'
apply plugin: "net.ltgt.errorprone"
apply plugin: "com.github.spotbugs"
apply plugin: 'net.ltgt.errorprone'
apply plugin: 'com.github.spotbugs'
apply plugin: 'com.github.andygoossens.modernizer'
apply plugin: 'se.thinkcode.cucumber-runner'
/* define the valid syntax level for source files */
sourceCompatibility = JavaVersion.VERSION_17
@@ -618,6 +625,7 @@ configure(project.fineractJavaProjects) {
test {
useJUnitPlatform()
dependsOn 'cucumber'
if (project.hasProperty('excludeTests')) {
filter {
@@ -630,6 +638,11 @@ configure(project.fineractJavaProjects) {
logLevel 'quiet'
}
cucumber {
tags = 'not @ignore'
main = 'io.cucumber.core.cli.Main'
}
// Configuration for spotbugs plugin
// https://github.com/spotbugs/spotbugs-gradle-plugin
+4
View File
@@ -37,6 +37,10 @@ dependencies {
testImplementation(
'org.junit.jupiter:junit-jupiter-api',
'io.cucumber:cucumber-core',
'io.cucumber:cucumber-java',
'io.cucumber:cucumber-java8',
'io.cucumber:cucumber-junit-platform-engine',
'com.google.truth:truth',
'com.google.truth.extensions:truth-java8-extension'
)
@@ -0,0 +1,20 @@
#
# 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.
#
cucumber.publish.quiet=true
+10 -6
View File
@@ -8,19 +8,23 @@ include::preface.adoc[]
include::colophon.adoc[]
include::01_introduction.adoc[]
include::introduction.adoc[]
include::deployment.adoc[]
include::02_architecture.adoc[]
include::architecture.adoc[]
include::03_oauth.adoc[]
include::oauth.adoc[]
// include::04_api.adoc[]
// include::api.adoc[]
include::05_client.adoc[]
include::client.adoc[]
include::06_validation.adoc[]
include::validation.adoc[]
include::testing.adoc[]
include::deployment.adoc[]
include::glossary.adoc[]
+5
View File
@@ -0,0 +1,5 @@
== Testing
=== Cucumber
TBD.
+5
View File
@@ -128,6 +128,11 @@ dependencies {
testImplementation( 'org.mockito:mockito-core',
'org.mockito:mockito-junit-jupiter',
'org.junit.platform:junit-platform-runner', // required to be able to run tests directly under Eclipse, see FINERACT-943 & FINERACT-1021
'io.cucumber:cucumber-core',
'io.cucumber:cucumber-java',
'io.cucumber:cucumber-java8',
'io.cucumber:cucumber-junit-platform-engine',
'io.cucumber:cucumber-spring',
'io.github.classgraph:classgraph'
)
testImplementation ('org.springframework.boot:spring-boot-starter-test') {
@@ -20,7 +20,6 @@ package org.apache.fineract.infrastructure.core.service.migration;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import com.zaxxer.hikari.HikariDataSource;
import java.util.List;
import java.util.function.Function;
import javax.sql.DataSource;
@@ -48,7 +47,7 @@ public class TenantDatabaseUpgradeService implements InitializingBean {
private static final String TENANT_DB_CONTEXT = "tenant_db";
private final TenantDetailsService tenantDetailsService;
private final HikariDataSource tenantDataSource;
private final DataSource tenantDataSource;
private final FineractProperties fineractProperties;
private final TenantDatabaseStateVerifier databaseStateVerifier;
private final ExtendedSpringLiquibaseFactory liquibaseFactory;
@@ -56,7 +55,7 @@ public class TenantDatabaseUpgradeService implements InitializingBean {
@Autowired
public TenantDatabaseUpgradeService(final TenantDetailsService detailsService,
@Qualifier("hikariTenantDataSource") final HikariDataSource tenantDataSource, final FineractProperties fineractProperties,
@Qualifier("hikariTenantDataSource") final DataSource tenantDataSource, final FineractProperties fineractProperties,
TenantDatabaseStateVerifier databaseStateVerifier, ExtendedSpringLiquibaseFactory liquibaseFactory,
TenantDataSourceFactory tenantDataSourceFactory) {
this.tenantDetailsService = detailsService;
@@ -595,7 +595,7 @@ public class LoanProduct extends AbstractPersistableCustom {
}
}
protected LoanProduct() {
public LoanProduct() {
this.loanProductRelatedDetail = null;
this.loanProductMinMaxConstraints = null;
}
@@ -16,25 +16,30 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.configuration.spring;
package org.apache.fineract;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.apache.fineract.infrastructure.core.boot.AbstractApplicationConfiguration;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.database.DatabaseIndependentQueryService;
import org.apache.fineract.infrastructure.core.service.migration.ExtendedSpringLiquibaseFactory;
import org.apache.fineract.infrastructure.core.service.migration.TenantDataSourceFactory;
import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseStateVerifier;
import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService;
import org.apache.fineract.infrastructure.jobs.service.JobRegisterService;
import org.apache.fineract.infrastructure.security.service.TenantDetailsService;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Spring @Configuration which does not require a running database. It also does not load any job configuration (as they
@@ -42,14 +47,9 @@ import org.springframework.security.crypto.password.PasswordEncoder;
* (and faster to run such tests).
*/
@EnableConfigurationProperties({ FineractProperties.class })
public class TestsWithoutDatabaseAndNoJobsConfiguration extends AbstractApplicationConfiguration {
@Bean
public HikariConfig hikariConfig() {
HikariConfig mock = mock(HikariConfig.class);
when(mock.getDriverClassName()).thenReturn("org.mariadb.jdbc.Driver");
return mock;
}
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class TestConfiguration extends AbstractApplicationConfiguration {
@Bean
public TenantDataSourceFactory tenantDataSourceFactory() {
@@ -62,19 +62,43 @@ public class TestsWithoutDatabaseAndNoJobsConfiguration extends AbstractApplicat
};
}
@Bean
public HikariDataSource tenantDataSource() {
return mock(HikariDataSource.class, Mockito.RETURNS_MOCKS);
}
@Bean
public TenantDetailsService tenantDetailsService() {
return mock(TenantDetailsService.class, Mockito.RETURNS_MOCKS);
}
@Bean
public ExtendedSpringLiquibaseFactory liquibaseFactory() {
return mock(ExtendedSpringLiquibaseFactory.class, Mockito.RETURNS_MOCKS);
}
@Bean
public DatabaseIndependentQueryService databaseIndependentQueryService() {
return mock(DatabaseIndependentQueryService.class, Mockito.RETURNS_MOCKS);
}
@Bean
public TenantDatabaseStateVerifier tenantDatabaseStateVerifier(DatabaseIndependentQueryService databaseIndependentQueryService,
LiquibaseProperties liquibaseProperties, FineractProperties fineractProperties) {
return new TenantDatabaseStateVerifier(liquibaseProperties, databaseIndependentQueryService);
}
/**
* Override TenantDatabaseUpgradeService binding, because the real one has a @PostConstruct upgradeAllTenants()
* which accesses the database on start-up.
*/
@Bean
public TenantDatabaseUpgradeService tenantDatabaseUpgradeService() {
return new TenantDatabaseUpgradeService(null, null, null, null, null, null) {
@Override
public void afterPropertiesSet() {
// NOOP
}
};
public TenantDatabaseUpgradeService tenantDatabaseUpgradeService(TenantDetailsService tenantDetailsService,
HikariDataSource tenantDataSource, TenantDatabaseStateVerifier tenantDatabaseStateVerifier,
ExtendedSpringLiquibaseFactory liquibaseFactory, TenantDataSourceFactory tenantDataSourceFactory,
FineractProperties fineractProperties) {
return new TenantDatabaseUpgradeService(tenantDetailsService, tenantDataSource, fineractProperties, tenantDatabaseStateVerifier,
liquibaseFactory, tenantDataSourceFactory);
}
/**
@@ -95,10 +119,4 @@ public class TestsWithoutDatabaseAndNoJobsConfiguration extends AbstractApplicat
DataSource mockDataSource = mock(DataSource.class, Mockito.RETURNS_MOCKS);
return mockDataSource;
}
@Bean
@Primary
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@@ -16,23 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.core.boot.tests;
package org.apache.fineract;
import org.apache.fineract.ServerWithMariaDB4jApplication;
import io.cucumber.spring.CucumberContextConfiguration;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
@CucumberContextConfiguration
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = ServerWithMariaDB4jApplication.Configuration.class)
@TestPropertySource("classpath:application-test.properties")
@WebAppConfiguration
public abstract class AbstractSpringBootWithMariaDB4jIntegrationTest {
// do NOT put any helper methods here!
// it's much better to use composition instead of inheritance
// so write a test util ("fixture") and use it as a field in your test
}
@ContextConfiguration(classes = TestConfiguration.class)
// @SpringBootTest(classes = TestConfiguration.class)
public class TestSuite {}
@@ -18,14 +18,23 @@
*/
package org.apache.fineract.accounting.common;
import org.junit.jupiter.api.Test;
import org.springframework.util.Assert;
import static org.assertj.core.api.Assertions.assertThat;
public class AccountingConstantsTest {
import io.cucumber.java8.En;
import java.util.List;
import org.apache.fineract.accounting.financialactivityaccount.data.FinancialActivityData;
@Test
public void testGetAllFinancialActivities() {
Assert.notEmpty(AccountingConstants.FinancialActivity.getAllFinancialActivities(),
"static initialization of collection of all enums is broken");
public class AccountingFinanciaActivityStepDefinitions implements En {
private List<FinancialActivityData> data;
public AccountingFinanciaActivityStepDefinitions() {
Given("All financial activities", () -> {
this.data = AccountingConstants.FinancialActivity.getAllFinancialActivities();
});
Then("The they should not be empty", () -> {
assertThat(data).isNotEmpty();
});
}
}
@@ -1,64 +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.batch;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.UriInfo;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.batch.service.BatchApiService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
/**
* Runs a unit test for BatchApiResource by mocking the BatchRequest and BatchApiService objects.
*
* @author Rishabh Shukla
*/
public class BatchBuilder {
// Verify a non-empty response by BatchApiResource.
@Test
public void batchApiTest() {
// Mock a BatchRequest objects
final BatchRequest batchTest = Mockito.mock(BatchRequest.class);
// Mock a BatchApiService object
final BatchApiService serviceTest = Mockito.mock(BatchApiService.class);
// Mock a UriInfo object
final UriInfo uriInfo = Mockito.mock(UriInfo.class);
final List<BatchRequest> requestList = new ArrayList<>();
requestList.add(batchTest);
// Call the BatchApiService using mocked objects
final List<BatchResponse> result = serviceTest.handleBatchRequestsWithoutEnclosingTransaction(requestList, uriInfo);
// Verifies whether handleBatchRequests() function of BatchApiService
// was called
Mockito.verify(serviceTest).handleBatchRequestsWithoutEnclosingTransaction(requestList, uriInfo);
// Verifies a non-empty response by the BatchApiResource
Assertions.assertNotNull(result);
}
}
@@ -0,0 +1,62 @@
/**
* 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.batch.builder;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import io.cucumber.java8.En;
import java.util.Collections;
import java.util.List;
import javax.ws.rs.core.UriInfo;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.batch.service.BatchApiService;
public class BatchBuilderWithoutEnclosingTransactionStepDefinitions implements En {
private BatchApiService batchApiService;
private List<BatchRequest> requests;
private UriInfo uriInfo;
private List<BatchResponse> responses;
public BatchBuilderWithoutEnclosingTransactionStepDefinitions() {
Given("A batch request", () -> {
this.batchApiService = mock(BatchApiService.class);
this.uriInfo = mock(UriInfo.class);
this.requests = Collections.singletonList(new BatchRequest());
});
When("The user calls the batch service handle request method without enclosing transaction", () -> {
responses = this.batchApiService.handleBatchRequestsWithoutEnclosingTransaction(this.requests, uriInfo);
});
Then("The batch service handle request method without enclosing transaction should have been called", () -> {
assertNotNull(responses);
verify(this.batchApiService).handleBatchRequestsWithoutEnclosingTransaction(requests, uriInfo);
});
}
}
@@ -16,33 +16,34 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.core.exception;
package org.apache.fineract.commands.provider;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.cucumber.java8.En;
import org.apache.fineract.commands.exception.UnsupportedCommandException;
import org.springframework.beans.factory.annotation.Autowired;
class MultiExceptionTest {
public class CommandHandlerExceptionStepDefinitions implements En {
private static Logger LOG = LoggerFactory.getLogger(MultiExceptionTest.class);
@Autowired
private CommandHandlerProvider commandHandlerProvider;
@Test()
void testEmpty() throws MultiException {
assertThrows(IllegalArgumentException.class, () -> {
throw new MultiException(Collections.emptyList());
private String entity;
private String action;
public CommandHandlerExceptionStepDefinitions() {
Given("/^A missing command handler for entity (.*) and action (.*)$/", (String entity, String action) -> {
this.entity = entity;
this.action = action;
});
Then("The system should throw an exception", () -> {
assertThrows(UnsupportedCommandException.class, () -> {
this.commandHandlerProvider.getHandler(entity, action);
});
});
}
@Test()
void test() throws MultiException {
List<Throwable> causes = List.of(new IllegalArgumentException(), new IllegalStateException());
MultiException e = new MultiException(causes);
LOG.warn("Biep, bieb", e);
// Uncomment to see JUnit UI:
// throw e;
}
}
@@ -0,0 +1,52 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.commands.provider;
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.cucumber.java8.En;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
public class CommandHandlerProviderStepDefinitions implements En {
@Autowired
private CommandHandlerProvider commandHandlerProvider;
private NewCommandSourceHandler commandHandler;
private CommandProcessingResult result;
public CommandHandlerProviderStepDefinitions() {
Given("/^A command handler for entity (.*) and action (.*)$/", (String entity, String action) -> {
this.commandHandler = this.commandHandlerProvider.getHandler(entity, action);
});
When("The user processes the command with ID {long}", (Long id) -> {
this.result = commandHandler
.processCommand(JsonCommand.fromExistingCommand(id, null, null, null, null, null, null, null, null, null, null));
});
Then("The command ID matches {long}", (Long id) -> {
assertEquals(id, result.commandId());
});
}
}
@@ -1,67 +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.commands.provider;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.fineract.commands.exception.UnsupportedCommandException;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.configuration.spring.TestsWithoutDatabaseAndNoJobsConfiguration;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
@ExtendWith(SpringExtension.class)
@TestPropertySource("classpath:application-test.properties")
@WebAppConfiguration
@ContextConfiguration(classes = TestsWithoutDatabaseAndNoJobsConfiguration.class)
public class CommandHandlerProviderTest {
@Autowired
private CommandHandlerProvider commandHandlerProvider;
public CommandHandlerProviderTest() {
}
@Test
public void shouldRegisterHandler() {
final Long testCommandId = 815L;
final NewCommandSourceHandler registeredHandler = this.commandHandlerProvider.getHandler("HUMAN", "UPDATE");
final CommandProcessingResult result = registeredHandler
.processCommand(JsonCommand.fromExistingCommand(testCommandId, null, null, null, null, null, null, null, null, null, null));
assertEquals(testCommandId, result.commandId());
}
@Test
public void shouldThrowUnsupportedCommandException() throws UnsupportedCommandException {
Assertions.assertThrows(UnsupportedCommandException.class, () -> {
this.commandHandlerProvider.getHandler("WHATEVER", "DOSOMETHING");
});
}
}
@@ -1,67 +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.infrastructure.classdupes;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.opentest4j.AssertionFailedError;
/**
* JUnit Rule to run detect duplicate entries on the classpath. Usage:
*
* <pre>
* public static {@literal @}ClassRule ClasspathHellDuplicatesCheckRule
* dupes = new ClasspathHellDuplicatesCheckRule();
* </pre>
*
* <p>
* NB that the basepom/duplicate-finder-maven-plugin already runs as part of odlparent. It has a similar purpose, but
* covers build time instead of runtime testing. This JUnit Rule class is thus recommended to be used in particular in
* tests which previously ran into JAR Hell issues, and for which non-regression with a clear failure message in case of
* future similar problems is important. (This provides more details at runtime than duplicate-finder-maven-plugin does
* at build time.)
*
* @author Michael Vorburger.ch
*/
public class ClasspathHellDuplicatesCheckExtension implements AfterEachCallback {
private final ClasspathHellDuplicatesChecker checker;
public ClasspathHellDuplicatesCheckExtension(ClasspathHellDuplicatesChecker checker) {
this.checker = checker;
}
public ClasspathHellDuplicatesCheckExtension() {
this(ClasspathHellDuplicatesChecker.INSTANCE);
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
checkClasspath();
}
protected void checkClasspath() {
Map<String, List<String>> dupes = checker.getDuplicates();
if (!dupes.isEmpty()) {
throw new AssertionFailedError(dupes.size() + " Classpath duplicates detected:\n" + checker.toString(dupes));
}
}
}
@@ -1,38 +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.infrastructure.classdupes;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
/**
* {@link ClasspathHellDuplicatesCheckRule} test.
*
* @author Michael Vorburger
*/
@ExtendWith(ClasspathHellDuplicatesCheckExtension.class)
public class ClasspathHellDuplicatesCheckRuleTest {
// @ClassRule public static ClasspathHellDuplicatesCheckRule jHades = new
// ClasspathHellDuplicatesCheckRule();
@Test // we just need this because JUnit doesn't like a *Test class with
// only a Rule
public void testIfThereAreAnyDuplicatesOnTheClasspath() throws Exception {}
}
@@ -16,8 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.classdupes;
package org.apache.fineract.infrastructure.classpath;
import io.cucumber.java8.En;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ResourceList;
import io.github.classgraph.ScanResult;
@@ -25,75 +26,56 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.opentest4j.AssertionFailedError;
/**
* Check classpath for duplicates.
*
* @author Michael Vorburger.ch
*/
public class ClasspathHellDuplicatesChecker {
public class ClasspathDuplicatesStepDefinitions implements En {
public static final ClasspathHellDuplicatesChecker INSTANCE = new ClasspathHellDuplicatesChecker();
private Map<String, List<String>> duplicates = new HashMap<>();
private final Map<String, List<String>> duplicates;
private ClassGraph classGraph;
public ClasspathHellDuplicatesChecker() {
duplicates = recheck();
}
public ClasspathDuplicatesStepDefinitions() {
Given("A class graph", () -> {
this.classGraph = new ClassGraph();
});
public Map<String, List<String>> getDuplicates() {
return duplicates;
}
public String toString(Map<String, List<String>> map) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
sb.append(entry.getKey());
sb.append('\n');
for (String location : entry.getValue()) {
sb.append(" ");
sb.append(location);
sb.append('\n');
}
}
return sb.toString();
}
private Map<String, List<String>> recheck() {
Map<String, List<String>> dupes = new HashMap<>();
// To debug this scanner, use ClassGraph().verbose()
// We intentionally do not use .classFilesOnly(), or
// .nonClassFilesOnly(), to check both
try (ScanResult scanResult = new ClassGraph().scan()) {
for (Map.Entry<String, ResourceList> dupe : scanResult.getAllResources().findDuplicatePaths()) {
String resourceName = dupe.getKey();
if (!isHarmlessDuplicate(resourceName)) {
boolean addIt = true;
List<String> jars = dupe.getValue().stream().map(resource -> resource.getURL().toExternalForm())
.collect(Collectors.toList());
for (String jar : jars) {
if (skipJAR(jar)) {
addIt = false;
break;
When("The user scans the class graph", () -> {
// nothing to do here
try (ScanResult scanResult = this.classGraph.scan()) {
for (Map.Entry<String, ResourceList> dupe : scanResult.getAllResources().findDuplicatePaths()) {
String resourceName = dupe.getKey();
if (!isHarmlessDuplicate(resourceName)) {
boolean addIt = true;
List<String> jars = dupe.getValue().stream().map(resource -> resource.getURL().toExternalForm())
.collect(Collectors.toList());
for (String jar : jars) {
if (skipJAR(jar)) {
addIt = false;
break;
}
}
if (addIt) {
duplicates.put(resourceName, jars);
}
}
if (addIt) {
dupes.put(resourceName, jars);
}
}
}
return dupes;
}
});
Then("There should be no duplicates", () -> {
if (!duplicates.isEmpty()) {
throw new AssertionFailedError(duplicates.size() + " Classpath duplicates detected:\n" + duplicates);
}
});
}
private boolean skipJAR(String jarPath) {
// ./gradlew test finds classes from the Gradle Wrapper (which don't
// show up in-IDE), exclude those
return jarPath.contains("/.gradle/wrapper/dists/");
return jarPath.contains("/.gradle/wrapper/dists/") || jarPath.contains("/JetBrains/");
}
protected boolean isHarmlessDuplicate(String resourcePath) {
// list from org.jhades.reports.DuplicatesReport
private boolean isHarmlessDuplicate(String resourcePath) {
return resourcePath.equals("META-INF/MANIFEST.MF") || resourcePath.equals("META-INF/INDEX.LIST")
|| resourcePath.equals("META-INF/ORACLE_J.SF") || resourcePath.toUpperCase().startsWith("META-INF/ASL")
|| resourcePath.toUpperCase().startsWith("META-INF/NOTICE") || resourcePath.toUpperCase().startsWith("META-INF/LICENSE")
@@ -105,9 +87,8 @@ public class ClasspathHellDuplicatesChecker {
|| resourcePath.startsWith("META-INF/services") || resourcePath.equals("META-INF/DEPENDENCIES")
|| resourcePath.equals("META-INF/git.properties") || resourcePath.equals("META-INF/io.netty.versions.properties")
|| resourcePath.equals("META-INF/jersey-module-version") || resourcePath.startsWith("OSGI-INF/blueprint/")
|| resourcePath.startsWith("org/opendaylight/blueprint/") || resourcePath.endsWith("reference.conf") // in
// Akka's
// JARs
// in Akka's JARs
|| resourcePath.startsWith("org/opendaylight/blueprint/") || resourcePath.endsWith("reference.conf")
// json-schema-core and json-schema-validator depend on each
// other and include these files
|| resourcePath.equals("draftv4/schema") || resourcePath.equals("draftv3/schema") //
@@ -1,52 +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.infrastructure.configuration.spring;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
/**
* This integration test ensure that the "production" Spring XML configuration files (appContext.xml & Co.) are valid.
* It does not need any database for that.
*
* Note: For a simple test like this, contrary to AbstractSpringBootWithMariaDB4jIntegrationTest, there is no need to
* use Boot's SpringApplicationConfiguration here, instead the simpler classic Sring core ContextConfiguration is
* sufficient (as long as the TestsWithoutDatabaseAndNoJobsConfiguration used extends AbstractApplicationConfiguration).
*/
@ExtendWith(SpringExtension.class)
@TestPropertySource("classpath:application-test.properties")
@WebAppConfiguration
@ContextConfiguration(classes = TestsWithoutDatabaseAndNoJobsConfiguration.class)
public class SpringConfigurationTest {
/**
* This tests if "Aal izz well" (i.e. if we can start-up with the [almost] "production" Spring XML configuration) by
* doing nothing - it doesn't have to, as the SpringJUnit4ClassRunner with this @ContextConfiguration will
* automatically fail if e.g. there is any invalid Spring XML, an invalid bean definition somewhere or anything like
* that.
*
* @see https://www.google.ch/search?q=Aal+izz+well
*/
@Test
public void testSpringXMLConfiguration() {}
}
@@ -0,0 +1,128 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.core;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import io.cucumber.java8.En;
import java.util.List;
import javax.sql.DataSource;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.migration.ExtendedSpringLiquibase;
import org.apache.fineract.infrastructure.core.service.migration.ExtendedSpringLiquibaseFactory;
import org.apache.fineract.infrastructure.core.service.migration.SchemaUpgradeNeededException;
import org.apache.fineract.infrastructure.core.service.migration.TenantDataSourceFactory;
import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseStateVerifier;
import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService;
import org.apache.fineract.infrastructure.security.service.TenantDetailsService;
public class LiquibaseStepDefinitions implements En {
private TenantDataSourceFactory tenantDataSourceFactory;
private TenantDetailsService tenantDetailsService;
private TenantDatabaseStateVerifier databaseStateVerifier;
private FineractProperties fineractProperties;
private ExtendedSpringLiquibaseFactory liquibaseFactory;
private ExtendedSpringLiquibase initialTenantStoreLiquibase;
private ExtendedSpringLiquibase tenantStoreLiquibase;
private ExtendedSpringLiquibase initialTenantLiquibase;
private ExtendedSpringLiquibase tenantLiquibase;
private FineractPlatformTenant defaultTenant;
private DataSource defaultTenantDataSource;
private TenantDatabaseUpgradeService tenantDatabaseUpgradeService;
private List allTenants;
private SchemaUpgradeNeededException result;
public LiquibaseStepDefinitions() {
Given("Liquibase is disabled with a default tenant", () -> {
tenantDataSourceFactory = mock(TenantDataSourceFactory.class);
tenantDetailsService = mock(TenantDetailsService.class);
databaseStateVerifier = mock(TenantDatabaseStateVerifier.class);
fineractProperties = mock(FineractProperties.class);
liquibaseFactory = mock(ExtendedSpringLiquibaseFactory.class);
defaultTenant = mock(FineractPlatformTenant.class);
allTenants = List.of(defaultTenant);
defaultTenantDataSource = mock(DataSource.class);
initialTenantLiquibase = mock(ExtendedSpringLiquibase.class);
tenantLiquibase = mock(ExtendedSpringLiquibase.class);
initialTenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
tenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
given(databaseStateVerifier.isLiquibaseDisabled()).willReturn(true);
given(fineractProperties.getTenant()).willReturn(new FineractProperties.FineractTenantProperties());
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_store_db", "initial_switch"))
.willReturn(initialTenantStoreLiquibase);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_store_db")).willReturn(tenantStoreLiquibase);
given(tenantDetailsService.findAllTenants()).willReturn(allTenants);
given(tenantDataSourceFactory.create(defaultTenant)).willReturn(defaultTenantDataSource);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db", "initial_switch")).willReturn(initialTenantLiquibase);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db")).willReturn(tenantLiquibase);
tenantDatabaseUpgradeService = new TenantDatabaseUpgradeService(tenantDetailsService, defaultTenantDataSource,
fineractProperties, databaseStateVerifier, liquibaseFactory, tenantDataSourceFactory);
});
Given("Liquibase runs the very first time for the tenant store", () -> {
given(databaseStateVerifier.isFirstLiquibaseMigration(defaultTenantDataSource)).willReturn(true);
});
Given("A previously Flyway migrated tenant store database", () -> {
given(databaseStateVerifier.isFlywayPresent(defaultTenantDataSource)).willReturn(true);
});
Given("A previously Flyway migrated tenant store database on an earlier version than 1.6", () -> {
given(databaseStateVerifier.isTenantStoreOnLatestUpgradableVersion(defaultTenantDataSource)).willReturn(false);
});
Given("Liquibase runs the very first time for the default tenant", () -> {
given(databaseStateVerifier.isFirstLiquibaseMigration(defaultTenantDataSource)).willReturn(true);
});
Given("A previously Flyway migrated default tenant database", () -> {
given(databaseStateVerifier.isFlywayPresent(defaultTenantDataSource)).willReturn(true);
});
Given("A previously Flyway migrated default tenant database on the 1.6 version", () -> {
given(databaseStateVerifier.isTenantStoreOnLatestUpgradableVersion(defaultTenantDataSource)).willReturn(true);
});
When("The database migration process is executed", () -> {
// TODO: @galovics will not throw an exception if disabled = true... but if Liquibase is enabled then "Then"
// section (as layed out in original tests) will fail
// this.result = assertThrows(SchemaUpgradeNeededException.class, () ->
// tenantDatabaseUpgradeService.afterPropertiesSet());
tenantDatabaseUpgradeService.afterPropertiesSet();
});
Then("The tenant store upgrade fails with a schema upgrade needed", () -> {
// TODO: @galovics verifications work dependent on Liquibase enabled or not... for what should we test?
// assertThat(result).isNotNull();
// verify(liquibaseFactory).create(eq(defaultTenantDataSource), any());
verifyNoMoreInteractions(liquibaseFactory);
verifyNoInteractions(initialTenantStoreLiquibase, tenantStoreLiquibase, initialTenantLiquibase, tenantLiquibase,
defaultTenantDataSource);
});
}
}
@@ -0,0 +1,51 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.core;
import static org.junit.jupiter.api.Assertions.assertThrows;
import io.cucumber.java8.En;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.exception.MultiException;
public class MultiExceptionStepDefinitions implements En {
private List<Throwable> exceptions = new ArrayList<>();
private MultiException multiException;
public MultiExceptionStepDefinitions() {
Given("/^A multi exception with exceptions (.*) and (.*)$/", (String exception1, String exception2) -> {
if (!StringUtils.isBlank(exception1)) {
this.exceptions.add(Class.forName(exception1).asSubclass(Throwable.class).getDeclaredConstructor().newInstance());
}
if (!StringUtils.isBlank(exception2)) {
this.exceptions.add(Class.forName(exception2).asSubclass(Throwable.class).getDeclaredConstructor().newInstance());
}
});
Then("/^A (.*) should be thrown$/", (String expected) -> {
assertThrows(Class.forName(expected).asSubclass(Throwable.class), () -> {
throw new MultiException(exceptions);
});
});
}
}
@@ -1,49 +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.infrastructure.core.boot.tests;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertThat;
import java.util.List;
import java.util.Map;
import org.apache.fineract.ServerApplication;
import org.apache.fineract.common.RestAssuredFixture;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/**
* This is an integration test for the Spring Boot launch stuff.
*
* @see ServerApplication
*/
public class SpringBootServerLoginTest extends AbstractSpringBootWithMariaDB4jIntegrationTest {
protected RestAssuredFixture util;
@Test
@Disabled("Failing on Cloubees")
public void hasPlatformStarted() {
util = new RestAssuredFixture(8443);
List<Map<String, String>> response = util.httpGet("/users");
assertThat(response.get(0).get("username"), is("mifos"));
}
}
@@ -1,219 +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.infrastructure.core.service.migration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.zaxxer.hikari.HikariDataSource;
import java.util.List;
import javax.sql.DataSource;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.security.service.TenantDetailsService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class TenantDatabaseUpgradeServiceTest {
@Mock
private TenantDetailsService tenantDetailsService;
@Mock
private HikariDataSource tenantDataSource;
@Mock
private FineractProperties fineractProperties;
@Mock
private TenantDatabaseStateVerifier databaseStateVerifier;
@Mock
private ExtendedSpringLiquibaseFactory liquibaseFactory;
@Mock
private TenantDataSourceFactory tenantDataSourceFactory;
private TenantDatabaseUpgradeService underTest;
@BeforeEach
public void setUp() {
underTest = new TenantDatabaseUpgradeService(tenantDetailsService, tenantDataSource, fineractProperties, databaseStateVerifier,
liquibaseFactory, tenantDataSourceFactory);
}
@Test
public void testAfterPropertiesSetDoesNotDoAnythingWhenLiquibaseIsDisabled() throws Exception {
// given
given(databaseStateVerifier.isLiquibaseDisabled()).willReturn(true);
// when
underTest.afterPropertiesSet();
// then
verify(databaseStateVerifier).isLiquibaseDisabled();
verifyNoMoreInteractions(databaseStateVerifier);
verifyNoInteractions(tenantDetailsService, tenantDataSource, fineractProperties, liquibaseFactory, tenantDataSourceFactory);
}
@Test
public void testAfterPropertiesSetGetsLiquibaseAppliedWhenStartingFromScratch() throws Exception {
// given
ExtendedSpringLiquibase initialTenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
ExtendedSpringLiquibase tenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
given(databaseStateVerifier.isLiquibaseDisabled()).willReturn(false);
given(databaseStateVerifier.isFirstLiquibaseMigration(tenantDataSource)).willReturn(true);
given(databaseStateVerifier.isFlywayPresent(tenantDataSource)).willReturn(false);
given(fineractProperties.getTenant()).willReturn(new FineractProperties.FineractTenantProperties());
given(liquibaseFactory.create(tenantDataSource, "tenant_store_db", "initial_switch")).willReturn(initialTenantStoreLiquibase);
given(liquibaseFactory.create(tenantDataSource, "tenant_store_db")).willReturn(tenantStoreLiquibase);
ExtendedSpringLiquibase initialTenantLiquibase = mock(ExtendedSpringLiquibase.class);
ExtendedSpringLiquibase tenantLiquibase = mock(ExtendedSpringLiquibase.class);
FineractPlatformTenant defaultTenant = mock(FineractPlatformTenant.class);
List<FineractPlatformTenant> allTenants = List.of(defaultTenant);
DataSource defaultTenantDataSource = mock(DataSource.class);
given(databaseStateVerifier.isFirstLiquibaseMigration(defaultTenantDataSource)).willReturn(true);
given(databaseStateVerifier.isFlywayPresent(defaultTenantDataSource)).willReturn(false);
given(tenantDetailsService.findAllTenants()).willReturn(allTenants);
given(tenantDataSourceFactory.create(defaultTenant)).willReturn(defaultTenantDataSource);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db", "initial_switch")).willReturn(initialTenantLiquibase);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db")).willReturn(tenantLiquibase);
// when
underTest.afterPropertiesSet();
// then
verify(initialTenantStoreLiquibase).afterPropertiesSet();
verify(tenantStoreLiquibase).afterPropertiesSet();
verify(initialTenantLiquibase).afterPropertiesSet();
verify(tenantLiquibase).afterPropertiesSet();
}
@Test
public void testAfterPropertiesSetSyncsTheInitialChangeLogAndAppliesRemainingChangesWhenFlywayIsOnLatestVersion() throws Exception {
// given
ExtendedSpringLiquibase initialTenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
ExtendedSpringLiquibase tenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
given(databaseStateVerifier.isLiquibaseDisabled()).willReturn(false);
given(databaseStateVerifier.isFirstLiquibaseMigration(tenantDataSource)).willReturn(true);
given(databaseStateVerifier.isFlywayPresent(tenantDataSource)).willReturn(true);
given(databaseStateVerifier.isTenantStoreOnLatestUpgradableVersion(tenantDataSource)).willReturn(true);
given(fineractProperties.getTenant()).willReturn(new FineractProperties.FineractTenantProperties());
given(liquibaseFactory.create(tenantDataSource, "tenant_store_db", "initial_switch")).willReturn(initialTenantStoreLiquibase);
given(liquibaseFactory.create(tenantDataSource, "tenant_store_db")).willReturn(tenantStoreLiquibase);
ExtendedSpringLiquibase initialTenantLiquibase = mock(ExtendedSpringLiquibase.class);
ExtendedSpringLiquibase tenantLiquibase = mock(ExtendedSpringLiquibase.class);
FineractPlatformTenant defaultTenant = mock(FineractPlatformTenant.class);
List<FineractPlatformTenant> allTenants = List.of(defaultTenant);
DataSource defaultTenantDataSource = mock(DataSource.class);
given(databaseStateVerifier.isFirstLiquibaseMigration(defaultTenantDataSource)).willReturn(true);
given(databaseStateVerifier.isFlywayPresent(defaultTenantDataSource)).willReturn(true);
given(databaseStateVerifier.isTenantOnLatestUpgradableVersion(defaultTenantDataSource)).willReturn(true);
given(tenantDetailsService.findAllTenants()).willReturn(allTenants);
given(tenantDataSourceFactory.create(defaultTenant)).willReturn(defaultTenantDataSource);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db", "initial_switch")).willReturn(initialTenantLiquibase);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db")).willReturn(tenantLiquibase);
// when
underTest.afterPropertiesSet();
// then
verify(initialTenantStoreLiquibase).changeLogSync();
verify(tenantStoreLiquibase).afterPropertiesSet();
verify(initialTenantLiquibase).changeLogSync();
verify(tenantLiquibase).afterPropertiesSet();
}
@Test
public void testAfterPropertiesSetFailsWhenTenantStoreFlywayIsNotOnLatestVersion() throws Exception {
// given
ExtendedSpringLiquibase initialTenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
ExtendedSpringLiquibase tenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
given(databaseStateVerifier.isLiquibaseDisabled()).willReturn(false);
given(databaseStateVerifier.isFirstLiquibaseMigration(tenantDataSource)).willReturn(true);
given(databaseStateVerifier.isFlywayPresent(tenantDataSource)).willReturn(true);
given(databaseStateVerifier.isTenantStoreOnLatestUpgradableVersion(tenantDataSource)).willReturn(false);
given(fineractProperties.getTenant()).willReturn(new FineractProperties.FineractTenantProperties());
given(liquibaseFactory.create(tenantDataSource, "tenant_store_db", "initial_switch")).willReturn(initialTenantStoreLiquibase);
given(liquibaseFactory.create(tenantDataSource, "tenant_store_db")).willReturn(tenantStoreLiquibase);
ExtendedSpringLiquibase initialTenantLiquibase = mock(ExtendedSpringLiquibase.class);
ExtendedSpringLiquibase tenantLiquibase = mock(ExtendedSpringLiquibase.class);
FineractPlatformTenant defaultTenant = mock(FineractPlatformTenant.class);
List<FineractPlatformTenant> allTenants = List.of(defaultTenant);
DataSource defaultTenantDataSource = mock(DataSource.class);
given(databaseStateVerifier.isFirstLiquibaseMigration(defaultTenantDataSource)).willReturn(true);
given(databaseStateVerifier.isFlywayPresent(defaultTenantDataSource)).willReturn(true);
given(databaseStateVerifier.isTenantOnLatestUpgradableVersion(defaultTenantDataSource)).willReturn(true);
given(tenantDetailsService.findAllTenants()).willReturn(allTenants);
given(tenantDataSourceFactory.create(defaultTenant)).willReturn(defaultTenantDataSource);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db", "initial_switch")).willReturn(initialTenantLiquibase);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db")).willReturn(tenantLiquibase);
// when
SchemaUpgradeNeededException result = assertThrows(SchemaUpgradeNeededException.class, () -> underTest.afterPropertiesSet());
// then
assertThat(result).isNotNull();
verify(liquibaseFactory).create(eq(tenantDataSource), any());
verifyNoMoreInteractions(liquibaseFactory);
verifyNoInteractions(initialTenantStoreLiquibase, tenantStoreLiquibase, initialTenantLiquibase, tenantLiquibase);
}
@Test
public void testAfterPropertiesSetFailsWhenTenantFlywayIsNotOnLatestVersion() throws Exception {
// given
ExtendedSpringLiquibase initialTenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
ExtendedSpringLiquibase tenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
given(databaseStateVerifier.isLiquibaseDisabled()).willReturn(false);
given(databaseStateVerifier.isFirstLiquibaseMigration(tenantDataSource)).willReturn(true);
given(databaseStateVerifier.isFlywayPresent(tenantDataSource)).willReturn(true);
given(databaseStateVerifier.isTenantStoreOnLatestUpgradableVersion(tenantDataSource)).willReturn(true);
given(fineractProperties.getTenant()).willReturn(new FineractProperties.FineractTenantProperties());
given(liquibaseFactory.create(tenantDataSource, "tenant_store_db", "initial_switch")).willReturn(initialTenantStoreLiquibase);
given(liquibaseFactory.create(tenantDataSource, "tenant_store_db")).willReturn(tenantStoreLiquibase);
ExtendedSpringLiquibase initialTenantLiquibase = mock(ExtendedSpringLiquibase.class);
ExtendedSpringLiquibase tenantLiquibase = mock(ExtendedSpringLiquibase.class);
FineractPlatformTenant defaultTenant = mock(FineractPlatformTenant.class);
List<FineractPlatformTenant> allTenants = List.of(defaultTenant);
DataSource defaultTenantDataSource = mock(DataSource.class);
given(databaseStateVerifier.isFirstLiquibaseMigration(defaultTenantDataSource)).willReturn(true);
given(databaseStateVerifier.isFlywayPresent(defaultTenantDataSource)).willReturn(true);
given(databaseStateVerifier.isTenantOnLatestUpgradableVersion(defaultTenantDataSource)).willReturn(false);
given(tenantDetailsService.findAllTenants()).willReturn(allTenants);
given(tenantDataSourceFactory.create(defaultTenant)).willReturn(defaultTenantDataSource);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db", "initial_switch")).willReturn(initialTenantLiquibase);
given(liquibaseFactory.create(defaultTenantDataSource, "tenant_db")).willReturn(tenantLiquibase);
// when
SchemaUpgradeNeededException result = assertThrows(SchemaUpgradeNeededException.class, () -> underTest.afterPropertiesSet());
// then
assertThat(result).isNotNull();
verify(liquibaseFactory, times(2)).create(eq(tenantDataSource), any());
verify(liquibaseFactory).create(eq(defaultTenantDataSource), any());
verifyNoMoreInteractions(liquibaseFactory);
verify(initialTenantStoreLiquibase).changeLogSync();
verify(tenantStoreLiquibase).afterPropertiesSet();
verifyNoInteractions(initialTenantLiquibase, tenantLiquibase);
}
}
@@ -1,113 +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.infrastructure.security.utils;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
/**
* Unit Test for {@link SQLBuilder}.
*
* @author Michael Vorburger <mike@vorburger.ch>
*/
public class SQLBuilderTest {
@Test
public void testEmpty() {
SQLBuilder sqlBuilder = new SQLBuilder();
assertEquals("", sqlBuilder.getSQLTemplate());
assertArrayEquals(new Object[] {}, sqlBuilder.getArguments());
assertEquals("SQLBuilder{}", sqlBuilder.toString());
}
@Test
public void testUsage() {
SQLBuilder sqlBuilder = new SQLBuilder();
sqlBuilder.addCriteria("name =", "Michael");
sqlBuilder.addCriteria("hobby LIKE ", "Mifos/Apache Fineract");
sqlBuilder.addCriteria("age < ", 123);
assertEquals(" WHERE name = ? AND hobby LIKE ? AND age < ?", sqlBuilder.getSQLTemplate());
assertArrayEquals(new Object[] { "Michael", "Mifos/Apache Fineract", 123 }, sqlBuilder.getArguments());
assertEquals("SQLBuilder{WHERE name = ['Michael'] AND hobby LIKE ['Mifos/Apache Fineract'] AND age < [123]}",
sqlBuilder.toString());
}
@Test
public void testNullArgument() {
SQLBuilder sqlBuilder = new SQLBuilder();
sqlBuilder.addCriteria("ref =", null);
assertEquals(" WHERE ref = ?", sqlBuilder.getSQLTemplate());
assertArrayEquals(new Object[] { null }, sqlBuilder.getArguments());
assertEquals("SQLBuilder{WHERE ref = [null]}", sqlBuilder.toString());
}
@Test
public void testLowerAndUpperCaseOperators() {
SQLBuilder sqlBuilder = new SQLBuilder();
sqlBuilder.addCriteria("hobby LIKE ", "Mifos/Apache Fineract");
sqlBuilder.addCriteria("hobby like ", "Mifos/Apache Fineract");
}
@Test
public void testAddIllegalArguments() throws Exception {
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("age<", 123);
}, "space between column and operator");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria(null, "argument");
}, "null Criteria Fragment");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("", "argument");
}, "empty Criteria Fragment");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria(" ", "argument");
}, "space only Criteria Fragment");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("age = ?", 123);
}, "Criteria Fragment with ?");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("age", 123);
}, "Criteria Fragment missing operator");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("and age = ?", 123);
}, "Criteria starts with AND");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("age = ? and", 123);
}, "Criteria ends with AND");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("or age =", 123);
}, "Criteria starts with OR");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("age = ? or", 123);
}, "Criteria ends with OR");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("(age =", 123);
}, "Criteria contains opening parentheis");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("age = ?)", 123);
}, "Criteria contains closing parentheis");
assertThrows(IllegalArgumentException.class, () -> {
new SQLBuilder().addCriteria("age< = ?)", 123);
}, "Offset corner case");
}
}
@@ -0,0 +1,47 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.sqlbuilder;
import static org.apache.fineract.infrastructure.sqlbuilder.SqlBuilderUtil.transform;
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.cucumber.java8.En;
import org.apache.fineract.infrastructure.security.utils.SQLBuilder;
public class SqlBuilderExceptionStepDefinitions implements En {
private SQLBuilder sqlBuilder;
private Throwable throwable;
public SqlBuilderExceptionStepDefinitions() {
Given("/^An illegal criteria (.*) with argument (.*)$/", (String criteria, String argument) -> {
sqlBuilder = new SQLBuilder();
try {
sqlBuilder.addCriteria(criteria, transform(argument));
} catch (Throwable t) {
this.throwable = t;
}
});
Then("/^The builder should throw an exception (.*) with message (.*)$/", (String exception, String message) -> {
assertEquals(Class.forName(exception), throwable.getClass());
});
}
}
@@ -0,0 +1,85 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.sqlbuilder;
import static org.apache.fineract.infrastructure.sqlbuilder.SqlBuilderUtil.transform;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.cucumber.java8.En;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.security.utils.SQLBuilder;
public class SqlBuilderStepDefinitions implements En {
private SQLBuilder sqlBuilder;
private List<Object> expectedArguments;
public SqlBuilderStepDefinitions() {
Given("/^A criteria (.*), (.*), (.*), (.*) and arguments (.*), (.*), (.*), (.*)$/", (String criteria1, String criteria2,
String criteria3, String criteria4, String argument1, String argument2, String argument3, String argument4) -> {
sqlBuilder = new SQLBuilder();
expectedArguments = new ArrayList<>();
if (!StringUtils.isEmpty(criteria1)) {
sqlBuilder.addCriteria(criteria1, transform(argument1));
}
if (!StringUtils.isEmpty(criteria2)) {
sqlBuilder.addCriteria(criteria2, transform(argument2));
}
if (!StringUtils.isEmpty(criteria3)) {
sqlBuilder.addCriteria(criteria3, transform(argument3));
}
if (!StringUtils.isEmpty(criteria4)) {
sqlBuilder.addCriteria(criteria4, transform(argument4));
}
if (!StringUtils.isBlank(argument1)) {
expectedArguments.add(transform(argument1));
}
if (!StringUtils.isBlank(argument2)) {
expectedArguments.add(transform(argument2));
}
if (!StringUtils.isBlank(argument3)) {
expectedArguments.add(transform(argument3));
}
if (!StringUtils.isBlank(argument4)) {
expectedArguments.add(transform(argument4));
}
});
Then("/^The template should match expected template (.*)$/", (String template) -> {
assertEquals(template.trim(), sqlBuilder.getSQLTemplate().trim());
});
Then("/^The arguments should match expected arguments (.*)$/", (String argLine) -> {
assertArrayEquals(expectedArguments.toArray(new Object[0]), sqlBuilder.getArguments(), sqlBuilder.toString());
});
Then("/^The builder should match (.*)$/", (String expected) -> {
if (!StringUtils.isBlank(expected)) {
assertEquals(expected, sqlBuilder.toString());
}
});
}
}
@@ -0,0 +1,52 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.sqlbuilder;
import org.apache.commons.lang3.StringUtils;
final class SqlBuilderUtil {
private SqlBuilderUtil() {}
static Object transform(String argument) {
if (!StringUtils.isBlank(argument)) {
if ("NULL".equals(argument)) {
return null;
} else if ("EMPTY".equals(argument)) {
return "";
} else if (isInt(argument)) {
return Integer.parseInt(argument);
} else if (isDouble(argument)) {
return Double.parseDouble(argument);
} else {
return argument;
}
}
return null;
}
static boolean isInt(String str) {
return str != null && str.matches("\\d+");
}
static boolean isDouble(String str) {
return str != null && str.matches("\\d+\\.\\d+");
}
}
@@ -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.mix.report;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import io.cucumber.java8.En;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
import java.util.HashMap;
import org.apache.commons.io.IOUtils;
import org.apache.fineract.mix.data.MixTaxonomyData;
import org.apache.fineract.mix.data.NamespaceData;
import org.apache.fineract.mix.service.NamespaceReadPlatformServiceImpl;
import org.apache.fineract.mix.service.XBRLBuilder;
import org.apache.fineract.template.service.TemplateServiceStepDefinitions;
import org.mockito.ArgumentMatchers;
public class MixXbrlBuilderStepDefinitions implements En {
private NamespaceReadPlatformServiceImpl readNamespaceService;
private final XBRLBuilder xbrlBuilder = new XBRLBuilder();
private Date start;
private Date end;
private String currency;
private HashMap<MixTaxonomyData, BigDecimal> data = new HashMap<>();
private String result;
public MixXbrlBuilderStepDefinitions() {
Given("/^The XBRL input parameters start date (.*), end date (.*), currency (.*), taxonomy (.*) and sample (.*)$/",
(String start, String end, String currency, String taxonomy, String sample) -> {
readNamespaceService = mock(NamespaceReadPlatformServiceImpl.class);
lenient().when(this.readNamespaceService.retrieveNamespaceByPrefix(ArgumentMatchers.anyString()))
.thenReturn(new NamespaceData(1L, "mockedprefix", "mockedurl"));
this.start = Date.valueOf(start);
this.end = Date.valueOf(end);
this.currency = currency;
MixTaxonomyData taxonomyData = mock(MixTaxonomyData.class);
when(taxonomyData.getName()).thenReturn(taxonomy);
this.data.put(taxonomyData, BigDecimal.valueOf(Long.parseLong(sample)));
});
When("The user builds the XBRL report", () -> {
result = this.xbrlBuilder.build(data, start, end, currency);
});
Then("/^The XBRL result should match (.*)$/", (String file) -> {
String expected = IOUtils.resourceToString("results/" + file, StandardCharsets.UTF_8,
TemplateServiceStepDefinitions.class.getClassLoader());
assertEquals(expected.trim(), result.trim());
});
}
}
@@ -16,36 +16,37 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.report.service;
package org.apache.fineract.mix.report;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.mockito.Mockito.mock;
import java.util.ArrayList;
import io.cucumber.java8.En;
import java.util.List;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.mix.service.XBRLResultServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.assertj.core.util.Arrays;
@ExtendWith(MockitoExtension.class)
public class ReadTaxonomyMappingServiceImplTest {
public class MixXbrlTaxonomyStepDefinitions implements En {
private XBRLResultServiceImpl readService;
@BeforeEach
public void setUp() throws Exception {
final RoutingDataSource dataSource = Mockito.mock(RoutingDataSource.class);
this.readService = new XBRLResultServiceImpl(dataSource, null, null);
private String template;
private List<String> result;
public MixXbrlTaxonomyStepDefinitions() {
Given("/^A XBRL template (.*)$/", (String template) -> {
this.readService = new XBRLResultServiceImpl(mock(RoutingDataSource.class), null, null);
this.template = template;
});
When("The user resolves GL codes", () -> {
this.result = this.readService.getGLCodes(template);
});
Then("/^The result should contain (.*)$/", (String line) -> {
assertArrayEquals(result.toArray(new String[0]), Arrays.array(line.split(",")));
});
}
@Test
public void shouldCorrectlyGetGLCode() {
final ArrayList<String> result = this.readService.getGLCodes("{12000}+{11000}");
assertEquals("12000", result.get(0));
assertEquals("11000", result.get(1));
}
}
@@ -1,82 +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.mix.report.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
import java.util.HashMap;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.fineract.mix.data.MixTaxonomyData;
import org.apache.fineract.mix.data.NamespaceData;
import org.apache.fineract.mix.service.NamespaceReadPlatformServiceImpl;
import org.apache.fineract.mix.service.XBRLBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
@ExtendWith(MockitoExtension.class)
public class XBRLBuilderTest {
private static final Logger LOG = LoggerFactory.getLogger(XBRLBuilderTest.class);
@Mock
private NamespaceReadPlatformServiceImpl readNamespaceService;
@InjectMocks
private final XBRLBuilder xbrlBuilder = new XBRLBuilder();
@BeforeEach
public void setUp() {
this.readNamespaceService = Mockito.mock(NamespaceReadPlatformServiceImpl.class);
lenient().when(this.readNamespaceService.retrieveNamespaceByPrefix(ArgumentMatchers.anyString()))
.thenReturn(new NamespaceData(1L, "mockedprefix", "mockedurl"));
}
@Test
public void shouldCorrectlyBuildMap() throws SAXException, IOException, ParserConfigurationException {
final HashMap<MixTaxonomyData, BigDecimal> map = new HashMap<MixTaxonomyData, BigDecimal>();
final MixTaxonomyData data1 = Mockito.mock(MixTaxonomyData.class);
when(data1.getName()).thenReturn("Assets");
map.put(data1, new BigDecimal(10000));
final String result = this.xbrlBuilder.build(map, Date.valueOf("2005-11-11"), Date.valueOf("2013-07-17"), "USD");
LOG.info("{}", result);
NodeList nodes = DocumentBuilderFactory.newInstance().newDocumentBuilder()
.parse(new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8))).getElementsByTagName("Assets");
assertNotNull(nodes);
assertNotNull(nodes.item(0));
assertEquals("Assets", nodes.item(0).getNodeName());
}
}
@@ -1,38 +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.notification;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.listener.SessionAwareMessageListener;
public class Listener implements SessionAwareMessageListener {
private static final Logger LOG = LoggerFactory.getLogger(Listener.class);
@Override
public void onMessage(Message message, Session session) throws JMSException {
TextMessage msg = (TextMessage) message;
LOG.info("Received: {}", msg.getText());
}
}
@@ -1,50 +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.notification;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class ListenerTest {
private Listener listener;
private Session session;
private TextMessage textMessageMock;
@BeforeEach
public void setUp() {
listener = new Listener();
session = Mockito.mock(Session.class);
textMessageMock = Mockito.mock(TextMessage.class);
}
@Test
public void testListener() throws JMSException {
Mockito.when(textMessageMock.getText()).thenReturn("content");
listener.onMessage(textMessageMock, session);
Mockito.verify(textMessageMock).getText();
}
}
@@ -1,112 +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.notification;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.refEq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Optional;
import org.apache.fineract.notification.domain.Notification;
import org.apache.fineract.notification.domain.NotificationMapper;
import org.apache.fineract.notification.service.NotificationGeneratorReadRepositoryWrapper;
import org.apache.fineract.notification.service.NotificationGeneratorWritePlatformService;
import org.apache.fineract.notification.service.NotificationMapperWritePlatformService;
import org.apache.fineract.notification.service.NotificationWritePlatformServiceImpl;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.core.userdetails.User;
@ExtendWith(MockitoExtension.class)
public class StorageTest {
private NotificationWritePlatformServiceImpl notificationWritePlatformServiceImpl;
@Mock
private NotificationGeneratorReadRepositoryWrapper notificationGeneratorReadRepositoryWrapper;
@Mock
private NotificationGeneratorWritePlatformService notificationGeneratorWritePlatformService;
@Mock
private NotificationMapperWritePlatformService notificationMapperWritePlatformService;
@Mock
private AppUserRepository appUserRepository;
@BeforeEach
public void setUp() {
notificationWritePlatformServiceImpl = new NotificationWritePlatformServiceImpl(notificationGeneratorWritePlatformService,
notificationGeneratorReadRepositoryWrapper, appUserRepository, notificationMapperWritePlatformService);
}
@Test
@Disabled // TODO FINERACT-828
public void testNotificationStorage() {
Long userId = 1L;
String objectType = "CLIENT";
Long objectIdentifier = 1L;
String action = "created";
Long actor = 1L;
String notificationContent = "A client was created";
boolean isSystemGenerated = false;
String now = getCurrentDateTime();
Notification notification = new Notification(objectType, objectIdentifier, action, actor, isSystemGenerated, notificationContent,
now);
AppUser appUser = new AppUser(null, new User("J.J.", "", true, true, true, true, Collections.emptyList()), null, "user@com", "John",
"", null, false, false, null, null);
NotificationMapper notificationMapper = new NotificationMapper(notification, appUser, false, now);
when(this.notificationGeneratorWritePlatformService.create(refEq(notification))).thenReturn(1L);
when(this.appUserRepository.findById(userId)).thenReturn(Optional.of(appUser));
when(this.notificationGeneratorReadRepositoryWrapper.findById(1L)).thenReturn(notification);
when(this.notificationMapperWritePlatformService.create(refEq(notificationMapper))).thenReturn(1L);
Long actualGeneratedNotificationId = notificationWritePlatformServiceImpl.notify(userId, objectType, objectIdentifier, action,
actor, notificationContent, isSystemGenerated);
verify(this.notificationGeneratorWritePlatformService, times(1)).create(refEq(notification, "createdAt"));
verify(this.notificationMapperWritePlatformService, times(1)).create(refEq(notificationMapper, "createdAt"));
verify(this.notificationGeneratorReadRepositoryWrapper, times(1)).findById(1L);
assertEquals(Long.valueOf(1), actualGeneratedNotificationId);
}
private String getCurrentDateTime() {
Date date = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return formatter.format(date);
}
}
@@ -1,104 +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.notification;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.refEq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Date;
import org.apache.fineract.notification.domain.Topic;
import org.apache.fineract.notification.domain.TopicRepository;
import org.apache.fineract.notification.domain.TopicSubscriber;
import org.apache.fineract.notification.service.TopicSubscriberWritePlatformService;
import org.apache.fineract.notification.service.TopicWritePlatformService;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.office.domain.OfficeRepository;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepository;
import org.apache.fineract.useradministration.domain.Role;
import org.apache.fineract.useradministration.domain.RoleRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class TopicTest {
@Mock
private OfficeRepository officeRepository;
@Mock
private RoleRepository roleRepository;
@Mock
private TopicWritePlatformService topicWritePltfService;
@Mock
private TopicRepository topicRepository;
@Mock
private TopicSubscriberWritePlatformService topicSubscriberWritePltfService;
@Mock
private AppUserRepository appUserRepository;
@Test
public void testTopicStorage() {
Office office = officeRepository.getById(1L);
Role role = new Role("New Member_Type", "Testing topic creation");
Topic topic = topicRepository.getById(1L);
lenient().when(this.officeRepository.getById(1L)).thenReturn(office);
lenient().when(this.topicRepository.getById(1L)).thenReturn(topic);
when(this.roleRepository.save(role)).thenReturn(role);
when(this.topicWritePltfService.create(refEq(topic))).thenReturn(1L);
this.roleRepository.save(role);
Long topicId = this.topicWritePltfService.create(topic);
verify(this.roleRepository, times(1)).save(role);
verify(this.topicWritePltfService, times(1)).create(refEq(topic));
assertEquals(topicId, Long.valueOf(1));
}
@Test
public void testTopicSubscriberStorage() {
AppUser user = appUserRepository.getById(1L);
Topic topic = topicRepository.getById(1L);
TopicSubscriber topicSubscriber = new TopicSubscriber(topic, user, new Date());
lenient().when(this.appUserRepository.getById(1L)).thenReturn(user);
lenient().when(this.topicRepository.getById(1L)).thenReturn(topic);
when(this.topicSubscriberWritePltfService.create(refEq(topicSubscriber))).thenReturn(1L);
Long subscriberId = this.topicSubscriberWritePltfService.create(topicSubscriber);
verify(this.topicSubscriberWritePltfService, times(1)).create(refEq(topicSubscriber));
assertEquals(subscriberId, Long.valueOf(1));
}
}
@@ -1,225 +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.portfolio.loanaccount;
import java.lang.reflect.Field;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@SuppressWarnings("unused")
@ExtendWith(MockitoExtension.class)
public class AdhikarLoanRepaymentScheduleTransactionProcessorTest {
// class under test
private RBILoanRepaymentScheduleTransactionProcessor processor;
private final LocalDate july2nd = LocalDate.of(2012, 7, 2);
private final MonetaryCurrency usDollars = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build();
private List<LoanRepaymentScheduleInstallment> installments;
@BeforeEach
public void setUpForEachTestCase() throws Exception {
Field field = MoneyHelper.class.getDeclaredField("roundingMode");
field.setAccessible(true);
field.set(null, RoundingMode.HALF_EVEN);
this.installments = LoanScheduleTestDataHelper.createSimpleLoanSchedule(this.july2nd, this.usDollars);
this.processor = new RBILoanRepaymentScheduleTransactionProcessor();
}
/**
* Scenario 1: Given no overdue installments, current interest due is paid before principal.
*/
@Test
@Disabled
public void givenNoOverdueInstallmentsOnTimeRepaymentPaysOffInterestDueFirst() {
// // setup
// Money repaymentAmount = new
// MoneyBuilder().with(usDollars).with("100.00").build();
// LoanTransaction onTimePartialRepayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(repaymentAmount).build();
//
// // execute test
// processor.handleTransaction(onTimePartialRepayment, usDollars,
// installments);
//
// // verification of derived fields on installments
// LoanRepaymentScheduleInstallment firstInstallment =
// this.installments.get(0);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("0.00"),
// interestOf("100.00"), interestWaivedOf("0.00"), firstInstallment,
// usDollars);
//
// assertThatLoanTransactionHasDerivedFieldsOf(principalOf("0.00"),
// interestOf("100.00"), onTimePartialRepayment, usDollars);
}
// /**
// * Scenario 2: Given overdue interest exists on overdue installment, a
// late
// * repayment results in overdue interest and current interest being paid
// * before principal component for overdue or current installments.
// */
// @Test
// public void
// givenOverdueInterestOnOverdueInstallmentsLateRepaymentPaysOffOverdueInterestAndCurrentInterestDue()
// {
//
// // setup
// Money repaymentAmount = new
// MoneyBuilder().with(usDollars).with("300.00").build();
// LoanTransaction lateRepayment = new
// LoanTransactionBuilder().repayment().with(july2nd.plusMonths(1)).with(repaymentAmount).build();
//
// // execute test
// processor.handleTransaction(lateRepayment, usDollars, installments);
//
// // verification of derived fields on installments
// LoanRepaymentScheduleInstallment firstInstallment =
// this.installments.get(0);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("0.00"),
// interestOf("200.00"), interestWaivedOf("0.00"), firstInstallment,
// usDollars);
//
// LoanRepaymentScheduleInstallment secondInstallment =
// this.installments.get(1);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("0.00"),
// interestOf("100.00"), interestWaivedOf("0.00"), secondInstallment,
// usDollars);
//
// LoanRepaymentScheduleInstallment thirdInstallment =
// this.installments.get(2);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("0.00"),
// interestOf("0.00"), interestWaivedOf("0.00"), thirdInstallment,
// usDollars);
//
// assertThatLoanTransactionHasDerivedFieldsOf(principalOf("0.00"),
// interestOf("300.00"), lateRepayment, usDollars);
// }
//
// /**
// * Scenario 3: Given an overdue installment with overdue interest and
// principal, a late
// * repayment results in overdue interest and current interest being paid
// * before principal component for overdue or current installments.
// */
// @Test
// public void
// givenAFullyOverdueInstallmentALateRepaymentShouldPayOffOverdueInterestAndCurrentInterestDueFirstThenOverduePrincipal()
// {
//
// // setup
// Money repaymentAmount = new
// MoneyBuilder().with(usDollars).with("900.00").build();
// LoanTransaction lateRepayment = new
// LoanTransactionBuilder().repayment().with(july2nd.plusMonths(1)).with(repaymentAmount).build();
//
// // execute test
// processor.handleTransaction(lateRepayment, usDollars, installments);
//
// // verification of derived fields on installments
// LoanRepaymentScheduleInstallment firstInstallment =
// this.installments.get(0);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("500.00"),
// interestOf("200.00"), interestWaivedOf("0.00"), firstInstallment,
// usDollars);
//
// LoanRepaymentScheduleInstallment secondInstallment =
// this.installments.get(1);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("0.00"),
// interestOf("200.00"), interestWaivedOf("0.00"), secondInstallment,
// usDollars);
//
// LoanRepaymentScheduleInstallment thirdInstallment =
// this.installments.get(2);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("0.00"),
// interestOf("0.00"), interestWaivedOf("0.00"), thirdInstallment,
// usDollars);
//
// assertThatLoanTransactionHasDerivedFieldsOf(principalOf("500.00"),
// interestOf("400.00"), lateRepayment, usDollars);
// }
//
// private void assertThatLoanTransactionHasDerivedFieldsOf(
// final String principal,
// final String interest,
// final LoanTransaction transaction, final MonetaryCurrency currency) {
//
// Money expectedPrincipalPortion = new
// MoneyBuilder().with(currency).with(principal).build();
// assertThat("Principal portion of transaction not as expected: ",
// transaction.getPrincipalPortion(currency).toString(),
// is(expectedPrincipalPortion.toString()));
//
// Money expectedInterestPortion = new
// MoneyBuilder().with(currency).with(interest).build();
// assertThat("Interest portion of transaction not as expected: ",
// transaction.getInterestPortion(currency).toString(),
// is(expectedInterestPortion.toString()));
// }
//
// private void assertThatLoanInstallmentHasDerivedFieldsOf(
// final String principal,
// final String interest,
// final String interestWaived,
// final LoanRepaymentScheduleInstallment installment, final
// MonetaryCurrency currency) {
//
// Money expectedPrincipalCompleted = new
// MoneyBuilder().with(currency).with(principal).build();
// assertThat("principal not as expected",
// installment.getPrincipalCompleted(currency).toString(),
// is(expectedPrincipalCompleted.toString()));
//
// Money expectedInterestCompleted = new
// MoneyBuilder().with(currency).with(interest).build();
// assertThat("interest completed not as expected:
// ",installment.getInterestCompleted(currency).toString(),
// is(expectedInterestCompleted.toString()));
//
// Money expectedInterestWaived = new
// MoneyBuilder().with(currency).with(interestWaived).build();
// assertThat("interest waived not as expected: ",
// installment.getInterestWaived(currency).toString(),
// is(expectedInterestWaived.toString()));
// }
//
// private String principalOf(final String value) {
// return value;
// }
//
// private String interestOf(final String value) {
// return value;
// }
//
// private String interestWaivedOf(final String value) {
// return value;
// }
}
@@ -1,628 +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.portfolio.loanaccount;
import java.lang.reflect.Field;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@SuppressWarnings("unused")
@ExtendWith(MockitoExtension.class)
public class HeavensFamilyLoanRepaymentScheduleTransactionProcessorTest {
// class under test
private HeavensFamilyLoanRepaymentScheduleTransactionProcessor processor;
private final LocalDate july2nd = LocalDate.of(2012, 7, 2);
private final MonetaryCurrency usDollars = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build();
private List<LoanRepaymentScheduleInstallment> installments;
@BeforeEach
public void setUpForEachTestCase() throws Exception {
Field field = MoneyHelper.class.getDeclaredField("roundingMode");
field.setAccessible(true);
field.set(null, RoundingMode.HALF_EVEN);
this.installments = LoanScheduleTestDataHelper.createSimpleLoanSchedule(this.july2nd, this.usDollars);
this.processor = new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
}
/**
* Scenario 1: Single transaction which is less than the interest component of the first installment.
*
* Expectation: - First installment shows interest completed equal to that of transactions, zero principal
* completed. (payment order interest, principal) - transaction has interest portion equal to transaction amount,
* and zero principal portion
*/
@Test
@Disabled
public void givenSingleOnTimeLoanTransactionShouldPayoffInterestComponentFirst() {
// // setup
// LoanRepaymentScheduleInstallment singleInstallment =
// this.installments.get(0);
//
// Money oneHundredDollars = new
// MoneyBuilder().with(usDollars).with("100.00").build();
// LoanTransaction onTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(oneHundredDollars).build();
//
// // execute test
// processor.handleTransaction(onTimePartialPayment, usDollars,
// installments);
//
// // verification of derived fields on installments
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("0.00",
// "100.00", singleInstallment);
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("0.00",
// "100.00", onTimePartialPayment);
}
// /**
// * Scenario 2: Two transactions which is less than the interest component
// of the first installment.
// *
// * Expectation:
// * - First installment shows interest completed equal to that of
// transactions, zero principal completed. (payment order interest,
// principal)
// * - transactions has interest portion equal to transaction amount, and
// zero principal portion
// */
// @Test
// public void
// givenMultipleOnTimeLoanTransactionShouldPayoffInterestComponentFirst() {
//
// // setup
// LoanRepaymentScheduleInstallment singleInstallment =
// this.installments.get(0);
//
// Money oneHundredDollars = new
// MoneyBuilder().with(usDollars).with("100.00").build();
// LoanTransaction onTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(oneHundredDollars).build();
//
// Money fiftyDollars = new
// MoneyBuilder().with(usDollars).with("50.00").build();
// LoanTransaction secondOnTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(fiftyDollars).build();
//
//
// // execute test
// processor.handleTransaction(onTimePartialPayment, usDollars,
// installments);
// processor.handleTransaction(secondOnTimePartialPayment, usDollars,
// installments);
//
// // verification of derived fields on installments
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("0.00",
// "150.00", singleInstallment);
//
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("0.00",
// "100.00", onTimePartialPayment);
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("0.00",
// "50.00", secondOnTimePartialPayment);
// }
//
// /**
// * Scenario 3: Single transaction which is greater than the interest
// component of the first installment but less than the total due for
// installment.
// *
// * Expectation:
// * - First installment shows interest completed equal to that interest
// due, remainder is against principal completed. (payment order interest,
// principal)
// * - transaction has interest portion equal to interest expected and
// principal portion of remainder.
// */
// @Test
// public void
// givenSingleOnTimeLoanTransactionShouldPayoffInterestThenPrincipalComponents()
// {
//
// // setup
// LoanRepaymentScheduleInstallment singleInstallment =
// this.installments.get(0);
//
// Money amount = new MoneyBuilder().with(usDollars).with("300.00").build();
// LoanTransaction onTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(amount).build();
//
// // execute test
// processor.handleTransaction(onTimePartialPayment, usDollars,
// installments);
//
// // verification of derived fields on installments
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("100.00",
// "200.00", singleInstallment);
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("100.00",
// "200.00", onTimePartialPayment);
// }
//
// /**
// * Scenario 4: Single transaction which is greater than the interest
// component of the first installment but less than the total due for
// installment.
// *
// * Expectation:
// * - First installment shows interest completed equal to that interest
// due, remainder is against principal completed. (payment order interest,
// principal)
// * - transaction has interest portion equal to interest expected and
// principal portion of remainder.
// */
// @Test
// public void
// givenMultipleOnTimeLoanTransactionShouldPayoffInterestThenPrincipalComponents()
// {
//
// // setup
// LoanRepaymentScheduleInstallment singleInstallment =
// this.installments.get(0);
//
// Money amount = new MoneyBuilder().with(usDollars).with("300.00").build();
// LoanTransaction onTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(amount).build();
//
// Money secondTransactionAmount = new
// MoneyBuilder().with(usDollars).with("50.00").build();
// LoanTransaction secondOnTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(secondTransactionAmount).build();
//
// // execute test
// processor.handleTransaction(onTimePartialPayment, usDollars,
// installments);
// processor.handleTransaction(secondOnTimePartialPayment, usDollars,
// installments);
//
// // verification of derived fields on installments
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("150.00",
// "200.00", singleInstallment);
//
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("100.00",
// "200.00", onTimePartialPayment);
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("50.00",
// "0.00", secondOnTimePartialPayment);
// }
//
// /**
// * Scenario 5:Single transaction which is equal to the total due for
// installment.
// *
// * Expectation:
// * - First installment shows interest completed and principal completed
// equal to their due amounts. (payment order interest, principal)
// * - transaction has interest portion and principal portions
// */
// @Test
// public void
// givenSingleFullOnTimeLoanTransactionShouldPayoffInterestThenPrincipalComponentInFull()
// {
//
// // setup
// LoanRepaymentScheduleInstallment singleInstallment =
// this.installments.get(0);
//
// Money amount = new
// MoneyBuilder().with(usDollars).with("1200.00").build();
// LoanTransaction onTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(amount).build();
//
// // execute test
// processor.handleTransaction(onTimePartialPayment, usDollars,
// installments);
//
// // verification of derived fields on installments
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("1000.00",
// "200.00", singleInstallment);
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("1000.00",
// "200.00", onTimePartialPayment);
// }
//
// /**
// * Scenario 6: Multiple transactions which together equal to the total due
// for installment.
// *
// * Expectation:
// * - First installment shows interest completed and principal completed
// equal to their due amounts. (payment order interest, principal)
// * - transaction has interest portion and principal portions
// */
// @Test
// public void
// givenMuliptleOnTimeLoanTransactionsThatInFullShouldPayoffInterestThenPrincipalComponentInFull()
// {
//
// // setup
// LoanRepaymentScheduleInstallment singleInstallment =
// this.installments.get(0);
//
// Money amount = new MoneyBuilder().with(usDollars).with("600.00").build();
// LoanTransaction onTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(amount).build();
//
// Money secondTransactionAmount = new
// MoneyBuilder().with(usDollars).with("600.00").build();
// LoanTransaction secondOnTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(secondTransactionAmount).build();
//
// // execute test
// processor.handleTransaction(onTimePartialPayment, usDollars,
// installments);
// processor.handleTransaction(secondOnTimePartialPayment, usDollars,
// installments);
//
// // verification of derived fields on installments
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("1000.00",
// "200.00", singleInstallment);
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("400.00",
// "200.00", onTimePartialPayment);
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("600.00",
// "0.00", secondOnTimePartialPayment);
// }
//
// /**
// * Scenario 7: Single transaction which is greater than total due for
// first installment.
// *
// * Expectation:
// * - First installment shows interest completed and principal completed
// equal to their due amounts. (payment order interest, principal)
// * - Second installment shows principal completed equal to the remaining
// amount
// * - transaction has interest portion and principal portions, the
// principal portion is the sum of principal completed for first and second
// installments
// */
// @Test
// public void
// givenSingleOnTimeLoanTransactionThatOverpaysInstallmentShouldPayoffInterestThenPrincipalComponentInFullOfFirstInstallmentWithRemainingOverpaymentAgainstPrincipalOfNextInstallment()
// {
//
// // setup
// Money amount = new
// MoneyBuilder().with(usDollars).with("1300.00").build();
// LoanTransaction onTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(amount).build();
//
// // execute test
// processor.handleTransaction(onTimePartialPayment, usDollars,
// installments);
//
// // verification of derived fields on installments
// LoanRepaymentScheduleInstallment firstInstallment =
// this.installments.get(0);
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("1000.00",
// "200.00", firstInstallment);
//
// LoanRepaymentScheduleInstallment secondInstallment =
// this.installments.get(1);
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("100.00",
// "0.00", secondInstallment);
//
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("1100.00",
// "200.00", onTimePartialPayment);
// }
//
// /**
// * Scenario 8: Multiple transactions which together are greater than total
// due for first installment.
// *
// * Expectation:
// * - First installment shows interest completed and principal completed
// equal to their due amounts. (payment order interest, principal)
// * - Second installment shows principal completed equal to the remaining
// amount
// */
// @Test
// public void
// givenMultipleOnTimeLoanTransactionThatOverpaysInstallmentShouldPayoffInterestThenPrincipalComponentInFullOfFirstInstallmentWithRemainingOverpaymentAgainstPrincipalOfNextInstallment()
// {
//
// // setup
// Money amount = new MoneyBuilder().with(usDollars).with("600.00").build();
// LoanTransaction onTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(amount).build();
//
// Money secondTransactionAmount = new
// MoneyBuilder().with(usDollars).with("700.00").build();
// LoanTransaction secondOnTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(secondTransactionAmount).build();
//
// // execute test
// processor.handleTransaction(onTimePartialPayment, usDollars,
// installments);
// processor.handleTransaction(secondOnTimePartialPayment, usDollars,
// installments);
//
// // verification of derived fields on installments
// LoanRepaymentScheduleInstallment singleInstallment =
// this.installments.get(0);
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("1000.00",
// "200.00", singleInstallment);
//
// LoanRepaymentScheduleInstallment secondInstallment =
// this.installments.get(1);
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("100.00",
// "0.00", secondInstallment);
//
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("400.00",
// "200.00", onTimePartialPayment);
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("700.00",
// "0.00", secondOnTimePartialPayment);
// }
//
// /**
// * Scenario 9: Single transaction which overpays first installment and
// second installment with outstanding amount been account as principal in
// third installment.
// *
// * Where an installments principal is paid for fully in advance - the
// interest component is waived.
// */
// @Test
// public void
// givenOverPaymentShouldPayoffInterestThenPrincipalComponentsOfFirstInstallmentWithRemainingOverpaymentAgainstPrincipalOfAllSubsequentInstallments()
// {
// // setup
// Money amount = new
// MoneyBuilder().with(usDollars).with("3000.00").build();
// LoanTransaction onTimePartialPayment = new
// LoanTransactionBuilder().repayment().with(july2nd).with(amount).build();
//
// // execute test
// processor.handleTransaction(onTimePartialPayment, usDollars,
// installments);
//
// // verification of derived fields on installments
// LoanRepaymentScheduleInstallment firstInstallment =
// this.installments.get(0);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("1000.00"),
// interestOf("200.00"), interestWaivedOf("0.00"), firstInstallment,
// usDollars);
//
// LoanRepaymentScheduleInstallment secondInstallment =
// this.installments.get(1);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("1000.00"),
// interestOf("0.00"), interestWaivedOf("200.00"), secondInstallment,
// usDollars);
//
// LoanRepaymentScheduleInstallment thirdInstallment =
// this.installments.get(2);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("800.00"),
// interestOf("0.00"), interestWaivedOf("0.00"), thirdInstallment,
// usDollars);
//
// assertThatLoanTransactionHasDerivedFieldsOf(principalOf("2800.00"),
// interestOf("200.00"), onTimePartialPayment, usDollars);
// }
//
// /**
// * Scenario 10: Single transaction which is a repayment in advance
// */
// @Test
// public void
// givenRepaymentOfInstallmentInAdvanceShouldPayoffOnlyPrincipalComponent()
// {
// // setup
// installments =
// LoanScheduleTestDataHelper.createSimpleLoanScheduleWithFirstInstallmentFullyPaid(july2nd,
// usDollars);
// LoanRepaymentScheduleInstallment firstInstallment =
// this.installments.get(0);
// LocalDate paymentInAdvanceDate =
// firstInstallment.getDueDate().minusDays(1);
//
// Money amount = new MoneyBuilder().with(usDollars).with("900.00").build();
// LoanTransaction paymentInAdvance = new
// LoanTransactionBuilder().repayment().with(paymentInAdvanceDate).with(amount).build();
//
// // execute test
// processor.handleTransaction(paymentInAdvance, usDollars, installments);
//
// // verification of derived fields on installments
// LoanRepaymentScheduleInstallment secondInstallment =
// this.installments.get(1);
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("900.00",
// "0.00", secondInstallment);
//
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("900.00",
// "0.00", paymentInAdvance);
// }
//
// /**
// * Scenario 11: Single transaction which is a repayment many days early
// but still does not qualify for 'in advance'.
// */
// @Test
// public void
// givenRepaymentOfInstallmentBeforeDueDateButNotInAdvanceShouldPayoffInterestAndPrincipalComponents()
// {
// // setup
// installments =
// LoanScheduleTestDataHelper.createSimpleLoanScheduleWithFirstInstallmentFullyPaid(july2nd,
// usDollars);
// LoanRepaymentScheduleInstallment firstInstallment =
// this.installments.get(0);
// LocalDate paymentBeforeDueDateButNotInAdvanceDate =
// firstInstallment.getDueDate().plusDays(1);
//
// Money amount = new MoneyBuilder().with(usDollars).with("900.00").build();
// LoanTransaction paymentBeforeDueDateButNotInAdvance = new
// LoanTransactionBuilder().repayment().with(paymentBeforeDueDateButNotInAdvanceDate).with(amount).build();
//
// // execute test
// processor.handleTransaction(paymentBeforeDueDateButNotInAdvance,
// usDollars, installments);
//
// // verification of derived fields on installments
// LoanRepaymentScheduleInstallment secondInstallment =
// this.installments.get(1);
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf("700.00",
// "200.00", secondInstallment);
//
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf("700.00",
// "200.00", paymentBeforeDueDateButNotInAdvance);
// }
//
//
// /**
// * Scenario 12: Single transaction which is a repayment many days early
// but still does not qualify for 'in advance'.
// */
// @Test
// public void
// givenLateRepaymentOfInstallmentShouldPayoffInterestAndPrincipalComponentsOfOverdueInstallmentsWithNoWaiveOfInterest()
// {
// // setup
// installments =
// LoanScheduleTestDataHelper.createSimpleLoanSchedule(july2nd, usDollars);
// LoanRepaymentScheduleInstallment firstInstallment =
// this.installments.get(0);
// LocalDate latePaymentDate = firstInstallment.getDueDate().plusMonths(5);
//
// Money amount = new
// MoneyBuilder().with(usDollars).with("3600.00").build();
// LoanTransaction lateRepaymentTransaction = new
// LoanTransactionBuilder().repayment().with(latePaymentDate).with(amount).build();
//
// // execute test
// processor.handleTransaction(lateRepaymentTransaction, usDollars,
// installments);
//
// // verification of derived fields on installments
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("1000.00"),
// interestOf("200.00"), interestWaivedOf("0.00"), firstInstallment,
// usDollars);
//
// LoanRepaymentScheduleInstallment secondInstallment =
// this.installments.get(1);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("1000.00"),
// interestOf("200.00"), interestWaivedOf("0.00"), secondInstallment,
// usDollars);
//
// LoanRepaymentScheduleInstallment thirdInstallment =
// this.installments.get(2);
// assertThatLoanInstallmentHasDerivedFieldsOf(principalOf("1000.00"),
// interestOf("200.00"), interestWaivedOf("0.00"), thirdInstallment,
// usDollars);
//
// assertThatLoanTransactionHasDerivedFieldsOf(principalOf("3000.00"),
// interestOf("600.00"), lateRepaymentTransaction, usDollars);
// }
//
//
// private void
// assertThatLoanTransactionHasDerivedFieldsForPrincipalInterestOf(
// final String principal, final String interest, final LoanTransaction
// transaction) {
//
// Money expectedInterestPortion = new
// MoneyBuilder().with(usDollars).with(interest).build();
// assertThat(transaction.getInterestPortion(usDollars).toString(),
// is(expectedInterestPortion.toString()));
//
// Money expectedPrincipalPortion = new
// MoneyBuilder().with(usDollars).with(principal).build();
// assertThat(transaction.getPrincipalPortion(usDollars).toString(),
// is(expectedPrincipalPortion.toString()));
// }
//
// private void
// assertThatLoanInstallmentHasDerivedFieldsForPrincipalInterestOf(final
// String principal, final String interest,
// final LoanRepaymentScheduleInstallment installment) {
// Money expectedInterestCompleted = new
// MoneyBuilder().with(usDollars).with(interest).build();
// assertThat(installment.getInterestCompleted(usDollars).toString(),
// is(expectedInterestCompleted.toString()));
//
// Money expectedPrincipalCompleted = new
// MoneyBuilder().with(usDollars).with(principal).build();
// assertThat(installment.getPrincipalCompleted(usDollars).toString(),
// is(expectedPrincipalCompleted.toString()));
// }
//
// private void assertThatLoanTransactionHasDerivedFieldsOf(
// final String principal,
// final String interest,
// final LoanTransaction transaction, final MonetaryCurrency currency) {
//
// Money expectedPrincipalPortion = new
// MoneyBuilder().with(currency).with(principal).build();
// assertThat("Principal portion of transaction not as expected: ",
// transaction.getPrincipalPortion(currency).toString(),
// is(expectedPrincipalPortion.toString()));
//
// Money expectedInterestPortion = new
// MoneyBuilder().with(currency).with(interest).build();
// assertThat("Interest portion of transaction not as expected: ",
// transaction.getInterestPortion(currency).toString(),
// is(expectedInterestPortion.toString()));
// }
//
// private void assertThatLoanInstallmentHasDerivedFieldsOf(
// final String principal,
// final String interest,
// final String interestWaived,
// final LoanRepaymentScheduleInstallment installment, final
// MonetaryCurrency currency) {
//
// Money expectedPrincipalCompleted = new
// MoneyBuilder().with(currency).with(principal).build();
// assertThat("principal not as expected",
// installment.getPrincipalCompleted(currency).toString(),
// is(expectedPrincipalCompleted.toString()));
//
// Money expectedInterestCompleted = new
// MoneyBuilder().with(currency).with(interest).build();
// assertThat("interest completed not as expected:
// ",installment.getInterestCompleted(currency).toString(),
// is(expectedInterestCompleted.toString()));
//
// Money expectedInterestWaived = new
// MoneyBuilder().with(currency).with(interestWaived).build();
// assertThat("interest waived not as expected: ",
// installment.getInterestWaived(currency).toString(),
// is(expectedInterestWaived.toString()));
// }
//
// private String principalOf(final String value) {
// return value;
// }
//
// private String interestOf(final String value) {
// return value;
// }
//
// private String interestWaivedOf(final String value) {
// return value;
// }
}
@@ -1,166 +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.portfolio.loanaccount;
import java.math.BigDecimal;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
/**
* This class is used to keep in one place configurations for setting up {@link LoanProductRelatedDetail} object used in
* {@link LoanScheduleGenerator} 's
*/
public final class LoanProductRelatedDetailTestHelper {
private LoanProductRelatedDetailTestHelper() {
}
public static LoanProductRelatedDetail createSettingsForEqualPrincipalAmortizationQuarterly() {
final MonetaryCurrency currency = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build();
final BigDecimal defaultPrincipal = BigDecimal.valueOf(Double.parseDouble("200000"));
// 2% per month, 24% per year
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.parseDouble("2"));
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.MONTHS;
final BigDecimal defaultAnnualNominalInterestRate = BigDecimal.valueOf(Double.parseDouble("24"));
final InterestMethod interestMethod = InterestMethod.DECLINING_BALANCE;
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD;
final Integer repayEvery = Integer.valueOf(3);
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.MONTHS;
final Integer defaultNumberOfRepayments = Integer.valueOf(4);
final AmortizationMethod amortizationMethod = AmortizationMethod.EQUAL_PRINCIPAL;
final BigDecimal inArrearsTolerance = BigDecimal.ZERO;
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType,
defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance);
}
public static LoanProductRelatedDetail createSettingsForEqualInstallmentAmortizationQuarterly() {
final MonetaryCurrency currency = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build();
final BigDecimal defaultPrincipal = BigDecimal.valueOf(Double.parseDouble("200000"));
// 2% per month, 24% per year
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.parseDouble("2"));
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.MONTHS;
final BigDecimal defaultAnnualNominalInterestRate = BigDecimal.valueOf(Double.parseDouble("24"));
final InterestMethod interestMethod = InterestMethod.DECLINING_BALANCE;
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD;
final Integer repayEvery = Integer.valueOf(3);
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.MONTHS;
final Integer defaultNumberOfRepayments = Integer.valueOf(4);
final AmortizationMethod amortizationMethod = AmortizationMethod.EQUAL_INSTALLMENTS;
final BigDecimal inArrearsTolerance = BigDecimal.ZERO;
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType,
defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance);
}
public static LoanProductRelatedDetail createSettingsForFlatQuarterly(final AmortizationMethod amortizationMethod) {
final MonetaryCurrency currency = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build();
final BigDecimal defaultPrincipal = BigDecimal.valueOf(Double.parseDouble("200000"));
// 2% per month, 24% per year
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.parseDouble("2"));
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.MONTHS;
final BigDecimal defaultAnnualNominalInterestRate = BigDecimal.valueOf(Double.parseDouble("24"));
final InterestMethod interestMethod = InterestMethod.FLAT;
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD;
final Integer repayEvery = Integer.valueOf(3);
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.MONTHS;
final Integer defaultNumberOfRepayments = Integer.valueOf(4);
final BigDecimal inArrearsTolerance = BigDecimal.ZERO;
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType,
defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance);
}
public static LoanProductRelatedDetail createSettingsForIrregularFlatEveryFourMonths() {
final MonetaryCurrency currency = new MonetaryCurrencyBuilder().withCode("KSH").withDigitsAfterDecimal(0).build();
final BigDecimal defaultPrincipal = BigDecimal.valueOf(Double.parseDouble("15000"));
// 2% per month, 24% per year
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(Double.parseDouble("2"));
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.MONTHS;
final BigDecimal defaultAnnualNominalInterestRate = BigDecimal.valueOf(Double.parseDouble("24"));
final InterestMethod interestMethod = InterestMethod.FLAT;
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD;
final Integer repayEvery = Integer.valueOf(3);
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.MONTHS;
final Integer defaultNumberOfRepayments = Integer.valueOf(2);
final BigDecimal inArrearsTolerance = BigDecimal.ZERO;
final AmortizationMethod amortizationMethod = AmortizationMethod.EQUAL_PRINCIPAL;
return createLoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery, repaymentFrequencyType,
defaultNumberOfRepayments, amortizationMethod, inArrearsTolerance);
}
private static LoanProductRelatedDetail createLoanProductRelatedDetail(final MonetaryCurrency currency,
final BigDecimal defaultPrincipal, final BigDecimal defaultNominalInterestRatePerPeriod,
final PeriodFrequencyType interestPeriodFrequencyType, final BigDecimal defaultAnnualNominalInterestRate,
final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
final Integer repayEvery, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfRepayments,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance) {
final Integer graceOnPrincipalPayment = Integer.valueOf(0);
final Integer recurringMoratoriumOnPrincipalPeriods = Integer.valueOf(0);
final Integer graceOnInterestPayment = Integer.valueOf(0);
final Integer graceOnInterestCharged = Integer.valueOf(0);
final Integer graceOnArrearsAgeing = Integer.valueOf(0);
final Integer daysInMonthType = DaysInMonthType.ACTUAL.getValue();
final Integer daysInYearType = DaysInYearType.ACTUAL.getValue();
final boolean isInterestRecalculationEnabled = false;
final boolean considerPartialPeriodInterest = false;
final boolean isEqualAmortization = false;
return new LoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, considerPartialPeriodInterest,
repayEvery, repaymentFrequencyType, defaultNumberOfRepayments, graceOnPrincipalPayment,
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, amortizationMethod,
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
isEqualAmortization);
}
}
@@ -1,87 +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.portfolio.loanaccount;
import java.time.LocalDate;
import java.util.Set;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
public class LoanRepaymentScheduleInstallmentBuilder {
private final Loan loan = null;
private Integer installmentNumber = Integer.valueOf(1);
private final LocalDate fromDate = LocalDate.now(DateUtils.getDateTimeZoneOfTenant());
private LocalDate dueDate = LocalDate.now(DateUtils.getDateTimeZoneOfTenant());
private final LocalDate latestTransactionDate = LocalDate.now(DateUtils.getDateTimeZoneOfTenant());
private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build();
private Money principal = new MoneyBuilder().build();
private Money interest = new MoneyBuilder().build();
private final Money feeCharges = new MoneyBuilder().build();
private final Money penaltyCharges = new MoneyBuilder().build();
private boolean completed = false;
private boolean recalculatedInterestComponent = false;
public LoanRepaymentScheduleInstallmentBuilder(final MonetaryCurrency currencyDetail) {
this.currencyDetail = currencyDetail;
this.principal = new MoneyBuilder().with(currencyDetail).build();
this.interest = new MoneyBuilder().with(currencyDetail).build();
}
public LoanRepaymentScheduleInstallment build() {
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(this.loan, this.installmentNumber,
this.fromDate, this.dueDate, this.principal.getAmount(), this.interest.getAmount(), this.feeCharges.getAmount(),
this.penaltyCharges.getAmount(), this.recalculatedInterestComponent, compoundingDetails);
if (this.completed) {
installment.payPrincipalComponent(this.latestTransactionDate, this.principal);
installment.payInterestComponent(this.latestTransactionDate, this.interest);
}
return installment;
}
public LoanRepaymentScheduleInstallmentBuilder withPrincipal(final String withPrincipal) {
this.principal = new MoneyBuilder().with(this.currencyDetail).with(withPrincipal).build();
return this;
}
public LoanRepaymentScheduleInstallmentBuilder withInterest(final String withInterest) {
this.interest = new MoneyBuilder().with(this.currencyDetail).with(withInterest).build();
return this;
}
public LoanRepaymentScheduleInstallmentBuilder withDueDate(final LocalDate withDueDate) {
this.dueDate = withDueDate;
return this;
}
public LoanRepaymentScheduleInstallmentBuilder completed() {
this.completed = true;
return this;
}
public LoanRepaymentScheduleInstallmentBuilder withInstallmentNumber(final int withInstallmentNumber) {
this.installmentNumber = withInstallmentNumber;
return this;
}
}
@@ -1,83 +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.portfolio.loanaccount;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
/**
* Helper class for creating loan schedule data suitable for testing.
*/
public final class LoanScheduleTestDataHelper {
private LoanScheduleTestDataHelper() {
}
/**
* Creates brand new three installment loan:
*
* For example: with firstDueDate = 02 July 2011
*
* Date Principal Interest Interest Waived ==================================
* ================================================ 02 July 2011 1,000 200 0 02 August 2011 1,000 200 0 02 September
* 2011 1,000 200 0
*/
public static List<LoanRepaymentScheduleInstallment> createSimpleLoanSchedule(final LocalDate firstDueDate,
final MonetaryCurrency currency) {
final LoanRepaymentScheduleInstallment firstInstallment = new LoanRepaymentScheduleInstallmentBuilder(currency)
.withInstallmentNumber(1).withDueDate(firstDueDate).withPrincipal("1000.00").withInterest("200.00").build();
final LoanRepaymentScheduleInstallment secondInstallment = new LoanRepaymentScheduleInstallmentBuilder(currency)
.withInstallmentNumber(2).withDueDate(firstDueDate.plusMonths(1)).withPrincipal("1000.00").withInterest("200.00").build();
final LoanRepaymentScheduleInstallment thirdInstallment = new LoanRepaymentScheduleInstallmentBuilder(currency)
.withInstallmentNumber(3).withDueDate(firstDueDate.plusMonths(2)).withPrincipal("1000.00").withInterest("200.00").build();
return Arrays.asList(firstInstallment, secondInstallment, thirdInstallment);
}
/**
* Creates three installment loan with first installment fully completed:
*
* For example: with firstDueDate = 02 July 2011
*
* Date Principal Interest Interest Waived Completed ========================
* ================================================== ====================================== 02 July 2011 1,000 200
* 0 true (principal paid, interest paid) 02 August 2011 1,000 200 0 false 02 September 2011 1,000 200 0 false
*/
public static List<LoanRepaymentScheduleInstallment> createSimpleLoanScheduleWithFirstInstallmentFullyPaid(final LocalDate firstDueDate,
final MonetaryCurrency currency) {
final LoanRepaymentScheduleInstallment firstInstallment = new LoanRepaymentScheduleInstallmentBuilder(currency)
.withInstallmentNumber(1).withDueDate(firstDueDate).withPrincipal("1000.00").withInterest("200.00").completed().build();
final LoanRepaymentScheduleInstallment secondInstallment = new LoanRepaymentScheduleInstallmentBuilder(currency)
.withInstallmentNumber(2).withDueDate(firstDueDate.plusMonths(1)).withPrincipal("1000.00").withInterest("200.00").build();
final LoanRepaymentScheduleInstallment thirdInstallment = new LoanRepaymentScheduleInstallmentBuilder(currency)
.withInstallmentNumber(3).withDueDate(firstDueDate.plusMonths(2)).withPrincipal("1000.00").withInterest("200.00").build();
return Arrays.asList(firstInstallment, secondInstallment, thirdInstallment);
}
}
@@ -1,58 +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.portfolio.loanaccount;
import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.Money;
public class LoanTransactionBuilder {
@SuppressWarnings("unused")
private Money transactionAmount = new MoneyBuilder().build();
@SuppressWarnings("unused")
private LocalDate transactionDate = LocalDate.now(DateUtils.getDateTimeZoneOfTenant());
@SuppressWarnings("unused")
private boolean repayment = false;
/**
* public LoanTransaction build() {
*
* LoanTransaction transaction = null;
*
* if (repayment) { transaction = LoanTransaction.repayment(transactionAmount, transactionDate); }
*
* return transaction; }
**/
public LoanTransactionBuilder with(final Money newAmount) {
this.transactionAmount = newAmount;
return this;
}
public LoanTransactionBuilder with(final LocalDate withTransactionDate) {
this.transactionDate = withTransactionDate;
return this;
}
public LoanTransactionBuilder repayment() {
this.repayment = true;
return this;
}
}
@@ -1,42 +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.portfolio.loanaccount;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
public class MonetaryCurrencyBuilder {
private String code = "XOF";
private int digitsAfterDecimal = 0;
private final Integer inMultiplesOf = null;
public MonetaryCurrency build() {
return new MonetaryCurrency(this.code, this.digitsAfterDecimal, this.inMultiplesOf);
}
public MonetaryCurrencyBuilder withCode(final String withCode) {
this.code = withCode;
return this;
}
public MonetaryCurrencyBuilder withDigitsAfterDecimal(final int withDigitsAfterDecimal) {
this.digitsAfterDecimal = withDigitsAfterDecimal;
return this;
}
}
@@ -1,43 +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.portfolio.loanaccount;
import java.math.BigDecimal;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
public class MoneyBuilder {
private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build();
private BigDecimal newAmount = BigDecimal.ZERO;
public Money build() {
return Money.of(this.currencyDetail, this.newAmount);
}
public MoneyBuilder with(final MonetaryCurrency withDetail) {
this.currencyDetail = withDetail;
return this;
}
public MoneyBuilder with(final String withAmount) {
this.newAmount = BigDecimal.valueOf(Double.parseDouble(withAmount));
return this;
}
}
@@ -1,134 +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.portfolio.loanaccount;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.File;
import javax.ws.rs.core.UriInfo;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.creditbureau.service.CreditReportWritePlatformServiceImpl;
import org.apache.fineract.infrastructure.creditbureau.service.ThitsaWorksCreditBureauIntegrationWritePlatformServiceImpl;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class ThitsaworksCreditBureauTest {
private FromJsonHelper fromApiJsonHelper = new FromJsonHelper();
@Mock
private ThitsaWorksCreditBureauIntegrationWritePlatformServiceImpl thitsaWorksCreditBureauIntegrationWritePlatformServiceImpl;
@InjectMocks
private final CreditReportWritePlatformServiceImpl creditReportWritePlatformServiceImpl = null;
/*
* @Autowired CreditBureauIntegrationTest(final FromJsonHelper fromApiJsonHelper) { this.fromApiJsonHelper =
* fromApiJsonHelper; }
*/
static String process = "process";
static String nrcID = "13/MiFoS(N)163525";
static String userName = "demomfi1@mifos.com";
static String password = "password";
static String subscriptionKey = "15c15ff17493acb44cb223f2feab2fe4";
static String subscriptionId = "4A7C-317A41BA-1FF8-8A64-F8EDBE0F625D";
static String url = "url";
static String token = "token";
static Long uniqueId = 8113399260L;
static byte[] file;
static File report;
static String fileName;
static UriInfo uriInfo;
static FormDataContentDisposition fileData;
static String testresult = "{ 'Data': {'BorrowerInfo': {'MainIdentifier': '2113439293', 'Name': 'Aung Khant Min',"
+ " 'NRC': '13/MiFoS(N)163525', 'Gender': '', 'DOB': '1990-01-20', 'FatherName': '', 'Address': '',"
+ " 'LastUpdatedDtm': 'Jul 8 2020 9:27AM', 'PrintedDtm': 'Aug 2 2020 2:54AM' }, "
+ " 'ActiveLoanStatus': 'Record found', 'ActiveLoans': [{'ReportingDate': '2020-03-01',"
+ " 'LoanGUID': 'BC309AD3-0444-4EFD-807E-79ECB99EE999',"
+ " 'Institution': 'Demo 1', 'Division': 'YANGON ',"
+ " 'Township': 'YANKIN ', 'DisbursedDate': 'xxxx',"
+ " 'DisbursedAmount': '500000', 'PrincipalOutstandingAmount': '300000',"
+ " 'TotalOutstandingAmount': 'xxxx', 'PrincipalOverdueAmount': '100000',"
+ " 'TotalOverdueAmount': '', 'DaysInDelay': '10',"
+ " 'UpdatedDtm': 'Apr 27 2020 9:41AM', 'SortColumn': '2020-03-01T00:00:00'}], "
+ " 'WriteOffLoanStatus': 'No record found', 'WriteOffLoans': null,"
+ " 'CreditScore': { 'Score': 'N/A','Class': 'N/A', 'Note': ''},"
+ " 'InquiryStatus': 'Record found', 'Inquiries': [{ 'InquiryDate': '', 'Institution': '','NoOfInquiry': ''} ] },"
+ " 'MessageDtm': '8/1/2020 8:24:20 PM UTC','SubscriptionID': '4A7C-317A41BA-1FF8-8A64-F8EDBE0F625D',"
+ " 'CallerIP': '207.46.228.155',"
+ " 'URI': 'https://qa-mmcix-api.azurewebsites.net/20200324/api/Dashboard/GetCreditReport?uniqueId=2113439292',"
+ " 'ResponseMessage': 'Record found'}";
@Test
public void getUniqueIdFromSearchMethodTest() {
String searchResult = "{\"Data\":[{\"UniqueID\":\"8113399260\",\"NRC\":\"12/KaMaRa(N)253426\",\"FullName\":\"Aye Aye\",\"DOB\":\"1990-05-22,1991-05-22\",\"FatherFullName\":\"U Aye Myint Maung\",\"Location\":\"Yangon-Thongwa,Twantay,Yankin\",\"Flag\":\"[{\\\"WriteOff\\\":1}]\",\"Active\":\"Y\"}],\"MessageDtm\":\"8/1/2020 6:39:00 PM UTC\",\"SubscriptionID\":\"317A1FF8-625D-41BA-BE0F-F8ED8A644A7C\",\"CallerIP\":\"207.46.228.155\",\"URI\":\"https://qa-mmcix-api.azurewebsites.net/20200324/api/Search/SimpleSearch?nrc=253426\",\"ResponseMessage\":\"Record found\"}";
when(this.thitsaWorksCreditBureauIntegrationWritePlatformServiceImpl.okHttpConnectionMethod(userName, password, subscriptionKey,
subscriptionId, url, token, report, fileData, uniqueId, nrcID, process)).thenReturn(searchResult);
final String search = thitsaWorksCreditBureauIntegrationWritePlatformServiceImpl.okHttpConnectionMethod(userName, password,
subscriptionKey, subscriptionId, url, token, report, fileData, uniqueId, nrcID, process);
when(thitsaWorksCreditBureauIntegrationWritePlatformServiceImpl.extractUniqueId(search)).thenCallRealMethod();
Long uniqueId = thitsaWorksCreditBureauIntegrationWritePlatformServiceImpl.extractUniqueId(searchResult);
Long expectedUniqueId = 8113399260L;
assertEquals(expectedUniqueId, uniqueId);
}
@Test
public void getCreditReportTest() {
String curentNrc = "13/MiFoS(N)163525";
when(this.thitsaWorksCreditBureauIntegrationWritePlatformServiceImpl.okHttpConnectionMethod(userName, password, subscriptionKey,
subscriptionId, url, token, report, fileData, uniqueId, nrcID, process)).thenReturn(testresult);
String creditReport = thitsaWorksCreditBureauIntegrationWritePlatformServiceImpl.okHttpConnectionMethod(userName, password,
subscriptionKey, subscriptionId, url, token, report, fileData, uniqueId, nrcID, process);
JsonObject resultObject = JsonParser.parseString(creditReport).getAsJsonObject();
String data = resultObject.get("Data").toString();
JsonObject dataObject = JsonParser.parseString(data).getAsJsonObject();
String borrowerInfo = dataObject.get("BorrowerInfo").toString();
JsonObject borrowerObject = JsonParser.parseString(borrowerInfo).getAsJsonObject();
String nrc = borrowerObject.get("NRC").toString();
nrc = nrc.substring(1, nrc.length() - 1);
assertEquals(nrc, curentNrc);
}
}
@@ -0,0 +1,78 @@
/**
* 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.portfolio.loanproduct;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThrows;
import static org.junit.jupiter.api.Assertions.assertEquals;
import io.cucumber.java8.En;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductTrancheDetails;
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException;
public class LoanProductValidationStepDefinitions implements En {
private LoanProduct loanProduct;
private LoanProductGeneralRuleException ruleException;
public LoanProductValidationStepDefinitions() {
Given("/^A loan product with values `allowMultipleDisbursal` is (.*), `disallowExpectedDisbursements` is (.*), `allowApprovedDisbursedAmountsOverApplied` is (.*), `overAppliedCalculationType` is (.*) and `overAppliedNumber` is (.*)$/",
(String allowMultipleDisbursal, String disallowExpectedDisbursements, String allowApprovedDisbursedAmountsOverApplied,
String overAppliedCalculationType, String overAppliedNumber) -> {
this.loanProduct = newLoanProduct(Boolean.parseBoolean(allowMultipleDisbursal));
if (!StringUtils.isBlank(disallowExpectedDisbursements)) {
loanProduct.setDisallowExpectedDisbursements(Boolean.parseBoolean(disallowExpectedDisbursements));
}
if (!StringUtils.isBlank(allowApprovedDisbursedAmountsOverApplied)) {
loanProduct.setAllowApprovedDisbursedAmountsOverApplied(
Boolean.parseBoolean(allowApprovedDisbursedAmountsOverApplied));
}
if (!StringUtils.isBlank(overAppliedCalculationType) && !"-".equals(overAppliedCalculationType)) {
loanProduct.setOverAppliedCalculationType(overAppliedCalculationType);
}
if (!StringUtils.isBlank(overAppliedNumber) && Integer.parseInt(overAppliedNumber) >= 0) {
loanProduct.setOverAppliedNumber(Integer.parseInt(overAppliedNumber));
}
});
When("/^The user validates the loan product before saving$/", () -> {
this.ruleException = assertThrows(LoanProductGeneralRuleException.class, () -> loanProduct.validateLoanProductPreSave());
});
Then("/^An exception (.*) with message template (.*) should be thrown$/", (String exception, String messageTemplate) -> {
if (!StringUtils.isBlank(exception)) {
assertEquals(Class.forName(exception.trim()), this.ruleException.getClass());
}
if (!StringUtils.isBlank(messageTemplate)) {
assertThat(messageTemplate.trim(), is(this.ruleException.getGlobalisationMessageCode()));
}
});
}
private LoanProduct newLoanProduct(boolean allowMultipleDisbursals) {
LoanProduct lp = new LoanProduct();
lp.setLoanProducTrancheDetails(new LoanProductTrancheDetails(allowMultipleDisbursals, null, null));
return lp;
}
}
@@ -1,158 +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.portfolio.loanproduct.domain;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThrows;
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class LoanProductTest {
@Test
public void shouldThrowExceptionWhenAllowMultipleDisbursalsFalseAndDisallowExpectedDisbursementsIsTrue() {
LoanProduct lp = stubLoanProduct(false);
lp.setDisallowExpectedDisbursements(true);
LoanProductGeneralRuleException expectedEx = assertThrows(LoanProductGeneralRuleException.class,
() -> lp.validateLoanProductPreSave());
assertThat(expectedEx.getGlobalisationMessageCode(),
is("error.msg.allowMultipleDisbursals.not.set.disallowExpectedDisbursements.cant.be.set"));
}
@Test
public void shouldThrowExceptionWhenDisallowExpectedDisbursementIsFalseAndAllowApprovedDisbursedAmountsOverAppliedIsTrue() {
LoanProduct lp = stubLoanProduct(true);
lp.setDisallowExpectedDisbursements(false);
lp.setAllowApprovedDisbursedAmountsOverApplied(true);
LoanProductGeneralRuleException expectedEx = assertThrows(LoanProductGeneralRuleException.class,
() -> lp.validateLoanProductPreSave());
assertThat(expectedEx.getGlobalisationMessageCode(),
is("error.msg.disallowExpectedDisbursements.not.set.allowApprovedDisbursedAmountsOverApplied.cant.be.set"));
}
@Test
public void shouldThrowExceptionWhenAllowApprovedDisbursedAmountsOverAppliedIsTrueAndAppliedCalculationTypeIsNull() {
LoanProduct lp = stubLoanProduct(true);
lp.setDisallowExpectedDisbursements(true);
lp.setAllowApprovedDisbursedAmountsOverApplied(true);
lp.setOverAppliedCalculationType(null);
LoanProductGeneralRuleException expectedEx = assertThrows(LoanProductGeneralRuleException.class,
() -> lp.validateLoanProductPreSave());
assertThat(expectedEx.getGlobalisationMessageCode(),
is("error.msg.allowApprovedDisbursedAmountsOverApplied.is.set.overAppliedCalculationType.is.mandatory"));
}
@Test
public void shouldThrowExceptionWhenAllowApprovedDisbursedAmountsOverAppliedIsTrueAndAppliedCalculationTypeIsEmpty() {
LoanProduct lp = stubLoanProduct(true);
lp.setDisallowExpectedDisbursements(true);
lp.setAllowApprovedDisbursedAmountsOverApplied(true);
lp.setOverAppliedCalculationType("");
LoanProductGeneralRuleException expectedEx = assertThrows(LoanProductGeneralRuleException.class,
() -> lp.validateLoanProductPreSave());
assertThat(expectedEx.getGlobalisationMessageCode(),
is("error.msg.allowApprovedDisbursedAmountsOverApplied.is.set.overAppliedCalculationType.is.mandatory"));
}
@Test
public void shouldThrowExceptionWhenAllowApprovedDisbursedAmountsOverAppliedIsFalseAndAppliedCalculationTypeIsNotEmpty() {
LoanProduct lp = stubLoanProduct(true);
lp.setDisallowExpectedDisbursements(true);
lp.setAllowApprovedDisbursedAmountsOverApplied(false);
lp.setOverAppliedCalculationType("flat");
LoanProductGeneralRuleException expectedEx = assertThrows(LoanProductGeneralRuleException.class,
() -> lp.validateLoanProductPreSave());
assertThat(expectedEx.getGlobalisationMessageCode(),
is("error.msg.allowApprovedDisbursedAmountsOverApplied.is.not.set.overAppliedCalculationType.cant.be.entered"));
}
@Test
public void shouldThrowExceptionWhenOverAppliedCalculationTypeNotValid() {
LoanProduct lp = stubLoanProduct(true);
lp.setDisallowExpectedDisbursements(true);
lp.setAllowApprovedDisbursedAmountsOverApplied(true);
lp.setOverAppliedCalculationType("notflat");
LoanProductGeneralRuleException expectedEx = assertThrows(LoanProductGeneralRuleException.class,
() -> lp.validateLoanProductPreSave());
assertThat(expectedEx.getGlobalisationMessageCode(), is("error.msg.overAppliedCalculationType.must.be.percentage.or.flat"));
}
@Test
public void shouldThrowExceptionWhenAllowApprovedDisbursedAmountsOverAppliedIsTrueAndAppliedCalculationNumberIsNull() {
LoanProduct lp = stubLoanProduct(true);
lp.setDisallowExpectedDisbursements(true);
lp.setAllowApprovedDisbursedAmountsOverApplied(true);
lp.setOverAppliedCalculationType("flat");
lp.setOverAppliedNumber(null);
LoanProductGeneralRuleException expectedEx = assertThrows(LoanProductGeneralRuleException.class,
() -> lp.validateLoanProductPreSave());
assertThat(expectedEx.getGlobalisationMessageCode(),
is("error.msg.allowApprovedDisbursedAmountsOverApplied.is.set.overAppliedNumber.is.mandatory"));
}
@Test
public void shouldThrowExceptionWhenAllowApprovedDisbursedAmountsOverAppliedIsFalseAndAppliedCalculationNumberIsNotNull() {
LoanProduct lp = stubLoanProduct(true);
lp.setDisallowExpectedDisbursements(true);
lp.setAllowApprovedDisbursedAmountsOverApplied(false);
lp.setOverAppliedNumber(80);
LoanProductGeneralRuleException expectedEx = assertThrows(LoanProductGeneralRuleException.class,
() -> lp.validateLoanProductPreSave());
assertThat(expectedEx.getGlobalisationMessageCode(),
is("error.msg.allowApprovedDisbursedAmountsOverApplied.is.not.set.overAppliedNumber.cant.be.entered"));
}
private LoanProduct stubLoanProduct(boolean allowMultipleDisbursals) {
LoanProduct lp = new LoanProduct();
lp.setLoanProducTrancheDetails(new LoanProductTrancheDetails(allowMultipleDisbursals, null, null));
return lp;
}
}
@@ -1,129 +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.template;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.google.common.io.Resources;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.math.RoundingMode;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.loanaccount.LoanScheduleTestDataHelper;
import org.apache.fineract.portfolio.loanaccount.MonetaryCurrencyBuilder;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.template.domain.Template;
import org.apache.fineract.template.domain.TemplateMapper;
import org.apache.fineract.template.service.TemplateMergeService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class TemplateMergeServiceTest {
private TemplateMergeService tms = new TemplateMergeService();
@BeforeEach
public void setUpForEachTestCase() throws Exception {
Field field = MoneyHelper.class.getDeclaredField("roundingMode");
field.setAccessible(true);
field.set(null, RoundingMode.HALF_EVEN);
}
@Test
public void compileHelloTemplate() throws Exception {
String templateText = "Hello Test for Template {{file.name}}!";
File file = new File("hello");
Map<String, Object> scopes = new HashMap<>();
scopes.put("file", file);
String output = compileTemplateText(templateText, scopes);
assertEquals("Hello Test for Template hello!", output);
}
@Test
public void compileLoanSummary() throws IOException {
LocalDate july2nd = LocalDate.of(2012, 7, 2);
MonetaryCurrency usDollars = new MonetaryCurrencyBuilder().withCode("USD").withDigitsAfterDecimal(2).build();
List<LoanRepaymentScheduleInstallment> installments = LoanScheduleTestDataHelper.createSimpleLoanSchedule(july2nd, usDollars);
Map<String, Object> scopes = new HashMap<>();
scopes.put("installments", installments);
String templateText = Resources.toString(Resources.getResource("template.mustache"), StandardCharsets.UTF_8);
String expectedOutput = Resources.toString(Resources.getResource("template-expected.html"), StandardCharsets.UTF_8);
String output = compileTemplateText(templateText, scopes);
assertEquals(expectedOutput, output);
}
@Test
public void arrayUsingLoop() throws Exception {
String templateText = "Hello Test for Template{{#data.name}} {{.}}{{/data.name}}!";
String jsonData = "{\"name\": [ \"Michael\", \"Terence\" ] }";
String expectedOutput = "Hello Test for Template Michael Terence!";
Map<String, Object> scopes = new HashMap<>();
scopes.put("data", createMapFromJSON(jsonData));
String output = compileTemplateText(templateText, scopes);
assertEquals(expectedOutput, output);
}
@Test
public void arrayUsingIndex() throws Exception {
String templateText = "Hello Test for Template {{data.name#1}} & {{data.name#0}}!";
String jsonData = "{\"name\": [ \"Michael\", \"Terence\" ] }";
String expectedOutput = "Hello Test for Template Terence & Michael!";
Map<String, Object> scopes = new HashMap<>();
scopes.put("data", createMapFromJSON(jsonData));
String output = compileTemplateText(templateText, scopes);
assertEquals(expectedOutput, output);
}
protected String compileTemplateText(String templateText, Map<String, Object> scope) throws MalformedURLException, IOException {
List<TemplateMapper> mappers = new ArrayList<>();
Template template = new Template("TemplateName", templateText, null, null, mappers);
return tms.compile(template, scope);
}
protected Map<String, Object> createMapFromJSON(String jsonText) {
Gson gson = new Gson();
Type ssMap = new TypeToken<Map<String, Object>>() {}.getType();
JsonElement json = JsonParser.parseString(jsonText);
return gson.fromJson(json, ssMap);
}
}
@@ -0,0 +1,82 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.template.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import io.cucumber.java8.En;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.fineract.template.domain.Template;
import org.apache.fineract.template.domain.TemplateMapper;
public class TemplateServiceStepDefinitions implements En {
private TemplateMergeService tms = new TemplateMergeService();
private String template;
private Map<String, Object> data;
private String result;
public TemplateServiceStepDefinitions() {
Given("/^A mustache template file (.*)$/", (String file) -> {
template = IOUtils.resourceToString("templates/" + file, StandardCharsets.UTF_8,
TemplateServiceStepDefinitions.class.getClassLoader());
});
Given("/^A JSON data file (.*)$/", (String file) -> {
data = parse(IOUtils.resourceToString("templates/" + file, StandardCharsets.UTF_8,
TemplateServiceStepDefinitions.class.getClassLoader()));
});
When("The user merges the template with data", () -> {
result = compile(template, data);
});
Then("/^The result should match the content of file (.*)$/", (String file) -> {
String expected = IOUtils.resourceToString("results/" + file, StandardCharsets.UTF_8,
TemplateServiceStepDefinitions.class.getClassLoader());
assertEquals(expected, result);
});
}
private String compile(String templateText, Map<String, Object> scope) throws IOException {
List<TemplateMapper> mappers = new ArrayList<>();
Template template = new Template("TemplateName", templateText, null, null, mappers);
return tms.compile(template, scope);
}
private Map<String, Object> parse(String data) {
Gson gson = new Gson();
Type ssMap = new TypeToken<Map<String, Object>>() {}.getType();
JsonElement json = JsonParser.parseString(data);
return gson.fromJson(json, ssMap);
}
}
@@ -0,0 +1,20 @@
#
# 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.
#
cucumber.publish.quiet=true
@@ -0,0 +1,6 @@
Feature: Accounting Service
@accounting
Scenario: Verify financial activities constants
Given All financial activities
Then The they should not be empty
@@ -0,0 +1,7 @@
Feature: Batch Service
@batch
Scenario: Verify that batch builder methods are called
Given A batch request
When The user calls the batch service handle request method without enclosing transaction
Then The batch service handle request method without enclosing transaction should have been called
@@ -0,0 +1,20 @@
Feature: Commands Service
@template
Scenario Outline: Verify that command providers are injected
Given A command handler for entity <entity> and action <action>
When The user processes the command with ID <id>
Then The command ID matches <id>
Examples:
| id | entity | action |
| 815 | HUMAN | UPDATE |
@template
Scenario Outline: Verify that command no command handler is provided
Given A missing command handler for entity <entity> and action <action>
Then The system should throw an exception
Examples:
| entity | action |
| WHATEVER | DOSOMETHING |
@@ -0,0 +1,7 @@
Feature: Classpath duplicates
@infrastructure
Scenario: Verify if any duplicates exist in the classpath
Given A class graph
When The user scans the class graph
Then There should be no duplicates
@@ -0,0 +1,23 @@
Feature: Core Infrastructure
@infrastructure
Scenario: Verify that tenant migration service is properly executed
Given Liquibase is disabled with a default tenant
Given Liquibase runs the very first time for the tenant store
Given A previously Flyway migrated tenant store database
Given A previously Flyway migrated tenant store database on an earlier version than 1.6
Given Liquibase runs the very first time for the default tenant
Given A previously Flyway migrated default tenant database
Given A previously Flyway migrated default tenant database on the 1.6 version
When The database migration process is executed
Then The tenant store upgrade fails with a schema upgrade needed
@infrastructure
Scenario Outline: Verify empty multi exceptions
Given A multi exception with exceptions <exception1> and <exception2>
Then A <expected> should be thrown
Examples:
| exception1 | exception2 | expected |
| | | java.lang.IllegalArgumentException |
| java.lang.IllegalArgumentException | java.lang.IllegalStateException | org.apache.fineract.infrastructure.core.exception.MultiException |
@@ -0,0 +1,35 @@
Feature: SQL Builder
@sqlbuilder
Scenario Outline: Verify that SQL builder has expected template and arguments
Given A criteria <criteria1>, <criteria2>, <criteria3>, <criteria4> and arguments <argument1>, <argument2>, <argument3>, <argument4>
Then The template should match expected template <template>
Then The arguments should match expected arguments <arguments>
Then The builder should match <expected>
Examples:
| criteria1 | argument1 | criteria2 | argument2 | criteria3 | argument3 | criteria4 | argument4 | template | expected |
| | | | | | | | | | SQLBuilder{} |
| name = | Michael | hobby LIKE | Mifos/Apache Fineract | age < | 123 | | | WHERE name = ? AND hobby LIKE ? AND age < ? | SQLBuilder{WHERE name = ['Michael'] AND hobby LIKE ['Mifos/Apache Fineract'] AND age < [123]} |
| ref = | NULL | | | | | | | WHERE ref = ? | SQLBuilder{WHERE ref = [null]} |
| hobby LIKE | Mifos/Apache Fineract | hobby like | Mifos/Apache Fineract | | | | | WHERE hobby LIKE ? AND hobby like ? | |
@sqlbuilder
Scenario Outline: Verify that SQL builder detects illegal criteria
Given An illegal criteria <criteria> with argument <argument>
Then The builder should throw an exception <exception> with message <message>
Examples:
| criteria | argument | message | exception |
| NULL | argument | <NULL> criteria fragment | java.lang.IllegalArgumentException |
| EMPTY | argument | <EMPTY> criteria fragment | java.lang.IllegalArgumentException |
| age< | 123 | Space between column and operator | java.lang.IllegalArgumentException |
| age = ? | 123 | Criteria with placeholder ? | java.lang.IllegalArgumentException |
| age | 123 | Criteria missing operator | java.lang.IllegalArgumentException |
| and age = ? | 123 | Criteria starts with AND | java.lang.IllegalArgumentException |
| age = ? and | 123 | Criteria ends with AND | java.lang.IllegalArgumentException |
| or age = ? | 123 | Criteria start with OR | java.lang.IllegalArgumentException |
| age = ? or | 123 | Criteria ends with OR | java.lang.IllegalArgumentException |
| (age = | 123 | Criteria contains opening parenthesis | java.lang.IllegalArgumentException |
| age = ? | 123 | Criteria contains closing parenthesis | java.lang.IllegalArgumentException |
| age< = ? | 123 | Offset corner case | java.lang.IllegalArgumentException |
@@ -0,0 +1,21 @@
Feature: MIX XBRL
@mix
Scenario Outline: Verify that GL codes resolve
Given A XBRL template <template>
When The user resolves GL codes
Then The result should contain <values>
Examples:
| template | values |
| {12000}+{11000} | 12000,11000 |
@mix
Scenario Outline: Verify XBRL builder is working
Given The XBRL input parameters start date <start>, end date <end>, currency <currency>, taxonomy <taxonomy> and sample <sample>
When The user builds the XBRL report
Then The XBRL result should match <result>
Examples:
| start | end | currency | taxonomy | sample | result |
| 2005-11-11 | 2013-07-17 | USD | Assets | 10000 | xbrl.xml |
@@ -0,0 +1,19 @@
Feature: Loan Product Validation
@loanproduct
Scenario Outline: Verify that loan product settings are throwing expected exceptions
Given A loan product with values `allowMultipleDisbursal` is <allowMultipleDisbursal>, `disallowExpectedDisbursements` is <disallowExpectedDisbursements>, `allowApprovedDisbursedAmountsOverApplied` is <allowApprovedDisbursedAmountsOverApplied>, `overAppliedCalculationType` is <overAppliedCalculationType> and `overAppliedNumber` is <overAppliedNumber>
When The user validates the loan product before saving
Then An exception <exception> with message template <template> should be thrown
Examples:
| allowMultipleDisbursal | disallowExpectedDisbursements | allowApprovedDisbursedAmountsOverApplied | overAppliedCalculationType | overAppliedNumber | exception | template |
| false | true | false | - | -1 | org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException | error.msg.allowMultipleDisbursals.not.set.disallowExpectedDisbursements.cant.be.set |
| true | false | true | - | -1 | org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException | error.msg.disallowExpectedDisbursements.not.set.allowApprovedDisbursedAmountsOverApplied.cant.be.set |
| true | true | true | - | -1 | org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException | error.msg.allowApprovedDisbursedAmountsOverApplied.is.set.overAppliedCalculationType.is.mandatory |
| true | true | true | - | -1 | org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException | error.msg.allowApprovedDisbursedAmountsOverApplied.is.set.overAppliedCalculationType.is.mandatory |
| true | false | true | - | -1 | org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException | error.msg.disallowExpectedDisbursements.not.set.allowApprovedDisbursedAmountsOverApplied.cant.be.set |
| true | true | true | notflat | -1 | org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException | error.msg.overAppliedCalculationType.must.be.percentage.or.flat |
| true | true | false | flat | -1 | org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException | error.msg.allowApprovedDisbursedAmountsOverApplied.is.not.set.overAppliedCalculationType.cant.be.entered |
| true | true | true | flat | -1 | org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException | error.msg.allowApprovedDisbursedAmountsOverApplied.is.set.overAppliedNumber.is.mandatory |
| true | true | false | - | 80 | org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException | error.msg.allowApprovedDisbursedAmountsOverApplied.is.not.set.overAppliedNumber.cant.be.entered |
@@ -0,0 +1,15 @@
Feature: Template Service
@template
Scenario Outline: Verify that mustache templates have expected results
Given A mustache template file <template>
Given A JSON data file <json>
When The user merges the template with data
Then The result should match the content of file <result>
Examples:
| template | json | result |
| hello.mustache | hello.json | hello.txt |
| loan.mustache | loan.json | loan.html |
| array.loop.mustache | array.json | array.loop.txt |
| array.index.mustache | array.json | array.index.txt |
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<configuration scan="false">
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>false</resetJUL>
</contextListener>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<withJansi>true</withJansi>
<encoder>
<pattern>%green(%d{yyyy-MM-dd HH:mm:ss.SSS}) [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.transaction" level="INFO" />
<logger name="org.springframework.data.convert" level="ERROR" />
<logger name="org.springframework.http.converter.json" level="ERROR" />
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -0,0 +1 @@
Hello Test for Template Terence & Michael!
@@ -0,0 +1 @@
Hello Test for Template Michael Terence!
@@ -0,0 +1 @@
Hello Test for Template hello!
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<xbrl><schemaRef xmlns:link="http://www.themix.org/sites/default/files/Taxonomy2010/dct/dc-all_2010-08-31.xsd"></schemaRef><Assets contextRef="As_Of_07_17_20131" unitRef="Unit2" decimals="0">10000</Assets><context id="As_Of_07_17_20131"><entity><identifier scheme="http://www.themix.org">000000</identifier></entity><period><instant>2013-07-17</instant></period></context><unit id="Unit2"><measure>iso4217:USD</measure></unit><unit id="Unit1"><measure>xbrli:pure</measure></unit></xbrl>
@@ -0,0 +1 @@
Hello Test for Template {{data.name#1}} & {{data.name#0}}!
@@ -0,0 +1,8 @@
{
"data": {
"name": [
"Michael",
"Terence"
]
}
}
@@ -0,0 +1 @@
Hello Test for Template{{#data.name}} {{.}}{{/data.name}}!
@@ -0,0 +1,5 @@
{
"file": {
"name": "hello"
}
}
@@ -0,0 +1 @@
Hello Test for Template {{file.name}}!
@@ -0,0 +1,22 @@
{
"installments": [
{
"dueDate": "2012-07-02",
"installmentNumber": "1",
"principal": "",
"feeChargesCharged": ""
},
{
"dueDate": "2012-08-02",
"installmentNumber": "2",
"principal": "",
"feeChargesCharged": ""
},
{
"dueDate": "2012-09-02",
"installmentNumber": "3",
"principal": "",
"feeChargesCharged": ""
}
]
}
File diff suppressed because one or more lines are too long
+7 -1
View File
@@ -29,7 +29,13 @@ dependencies {
'com.google.truth.extensions:truth-java8-extension',
'org.awaitility:awaitility',
'org.bouncycastle:bcpkix-jdk15on',
'org.bouncycastle:bcprov-jdk15on'
'org.bouncycastle:bcprov-jdk15on',
'io.cucumber:cucumber-core',
'io.cucumber:cucumber-java',
'io.cucumber:cucumber-java8',
'io.cucumber:cucumber-junit-platform-engine',
'io.cucumber:cucumber-spring',
'com.intuit.karate:karate-junit5',
// 'com.google.code.gson:gson',
)
testImplementation ('org.mock-server:mockserver-junit-jupiter') {
@@ -0,0 +1,20 @@
#
# 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.
#
cucumber.publish.quiet=true
+7 -1
View File
@@ -23,7 +23,13 @@ dependencies {
tomcat 'org.apache.tomcat:tomcat:9.0.60@zip'
testImplementation( files("$rootDir/fineract-provider/build/classes/java/main/"),
project(path: ':fineract-provider', configuration: 'runtimeElements'),
'org.junit.jupiter:junit-jupiter-api'
'org.junit.jupiter:junit-jupiter-api',
'io.cucumber:cucumber-core',
'io.cucumber:cucumber-java',
'io.cucumber:cucumber-java8',
'io.cucumber:cucumber-junit-platform-engine',
'io.cucumber:cucumber-spring',
'com.intuit.karate:karate-junit5',
)
testImplementation ('io.rest-assured:rest-assured') {
exclude group: 'commons-logging'
+7 -1
View File
@@ -24,7 +24,13 @@ dependencies {
testImplementation( files("$rootDir/fineract-provider/build/classes/java/main/"),
project(path: ':fineract-provider', configuration: 'runtimeElements'),
'org.junit.jupiter:junit-jupiter-api',
'com.icegreen:greenmail-junit5'
'io.cucumber:cucumber-core',
'io.cucumber:cucumber-java',
'io.cucumber:cucumber-java8',
'io.cucumber:cucumber-junit-platform-engine',
'io.cucumber:cucumber-spring',
'com.icegreen:greenmail-junit5',
'com.intuit.karate:karate-junit5',
)
testImplementation ('io.rest-assured:rest-assured') {
exclude group: 'commons-logging'