blob: 200d0fda0b7c6fb72675f8e9d8b8f2fdf9782830 [file] [log] [blame]
/*
* 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.cn.accounting;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.fineract.cn.accounting.api.v1.EventConstants;
import org.apache.fineract.cn.accounting.api.v1.client.LedgerAlreadyExistsException;
import org.apache.fineract.cn.accounting.api.v1.client.LedgerNotFoundException;
import org.apache.fineract.cn.accounting.api.v1.client.LedgerReferenceExistsException;
import org.apache.fineract.cn.accounting.api.v1.domain.Account;
import org.apache.fineract.cn.accounting.api.v1.domain.AccountType;
import org.apache.fineract.cn.accounting.api.v1.domain.Ledger;
import org.apache.fineract.cn.accounting.api.v1.domain.LedgerPage;
import org.apache.fineract.cn.accounting.util.AccountGenerator;
import org.apache.fineract.cn.accounting.util.LedgerGenerator;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class TestLedger extends AbstractAccountingTest {
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("src/doc/generated-snippets/test-ledger");
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
final String path = "/accounting/v1";
@Before
public void setUp(){
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
.build();
}
@Test
public void shouldCreateLedger() throws Exception {
final Ledger ledger = LedgerGenerator.createRandomLedger();
this.testSubject.createLedger(ledger);
Assert.assertTrue(this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger.getIdentifier()));
this.mockMvc.perform(post(path + "/ledgers")
.contentType(MediaType.APPLICATION_JSON_VALUE).accept(MediaType.APPLICATION_JSON_VALUE)
.content(ledger.getIdentifier()))
.andExpect(status().isNotFound());
}
@Test
public void shouldNotCreateLedgerAlreadyExists() throws Exception {
final Ledger ledger = LedgerGenerator.createRandomLedger();
this.testSubject.createLedger(ledger);
this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger.getIdentifier());
try {
this.testSubject.createLedger(ledger);
Assert.fail();
} catch (final LedgerAlreadyExistsException ex) {
// do nothing, expected
}
}
@Test
public void shouldFetchLedgers() throws Exception {
final LedgerPage currentLedgerPage = this.testSubject.fetchLedgers(false, null, null, null, null, null, null);
final Ledger ledger = LedgerGenerator.createRandomLedger();
this.testSubject.createLedger(ledger);
this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger.getIdentifier());
final LedgerPage ledgerPage = this.testSubject.fetchLedgers(false, null, null, null, null, null, null);
Assert.assertEquals(currentLedgerPage.getTotalElements() + 1L, ledgerPage.getTotalElements().longValue());
this.mockMvc.perform(get(path + "/ledgers/")
.accept(MediaType.ALL_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().is4xxClientError());
}
@Test
public void shouldFetchSubLedgers() throws Exception {
final Ledger parent = LedgerGenerator.createRandomLedger();
final Ledger child = LedgerGenerator.createRandomLedger();
parent.setSubLedgers(Collections.singletonList(child));
final LedgerPage currentLedgerPage = this.testSubject.fetchLedgers(true, child.getIdentifier(), null, null, null, null, null);
this.testSubject.createLedger(parent);
this.eventRecorder.wait(EventConstants.POST_LEDGER, parent.getIdentifier());
final LedgerPage ledgerPage = this.testSubject.fetchLedgers(true, child.getIdentifier(), null, null, null, null, null);
Assert.assertEquals(currentLedgerPage.getTotalElements() + 1L, ledgerPage.getTotalElements().longValue());
final Ledger fetchedSubLedger = ledgerPage.getLedgers().get(0);
Assert.assertEquals(parent.getIdentifier(), fetchedSubLedger.getParentLedgerIdentifier());
this.mockMvc.perform(get(path + "/ledgers/" + parent.getIdentifier() + "/")
.accept(MediaType.ALL_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE).content(ledgerPage.getLedgers().toString()))
.andExpect(status().is4xxClientError());
}
@Test
public void shouldFindLedger() throws Exception {
final Ledger ledger = LedgerGenerator.createRandomLedger();
this.testSubject.createLedger(ledger);
this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger.getIdentifier());
final Ledger foundLedger = this.testSubject.findLedger(ledger.getIdentifier());
Assert.assertNotNull(foundLedger);
Assert.assertEquals(ledger.getIdentifier(), foundLedger.getIdentifier());
Assert.assertEquals(ledger.getType(), foundLedger.getType());
Assert.assertEquals(ledger.getName(), foundLedger.getName());
Assert.assertEquals(ledger.getDescription(), foundLedger.getDescription());
Assert.assertNull(ledger.getParentLedgerIdentifier());
Assert.assertTrue(foundLedger.getSubLedgers().size() == 0);
Assert.assertNotNull(foundLedger.getCreatedBy());
Assert.assertNotNull(foundLedger.getCreatedOn());
Assert.assertNull(foundLedger.getLastModifiedBy());
Assert.assertNull(foundLedger.getLastModifiedOn());
Assert.assertEquals(ledger.getShowAccountsInChart(), foundLedger.getShowAccountsInChart());
this.mockMvc.perform(get(path + "/ledgers/" + ledger.getIdentifier())
.accept(MediaType.ALL_VALUE).contentType(MediaType.APPLICATION_JSON_VALUE)
.content(ledger.getIdentifier()))
.andExpect(status().isNotFound());
}
@Test
public void shouldNotFindLedgerUnknown() throws Exception {
try {
this.testSubject.findLedger(RandomStringUtils.randomAlphanumeric(8));
Assert.fail();
} catch (final LedgerNotFoundException ex) {
// do nothing, expected
}
}
@Test
public void shouldAddSubLedger() throws Exception {
final Ledger parentLedger = LedgerGenerator.createRandomLedger();
this.testSubject.createLedger(parentLedger);
this.eventRecorder.wait(EventConstants.POST_LEDGER, parentLedger.getIdentifier());
final Ledger subLedger = LedgerGenerator.createRandomLedger();
this.testSubject.addSubLedger(parentLedger.getIdentifier(), subLedger);
this.eventRecorder.wait(EventConstants.POST_LEDGER, subLedger.getIdentifier());
final Ledger foundParentLedger = this.testSubject.findLedger(parentLedger.getIdentifier());
Assert.assertTrue(foundParentLedger.getSubLedgers().size() == 1);
final Ledger foundSubLedger = foundParentLedger.getSubLedgers().get(0);
Assert.assertEquals(subLedger.getIdentifier(), foundSubLedger.getIdentifier());
this.mockMvc.perform(post(path + "/ledgers/" + parentLedger.getIdentifier())
.accept(MediaType.APPLICATION_JSON_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isNotFound());
}
@Test
public void shouldNotAddSubLedgerParentUnknown() throws Exception {
final Ledger subLedger = LedgerGenerator.createRandomLedger();
try {
this.testSubject.addSubLedger(RandomStringUtils.randomAlphanumeric(8), subLedger);
Assert.fail();
} catch (final LedgerNotFoundException ex) {
// do nothing, expected
}
}
@Test
public void shouldNotAddSubLedgerAlreadyExists() throws Exception {
final Ledger ledger = LedgerGenerator.createRandomLedger();
this.testSubject.createLedger(ledger);
this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger.getIdentifier());
try {
final Ledger subLedger = LedgerGenerator.createRandomLedger();
subLedger.setIdentifier(ledger.getIdentifier());
this.testSubject.addSubLedger(ledger.getIdentifier(), subLedger);
Assert.fail();
} catch (final LedgerAlreadyExistsException ex) {
// do nothing, expected
}
}
@Test
public void shouldModifyLedger() throws Exception {
final Ledger ledger = LedgerGenerator.createRandomLedger();
this.testSubject.createLedger(ledger);
this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger.getIdentifier());
ledger.setName(RandomStringUtils.randomAlphabetic(256));
ledger.setDescription(RandomStringUtils.randomAlphabetic(2048));
ledger.setShowAccountsInChart(Boolean.TRUE);
this.testSubject.modifyLedger(ledger.getIdentifier(), ledger);
this.eventRecorder.wait(EventConstants.PUT_LEDGER, ledger.getIdentifier());
final Ledger modifiedLedger = this.testSubject.findLedger(ledger.getIdentifier());
Assert.assertEquals(ledger.getName(), modifiedLedger.getName());
Assert.assertEquals(ledger.getDescription(), modifiedLedger.getDescription());
Assert.assertNotNull(modifiedLedger.getLastModifiedBy());
Assert.assertNotNull(modifiedLedger.getLastModifiedOn());
Assert.assertEquals(ledger.getShowAccountsInChart(), modifiedLedger.getShowAccountsInChart());
this.mockMvc.perform(put(path + "/ledgers/" + ledger.getIdentifier())
.accept(MediaType.APPLICATION_JSON_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isNotFound());
}
@Test
public void shouldNotModifyLedgerIdentifierMismatch() throws Exception {
final Ledger ledger = LedgerGenerator.createRandomLedger();
this.testSubject.createLedger(ledger);
this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger.getIdentifier());
final String randomName = RandomStringUtils.randomAlphanumeric(8);
try {
this.testSubject.modifyLedger(randomName, ledger);
Assert.fail();
} catch (final IllegalArgumentException ex) {
Assert.assertTrue(ex.getMessage().contains(randomName));
// do nothing, expected
}
}
@Test
public void shouldNotModifyLedgerUnknown() {
final Ledger ledger = LedgerGenerator.createRandomLedger();
try {
this.testSubject.modifyLedger(ledger.getIdentifier(), ledger);
Assert.fail();
} catch (final LedgerNotFoundException ex) {
// do nothing , expected
}
}
@Test
public void shouldDeleteLedger() throws Exception {
final Ledger ledger2delete = LedgerGenerator.createRandomLedger();
this.testSubject.createLedger(ledger2delete);
this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger2delete.getIdentifier());
this.testSubject.deleteLedger(ledger2delete.getIdentifier());
this.eventRecorder.wait(EventConstants.DELETE_LEDGER, ledger2delete.getIdentifier());
try {
this.testSubject.findLedger(ledger2delete.getIdentifier());
Assert.fail();
} catch (final LedgerNotFoundException ex) {
// do nothing, expected
}
this.mockMvc.perform(delete(path + "/ledgers/" + ledger2delete.getIdentifier())
.accept(MediaType.ALL_VALUE)
.contentType(MediaType.APPLICATION_JSON)
.content(ledger2delete.getIdentifier()))
.andExpect(status().isNotFound());
}
@Test
public void shouldNotDeleteLedgerUnknown() throws Exception {
try {
this.testSubject.deleteLedger(RandomStringUtils.randomAlphanumeric(8));
Assert.fail();
} catch (final LedgerNotFoundException ex) {
// do nothing, expected
}
}
@Test
public void shouldNotDeleteLedgerHoldsSubLedger() throws Exception {
final Ledger ledger2delete = LedgerGenerator.createRandomLedger();
ledger2delete.setSubLedgers(Collections.singletonList(LedgerGenerator.createRandomLedger()));
this.testSubject.createLedger(ledger2delete);
this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger2delete.getIdentifier());
try {
this.testSubject.deleteLedger(ledger2delete.getIdentifier());
Assert.fail();
} catch (final LedgerReferenceExistsException ex) {
// do nothing, expected
}
}
@Test
public void shouldNotDeleteLedgerHoldsAccount() throws Exception {
final Ledger ledger2delete = LedgerGenerator.createRandomLedger();
this.testSubject.createLedger(ledger2delete);
this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger2delete.getIdentifier());
final Account ledgerAccount = AccountGenerator.createRandomAccount(ledger2delete.getIdentifier());
this.testSubject.createAccount(ledgerAccount);
this.eventRecorder.wait(EventConstants.POST_ACCOUNT, ledgerAccount.getIdentifier());
try {
this.testSubject.deleteLedger(ledger2delete.getIdentifier());
Assert.fail();
} catch (final LedgerReferenceExistsException ex) {
// do nothing, expected
}
}
@Test
public void shouldFindLedgerWithSeparatorInIdentifier() throws Exception {
// RFC 3986 unreserved characters: ALPHA DIGIT "-", ".", "_", "~"
final String[] unreservedCharacters = new String[] {
"-",
".",
"_"
};
this.logger.info("Creating {} ledgers with unreserved characters.", unreservedCharacters.length);
boolean failed = false;
for (String unreservedCharacter : unreservedCharacters) {
final Ledger ledger = LedgerGenerator.createRandomLedger();
final String identifier = RandomStringUtils.randomAlphanumeric(3) + unreservedCharacter + RandomStringUtils.randomAlphanumeric(2);
ledger.setIdentifier(identifier);
this.logger.info("Creating ledger '{}' with unreserved character '{}' in identifier.", identifier, unreservedCharacter);
this.testSubject.createLedger(ledger);
Assert.assertTrue(this.eventRecorder.wait(EventConstants.POST_LEDGER, ledger.getIdentifier()));
try {
this.testSubject.findLedger(ledger.getIdentifier());
this.logger.info("Ledger '{}' with unreserved character '{}' in identifier found.", identifier, unreservedCharacter);
} catch (final Exception ex) {
this.logger.error("Ledger '{}' with unreserved character '{}' in identifier not found.", identifier, unreservedCharacter);
failed = true;
}
}
Assert.assertFalse(failed);
}
@Test
public void shouldStreamAllAccountsBelongingToLedger() throws InterruptedException {
final Ledger assetLedger = LedgerGenerator.createRandomLedger();
assetLedger.setType(AccountType.ASSET.name());
this.testSubject.createLedger(assetLedger);
this.eventRecorder.wait(EventConstants.POST_LEDGER, assetLedger.getIdentifier());
final List<Account> createdAssetAccounts = Stream.generate(() -> AccountGenerator.createRandomAccount(assetLedger.getIdentifier())).limit(1)
.peek(account -> {
account.setType(AccountType.ASSET.name());
this.testSubject.createAccount(account);
})
.collect(Collectors.toList());
for (final Account account : createdAssetAccounts) {
this.eventRecorder.wait(EventConstants.POST_ACCOUNT, account.getIdentifier());
}
final List<Account> foundAccounts = testSubject.streamAccountsOfLedger(assetLedger.getIdentifier(), "ASC")
.peek(account -> account.setState(null))
.collect(Collectors.toList());
Assert.assertEquals(createdAssetAccounts, foundAccounts);
try{
this.mockMvc.perform(get(path + "/ledgers/" + assetLedger.getIdentifier() + "/accounts" )
.accept(MediaType.ALL_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().is4xxClientError());
} catch (Exception exception){ exception.printStackTrace(); }
}
}