package de.asideas.crowdsource.domain.service.financinground;
import de.asideas.crowdsource.domain.model.FinancingRoundEntity;
import de.asideas.crowdsource.domain.model.PledgeEntity;
import de.asideas.crowdsource.domain.model.ProjectEntity;
import de.asideas.crowdsource.domain.model.UserEntity;
import de.asideas.crowdsource.presentation.FinancingRound;
import de.asideas.crowdsource.presentation.Pledge;
import de.asideas.crowdsource.domain.shared.ProjectStatus;
import de.asideas.crowdsource.repository.FinancingRoundRepository;
import de.asideas.crowdsource.repository.PledgeRepository;
import de.asideas.crowdsource.repository.ProjectRepository;
import org.hamcrest.core.Is;
import org.joda.time.DateTime;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class FinancingRoundPostProcessorTest {
final int EXPECTED_REMAINING_BUDGET_AFTER_ROUND = 44444 + 444444 - ((22222 - 11111) + (222222 - 111111));
private ProjectEntity project_0;
private ProjectEntity project_1;
private ProjectEntity project_2;
private ProjectEntity project_3;
private ProjectEntity project_4;
private List<ProjectEntity> mockedProjects;
@InjectMocks
private FinancingRoundPostProcessor financingRoundPostProcessor;
@Mock
private FinancingRoundRepository financingRoundRepository;
@Mock
private ProjectRepository projectRepository;
@Mock
private PledgeRepository pledgeRepository;
@Before
public void init() {
reset(financingRoundRepository, projectRepository, pledgeRepository);
project_0 = projectEntity(new UserEntity("test_email"), "test_projectId_0", "title", 44, "short description", "description", ProjectStatus.DEFERRED, null);
project_1 = projectEntity(new UserEntity("test_email"), "test_projectId_1", "title", 444, "short description", "description", ProjectStatus.FULLY_PLEDGED, null);
project_2 = projectEntity(new UserEntity("test_email"), "test_projectId_2", "title", 4444, "short description", "description", ProjectStatus.DEFERRED, null);
project_3 = projectEntity(new UserEntity("test_email"), "test_projectId_3", "title", 44444, "short description", "description", ProjectStatus.PUBLISHED, null);
project_4 = projectEntity(new UserEntity("test_email"), "test_projectId_4", "title", 444444, "short description", "description", ProjectStatus.PUBLISHED, null);
this.mockedProjects = Arrays.asList(project_0, project_1, project_2, project_3, project_4);
when(financingRoundRepository.save(any(FinancingRoundEntity.class))).then(i -> i.getArguments()[0]);
when(projectRepository.findByFinancingRound(any(FinancingRoundEntity.class))).thenReturn(mockedProjects);
when(pledgeRepository.findByFinancingRound(any(FinancingRoundEntity.class)))
.thenAnswer(inv -> {
final FinancingRoundEntity financingRound = inv.getArgumentAt(0, FinancingRoundEntity.class);
return Arrays.asList(
aPledgeEntity(project_3, financingRound, project_3.getPledgeGoal() / 2),
aPledgeEntity(project_3, financingRound, -project_3.getPledgeGoal() / 4),
aPledgeEntity(project_4, financingRound, project_4.getPledgeGoal() / 2),
aPledgeEntity(project_4, financingRound, -project_4.getPledgeGoal() / 4)
);
});
}
@Test
public void postProcess_succeeds() throws Exception {
final FinancingRoundEntity terminatedRound = prepareInactiveFinancingRound();
project_0.setFinancingRound(terminatedRound);
project_1.setFinancingRound(terminatedRound);
project_2.setFinancingRound(null);
final FinancingRoundEntity res = financingRoundPostProcessor.postProcess(terminatedRound);
verify(projectRepository, atLeastOnce()).findByFinancingRound(any(FinancingRoundEntity.class));
verify(financingRoundRepository).save(res);
verify(projectRepository, times(1)).save(any(ProjectEntity.class));
assertThat(project_0.getStatus(), is(ProjectStatus.PUBLISHED));
assertThat(project_1.getStatus(), is(ProjectStatus.FULLY_PLEDGED));
assertThat(project_2.getStatus(), is(ProjectStatus.DEFERRED));
assertThat(project_3.getStatus(), is(ProjectStatus.PUBLISHED));
assertThat(res.getPostRoundBudget(), is(EXPECTED_REMAINING_BUDGET_AFTER_ROUND));
assertThat(terminatedRound.getTerminationPostProcessingDone(), is(true));
}
@Test
public void postProcess_doesNothingOnActiveRound() throws Exception {
final FinancingRoundEntity activeRound = prepareActiveFinanzingRound();
project_0.setFinancingRound(activeRound);
final FinancingRoundEntity res = financingRoundPostProcessor.postProcess(activeRound);
verify(projectRepository, never()).findByFinancingRound(activeRound);
verify(projectRepository, never()).save(any(ProjectEntity.class));
verify(financingRoundRepository, never()).save(any(FinancingRoundEntity.class));
assertThat(res, is(activeRound));
assertThat(res.getTerminationPostProcessingDone(), is(false));
}
@Test
public void notifyProjectsOfTerminatedFinancingRound() throws Exception {
final FinancingRoundEntity terminatedRound = prepareInactiveFinancingRound();
project_0.setFinancingRound(terminatedRound);
project_1.setFinancingRound(terminatedRound);
project_2.setFinancingRound(null);
project_3.setFinancingRound(terminatedRound);
financingRoundPostProcessor.notifyProjectsOfTerminatedFinancingRound(terminatedRound);
verify(projectRepository).findByFinancingRound(eq(terminatedRound));
verify(projectRepository).save(any(ProjectEntity.class));
assertThat(project_0.getStatus(), is(ProjectStatus.PUBLISHED));
assertThat(project_0.getFinancingRound(), is(nullValue()));
assertThat(project_1.getStatus(), is(ProjectStatus.FULLY_PLEDGED));
assertThat(project_1.getFinancingRound(), is(terminatedRound));
assertThat(project_2.getStatus(), is(ProjectStatus.DEFERRED));
assertThat(project_2.getFinancingRound(), is(nullValue()));
assertThat(project_3.getStatus(), is(ProjectStatus.PUBLISHED));
assertThat(project_3.getFinancingRound(), is(terminatedRound));
}
@Test
public void assignUnpledgedBudgetToFinancingRound() throws Exception {
final FinancingRoundEntity financingRound = prepareInactiveFinancingRound();
financingRoundPostProcessor.assignUnpledgedBudgetToFinancingRound(financingRound);
assertThat(financingRound.getPostRoundBudget(), is(EXPECTED_REMAINING_BUDGET_AFTER_ROUND));
}
@Test
public void assignUnpledgedBudgetToFinancingRound_setToZeroInCasePledgeAmountBiggerThanBudget() throws Exception {
final FinancingRoundEntity financingRound = prepareInactiveFinancingRound();
financingRound.setBudget(project_3.getPledgeGoal());
financingRoundPostProcessor.assignUnpledgedBudgetToFinancingRound(financingRound);
assertThat(financingRound.getPostRoundBudget(), is(0));
}
private ProjectEntity projectEntity(UserEntity userEntity, String id, String title, int pledgeGoal, String shortDescription, String description, ProjectStatus status, DateTime lastModifiedDate) {
ProjectEntity projectEntity = new ProjectEntity();
projectEntity.setId(id);
projectEntity.setTitle(title);
projectEntity.setPledgeGoal(pledgeGoal);
projectEntity.setShortDescription(shortDescription);
projectEntity.setDescription(description);
projectEntity.setCreator(userEntity);
projectEntity.setStatus(status);
projectEntity.setLastModifiedDate(lastModifiedDate);
when(projectRepository.findOne(id)).thenReturn(projectEntity);
return projectEntity;
}
private FinancingRoundEntity prepareActiveFinanzingRound() {
FinancingRoundEntity res = aFinancingRound(new DateTime().plusDays(1));
res.setId(UUID.randomUUID().toString());
when(financingRoundRepository.findActive(any())).thenReturn(res);
Assert.assertThat(res.active(), Is.is(true));
return res;
}
private FinancingRoundEntity prepareInactiveFinancingRound() {
FinancingRoundEntity res = aFinancingRound(new DateTime().minusDays(1));
when(financingRoundRepository.findActive(any())).thenReturn(res);
Assert.assertThat(res.active(), Is.is(false));
return res;
}
private FinancingRoundEntity aFinancingRound(DateTime endDate) {
FinancingRound creationCmd = new FinancingRound();
creationCmd.setEndDate(endDate);
creationCmd.setBudget(project_3.getPledgeGoal() + project_4.getPledgeGoal());
FinancingRoundEntity res = FinancingRoundEntity.newFinancingRound(creationCmd, 7);
res.setStartDate(new DateTime().minusDays(2));
res.setId("test_finRoundId");
return res;
}
private PledgeEntity aPledgeEntity(ProjectEntity project, FinancingRoundEntity financingRoundy, int pledgeAmount) {
return new PledgeEntity(project, null, new Pledge(pledgeAmount), financingRoundy);
}
}