package de.asideas.crowdsource.service; import de.asideas.crowdsource.config.SchedulerConfig; import de.asideas.crowdsource.domain.model.FinancingRoundEntity; import de.asideas.crowdsource.presentation.FinancingRound; import de.asideas.crowdsource.domain.service.financinground.FinancingRoundPostProcessor; import de.asideas.crowdsource.repository.FinancingRoundRepository; import de.asideas.crowdsource.repository.PledgeRepository; import de.asideas.crowdsource.repository.ProjectRepository; import de.asideas.crowdsource.repository.UserRepository; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.boot.test.IntegrationTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; @RunWith(SpringJUnit4ClassRunner.class) @IntegrationTest @ContextConfiguration(classes = {FinancingRoundServiceSchedulingIT.Cfg.class, SchedulerConfig.class}) public class FinancingRoundServiceSchedulingIT { private static final Logger log = LoggerFactory.getLogger(FinancingRoundServiceSchedulingIT.class); private static final int COUNT_TASKS = 10; @Autowired private FinancingRoundRepository financingRoundRepository; @Autowired private FinancingRoundPostProcessor financingRoundPostProcessor; @Autowired private FinancingRoundService financingRoundService; private Set<FinancingRoundEntity> roundsToProcess; private volatile Set<String> invocationIds = new HashSet<>(); public FinancingRoundServiceSchedulingIT() { } @Before public void before() { reset(financingRoundPostProcessor, financingRoundRepository ); invocationIds = new HashSet<>(); roundsToProcess = new LinkedHashSet<>(COUNT_TASKS); for (int i = 0; i < COUNT_TASKS; i++) { roundsToProcess.add(newFinancingRound("test_roundId_" + i, i, DateTime.now())); } when(financingRoundRepository.findOne(any(String.class))).thenAnswer(i -> { String desiredId = (String) i.getArguments()[0]; return roundsToProcess.stream().filter(round -> round.getId().equals(desiredId)).findFirst().get(); }); } @Test public void schedulePostProcessing_manyAtOnceShouldAllBeExecuted() throws InterruptedException { int timePerTask = 50; when(financingRoundPostProcessor.postProcess(any(FinancingRoundEntity.class))).thenAnswer(i -> { FinancingRoundEntity arg = (FinancingRoundEntity) i.getArguments()[0]; invocationIds.add(arg.getId()); log.info("START processing of {}", arg.getId()); Thread.sleep(timePerTask); log.info("FINISHED processing of {}", arg.getId()); return arg; }); roundsToProcess.stream().forEach(financingRoundService::schedulePostProcessing); // Wait for tasks to be completed Thread.sleep(COUNT_TASKS * timePerTask + 100); roundsToProcess.stream().forEach( expRound -> assertTrue("Round with ID " + expRound.getId() + " has been executed", invocationIds.contains(expRound.getId()))); } private FinancingRoundEntity newFinancingRound(String id, int budget, DateTime endDate) { final FinancingRound cmd = new FinancingRound(); cmd.setBudget(budget); cmd.setEndDate(endDate); FinancingRoundEntity res = FinancingRoundEntity.newFinancingRound(cmd, 17); res.setId(id); return res; } @Configuration public static class Cfg { @Bean public UserRepository userRepository() { return mock(UserRepository.class); } @Bean public UserService userService() { return mock(UserService.class); } @Bean public FinancingRoundRepository financingRoundRepository() { return mock(FinancingRoundRepository.class); } @Bean public ProjectRepository projectRepository() { return mock(ProjectRepository.class); } @Bean public PledgeRepository opledgeRepository() { return mock(PledgeRepository.class); } @Bean public FinancingRoundPostProcessor financingRoundPostProcessor() { return mock(FinancingRoundPostProcessor.class); } @Bean public FinancingRoundService financingRoundService(TaskScheduler crowdScheduler) { return new FinancingRoundService(userRepository(), financingRoundRepository(), projectRepository(), financingRoundPostProcessor(), crowdScheduler, opledgeRepository()); } @Bean public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertyPlaceholderConfigurer(); } } }