diff --git a/backend/src/main/java/ch/puzzle/okr/multitenancy/TenantConfigProvider.java b/backend/src/main/java/ch/puzzle/okr/multitenancy/TenantConfigProvider.java index 9e7324f02f..453ef82b75 100644 --- a/backend/src/main/java/ch/puzzle/okr/multitenancy/TenantConfigProvider.java +++ b/backend/src/main/java/ch/puzzle/okr/multitenancy/TenantConfigProvider.java @@ -85,6 +85,10 @@ public Optional getJwkSetUri(String tenantId) { return getTenantConfigById(tenantId).map(TenantConfig::jwkSetUri); } + public List getAllTenantIds() { + return new ArrayList<>(this.tenantConfigs.keySet()); + } + public record TenantConfig(String tenantId, String[] okrChampionEmails, String jwkSetUri, String issuerUrl, String clientId, DataSourceConfig dataSourceConfig) { diff --git a/backend/src/main/java/ch/puzzle/okr/service/business/QuarterBusinessService.java b/backend/src/main/java/ch/puzzle/okr/service/business/QuarterBusinessService.java index 0e1eee25f9..2cc45c590a 100644 --- a/backend/src/main/java/ch/puzzle/okr/service/business/QuarterBusinessService.java +++ b/backend/src/main/java/ch/puzzle/okr/service/business/QuarterBusinessService.java @@ -1,17 +1,10 @@ package ch.puzzle.okr.service.business; -import static ch.puzzle.okr.Constants.BACK_LOG_QUARTER_LABEL; - import ch.puzzle.okr.models.Quarter; import ch.puzzle.okr.multitenancy.TenantConfigProvider; import ch.puzzle.okr.multitenancy.TenantContext; import ch.puzzle.okr.service.persistence.QuarterPersistenceService; import ch.puzzle.okr.service.validation.QuarterValidationService; -import java.time.LocalDateTime; -import java.time.YearMonth; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,6 +12,14 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import java.time.LocalDate; +import java.time.YearMonth; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static ch.puzzle.okr.Constants.BACK_LOG_QUARTER_LABEL; + @Service public class QuarterBusinessService { private static final Logger logger = LoggerFactory.getLogger(QuarterBusinessService.class); @@ -82,12 +83,14 @@ private int getStartOfBusinessYear(YearMonth startOfQuarter, int quarter) { return startOfQuarter.minusMonths((quarter - 1) * 3L).getYear(); } - private void generateQuarter(LocalDateTime start, String label) { + protected void generateQuarter(LocalDate start, String label, String schema) { + TenantContext.setCurrentTenant(schema); + YearMonth yearMonth = YearMonth.from(start); Quarter quarter = Quarter.Builder .builder() .withLabel(label) - .withStartDate(start.toLocalDate()) + .withStartDate(start) .withEndDate(yearMonth.plusMonths(2).atEndOfMonth()) .build(); validator.validateOnGeneration(quarter); @@ -101,11 +104,11 @@ private boolean isInLastMonthOfQuarter(int currentQuarter, int nextQuarter) { return Math.abs(nextQuarter - currentQuarter) == 2; } - YearMonth getCurrentYearMonth() { + public YearMonth getCurrentYearMonth() { return YearMonth.now(); } - Map generateQuarters() { + public Map generateQuarters() { Map quarters = new HashMap<>(); int quarterIndexZeroBased = quarterStart - 1; for (int i = 0; i < 12; i++) { @@ -129,19 +132,14 @@ public void scheduledGenerationQuarters() { String initialTenant = TenantContext.getCurrentTenant(); - List tenantSchemas = this.tenantConfigProvider - .getTenantConfigs() - .stream() - .map(TenantConfigProvider.TenantConfig::tenantId) - .toList(); + List tenantSchemas = this.tenantConfigProvider.getAllTenantIds(); // If we are in the last month of a quarter, generate the next quarter - // if (isInLastMonthOfQuarter(currentQuarter, nextQuarter)) { - for (String schema : tenantSchemas) { - logger.info("Generated quarters on last day of month for tenant {}", schema); - String label = createQuarterLabel(nextQuarterYearMonth, nextQuarter); - TenantContext.setCurrentTenant(schema); - generateQuarter(nextQuarterYearMonth.atDay(1).atStartOfDay(), label); - // } + if (isInLastMonthOfQuarter(currentQuarter, nextQuarter)) { + for (String schema : tenantSchemas) { + logger.info("Generated quarters on last day of month for tenant {}", schema); + String label = createQuarterLabel(nextQuarterYearMonth, nextQuarter); + generateQuarter(nextQuarterYearMonth.atDay(1), label, schema); + } } TenantContext.setCurrentTenant(initialTenant); diff --git a/backend/src/test/java/ch/puzzle/okr/service/persistence/QuarterPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/okr/service/persistence/QuarterPersistenceServiceIT.java index e3b2a9e47c..407184a390 100644 --- a/backend/src/test/java/ch/puzzle/okr/service/persistence/QuarterPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/okr/service/persistence/QuarterPersistenceServiceIT.java @@ -6,24 +6,49 @@ import static org.junit.jupiter.api.Assertions.*; import ch.puzzle.okr.models.Quarter; +import ch.puzzle.okr.multitenancy.TenantConfigProvider; import ch.puzzle.okr.multitenancy.TenantContext; +import ch.puzzle.okr.service.business.QuarterBusinessService; import ch.puzzle.okr.test.SpringIntegrationTest; import ch.puzzle.okr.test.TestHelper; import ch.puzzle.okr.util.quarter.check.QuarterRangeChecker; import java.time.LocalDate; +import java.time.Year; +import java.time.YearMonth; import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; +import org.springframework.test.util.ReflectionTestUtils; @SpringIntegrationTest class QuarterPersistenceServiceIT { + private static final Logger log = LoggerFactory.getLogger(QuarterPersistenceServiceIT.class); + @MockitoSpyBean @Autowired private QuarterPersistenceService quarterPersistenceService; + @MockitoSpyBean private TenantConfigProvider tenantConfigProvider; + + @MockitoSpyBean + @Autowired + @InjectMocks + private QuarterBusinessService quarterBusinessService; + @BeforeEach void setUp() { TenantContext.setCurrentTenant(TestHelper.SCHEMA_PITC); @@ -113,4 +138,26 @@ void findByLabelShouldReturnNullWhenLabelIsNull() { void getModelNameShouldReturnQuarter() { assertEquals(QUARTER, quarterPersistenceService.getModelName()); } + + @ParameterizedTest + @CsvSource(value = {"1,1,0", "2,1,0", "3,1,1", "4,1,0","5,1,0","6,2,1", "7,1,0", "8,1,0", "9,3,1", "10,3,0", "11,1,0", "12,4,1"}) + void testCronJob(int month, int quarterIndex, int amountOfInvocations) { + int startQuarter = 7; + ReflectionTestUtils.setField(quarterBusinessService, "quarterStart", startQuarter); + int nextYear = Year.now().atMonth(startQuarter).plusMonths(month+12).getYear(); + int nextYearShort = nextYear % 1000; + String expectedLabel = "GJ " + nextYearShort + "/" + (nextYearShort + 1) + "-Q"+ quarterIndex; + + Mockito.doReturn(YearMonth.of(nextYear, month)).when(quarterBusinessService).getCurrentYearMonth(); + Mockito.doReturn(List.of(TestHelper.SCHEMA_PITC)).when(tenantConfigProvider).getAllTenantIds(); + + quarterBusinessService.scheduledGenerationQuarters(); + + Mockito.verify(quarterBusinessService, Mockito.times(amountOfInvocations)).generateQuarter(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()); + Mockito.verify(quarterPersistenceService, Mockito.times(amountOfInvocations)).save(ArgumentMatchers.any()); + List createdQuarters = quarterPersistenceService.findAll().stream().filter(quarter -> quarter.getLabel().equals(expectedLabel)).toList(); + assertEquals(amountOfInvocations, createdQuarters.size()); + assertEquals(4+ amountOfInvocations, quarterBusinessService.getQuarters().size()); + createdQuarters.forEach(quarter -> quarterPersistenceService.deleteById(quarter.getId())); + } }