diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/common/endpoint/FeatureServiceEndpoint.java b/backend/src/main/java/ca/bc/gov/restapi/results/common/endpoint/FeatureServiceEndpoint.java index 8ff8e91d..a5ae4079 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/common/endpoint/FeatureServiceEndpoint.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/common/endpoint/FeatureServiceEndpoint.java @@ -1,6 +1,6 @@ package ca.bc.gov.restapi.results.common.endpoint; -import ca.bc.gov.restapi.results.common.service.RestService; +import ca.bc.gov.restapi.results.common.service.OpenMapsService; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -15,7 +15,7 @@ @AllArgsConstructor public class FeatureServiceEndpoint { - private final RestService restService; + private final OpenMapsService openMapsService; /** * Fetch Opening data from WFS. @@ -27,6 +27,6 @@ public class FeatureServiceEndpoint { public Object getOpeningPolygonAndProperties( @PathVariable String openingId) { - return restService.getOpeningPolygonAndProperties(openingId); + return openMapsService.getOpeningPolygonAndProperties(openingId); } } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/common/service/DashboardExtractionService.java b/backend/src/main/java/ca/bc/gov/restapi/results/common/service/DashboardExtractionService.java index ce8a6938..af01199e 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/common/service/DashboardExtractionService.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/common/service/DashboardExtractionService.java @@ -27,8 +27,8 @@ public class DashboardExtractionService { /** * Service for extracting data from oracle and adding into Postgres. * - * @param months Optional number of months. - * @param debug Optional debug mode enabled. + * @param months Optional number of months. + * @param debug Optional debug mode enabled. * @param manuallyTriggered Optional option. */ @Async @@ -47,18 +47,11 @@ public void extractDataForTheDashboard(Integer months, Boolean debug, Boolean ma dashboardInsertionService.loadDashboardData(extractionDto, startDateTime, params); } - private OracleExtractionParamsDto getParams( - Integer months, Boolean debug, Boolean manuallyTriggered) { - if (Objects.isNull(months)) { - months = 24; - } - if (Objects.isNull(debug)) { - debug = Boolean.FALSE; - } - if (Objects.isNull(manuallyTriggered)) { - manuallyTriggered = Boolean.FALSE; - } - - return new OracleExtractionParamsDto(months, debug, manuallyTriggered); - } + private OracleExtractionParamsDto getParams(Integer months, Boolean debug, Boolean manuallyTriggered) { + return new OracleExtractionParamsDto( + Objects.requireNonNullElse(months, 24), + Objects.requireNonNullElse(debug, Boolean.FALSE), + Objects.requireNonNullElse(manuallyTriggered, Boolean.FALSE) + ); +} } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/common/service/ForestClientService.java b/backend/src/main/java/ca/bc/gov/restapi/results/common/service/ForestClientService.java index 091829bd..c0f67bf2 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/common/service/ForestClientService.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/common/service/ForestClientService.java @@ -2,14 +2,15 @@ import ca.bc.gov.restapi.results.common.dto.ForestClientDto; import ca.bc.gov.restapi.results.common.provider.ForestClientApiProvider; -import java.util.Objects; import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; -import org.springframework.web.client.HttpClientErrorException; -/** This service contains methods for interacting with Forest Client API. */ +/** + * This service contains methods for interacting with Forest Client API. + */ @Slf4j @Service @RequiredArgsConstructor @@ -30,17 +31,11 @@ public Optional getClientByNumber(String clientNumber) { if (!fixedNumber.equals(clientNumber)) { log.info("Fixed client number to fetch {}", fixedNumber); } - - try { - return forestClientApiProvider.fetchClientByNumber(fixedNumber); - } catch (HttpClientErrorException.NotFound e) { - log.info(String.format("Client %s not found", clientNumber), e); - return Optional.empty(); - } + return forestClientApiProvider.fetchClientByNumber(fixedNumber); } private String checkClientNumber(String clientNumber) { - if (Objects.isNull(clientNumber)) { + if (StringUtils.isEmpty(clientNumber)) { return "00000000"; } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/common/service/RestService.java b/backend/src/main/java/ca/bc/gov/restapi/results/common/service/OpenMapsService.java similarity index 77% rename from backend/src/main/java/ca/bc/gov/restapi/results/common/service/RestService.java rename to backend/src/main/java/ca/bc/gov/restapi/results/common/service/OpenMapsService.java index c085de0b..8e84c722 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/common/service/RestService.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/common/service/OpenMapsService.java @@ -11,11 +11,11 @@ */ @Slf4j @Service -public class RestService { +public class OpenMapsService { private final RestClient restClient; - public RestService(@Qualifier("openMapsApi") RestClient openMapsApi) { + public OpenMapsService(@Qualifier("openMapsApi") RestClient openMapsApi) { this.restClient = openMapsApi; } @@ -40,14 +40,14 @@ public Object getOpeningPolygonAndProperties(String openingId) { .queryParam("SrsName", "EPSG:4326") .queryParam("PROPERTYNAME", "OPENING_ID," - + "GEOMETRY," - + "REGION_NAME," - + "REGION_CODE," - + "DISTRICT_NAME," - + "DISTRICT_CODE," - + "CLIENT_NAME," - + "CLIENT_NUMBER," - + "OPENING_WHEN_CREATED" + + "GEOMETRY," + + "REGION_NAME," + + "REGION_CODE," + + "DISTRICT_NAME," + + "DISTRICT_CODE," + + "CLIENT_NAME," + + "CLIENT_NUMBER," + + "OPENING_WHEN_CREATED" ) .queryParam("CQL_FILTER", "OPENING_ID=" + openingId) .build(Map.of()) diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/common/util/TimestampUtil.java b/backend/src/main/java/ca/bc/gov/restapi/results/common/util/TimestampUtil.java index a79a0528..bfde87a8 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/common/util/TimestampUtil.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/common/util/TimestampUtil.java @@ -2,14 +2,20 @@ import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.Period; import java.time.format.DateTimeFormatter; import java.util.Objects; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; -/** This class contains useful methods for parsing and handling timestamps. */ +/** + * This class contains useful methods for parsing and handling timestamps. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class TimestampUtil { - private TimestampUtil() {} /** * Parses a date string to a {@link LocalDateTime} instance. Format: yyyy-MM-dd. @@ -18,13 +24,34 @@ private TimestampUtil() {} * @return LocalDateTime parsed or null if a null value is found. */ public static LocalDateTime parseDateString(String dateStr) { - if (Objects.isNull(dateStr)) { + if (StringUtils.isEmpty(dateStr)) { return null; } + return parseDateString(LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + } + + /** + * Parses a LocalDate to a LocalDateTime instance with the time set to LocalTime.MIN. + * + * @param localDate The LocalDate to be parsed + * @return LocalDateTime parsed or null if a null value is found. + */ + public static LocalDateTime parseDateString(LocalDate localDate) { + return parseDateString(localDate, LocalTime.MIN); + } - LocalDate entryDateStartLd = - LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd")); - return entryDateStartLd.atStartOfDay(); + /** + * Parses a LocalDate to a LocalDateTime instance with the specified time. + * + * @param localDate The LocalDate to be parsed + * @param time The LocalTime to be set + * @return LocalDateTime parsed or null if a null value is found. + */ + public static LocalDateTime parseDateString(LocalDate localDate, LocalTime time) { + if (Objects.isNull(localDate)) { + return null; + } + return localDate.atTime(time); } /** diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/common/endpoint/FeatureServiceEndpointTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/common/endpoint/FeatureServiceEndpointTest.java index 25d147ec..cce26e10 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/common/endpoint/FeatureServiceEndpointTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/common/endpoint/FeatureServiceEndpointTest.java @@ -6,7 +6,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import ca.bc.gov.restapi.results.common.service.RestService; +import ca.bc.gov.restapi.results.common.service.OpenMapsService; import java.util.Optional; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -24,7 +24,8 @@ class FeatureServiceEndpointTest { @Autowired private MockMvc mockMvc; - @MockBean RestService restService; + @MockBean + OpenMapsService openMapsService; @Test @DisplayName("Get opening polygon and properties happy path should succeed") @@ -67,7 +68,7 @@ void getOpeningPolygonAndProperties_happyPath_shouldSucceed() throws Exception { } """; - when(restService.getOpeningPolygonAndProperties(openingId)) + when(openMapsService.getOpeningPolygonAndProperties(openingId)) .thenReturn(ResponseEntity.of(Optional.of(response))); mockMvc diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/common/service/DashboardExtractionServiceTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/common/service/DashboardExtractionServiceTest.java index 4ce8a163..8b42dacc 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/common/service/DashboardExtractionServiceTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/common/service/DashboardExtractionServiceTest.java @@ -9,20 +9,26 @@ import ca.bc.gov.restapi.results.oracle.service.OracleExtractionService; import ca.bc.gov.restapi.results.postgres.service.DashboardInsertionService; import java.util.ArrayList; +import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) +@DisplayName("Unit Test | Dashboard Extraction Service") class DashboardExtractionServiceTest { - @Mock OracleExtractionService oracleExtractionService; + @Mock + OracleExtractionService oracleExtractionService; - @Mock DashboardInsertionService dashboardInsertionService; + @Mock + DashboardInsertionService dashboardInsertionService; private DashboardExtractionService dashboardExtractionService; @@ -32,19 +38,50 @@ void setup() { new DashboardExtractionService(oracleExtractionService, dashboardInsertionService); } - @Test + @ParameterizedTest + @MethodSource("basicValues") @DisplayName("extract data for the dashboard happy path should succeed") - void extractDataForTheDashboard_happyPath_shouldSucceed() { - OracleExtractionParamsDto params = new OracleExtractionParamsDto(24, false, false); + void extractDataForTheDashboard_happyPath_shouldSucceed( + Integer months, + Boolean debug, + Boolean manuallyTriggered + ) { + OracleExtractionParamsDto params = new OracleExtractionParamsDto( + 24, + false, + false + ); OracleExtractionDto extractionDto = - new OracleExtractionDto(null, null, null, null, null, null, new ArrayList<>()); + new OracleExtractionDto( + null, + null, + null, + null, + null, + null, + new ArrayList<>() + ); when(oracleExtractionService.getOpeningActivities(params)).thenReturn(extractionDto); doNothing().when(dashboardInsertionService).loadDashboardData(any(), any(), any()); - dashboardExtractionService.extractDataForTheDashboard(null, null, null); + dashboardExtractionService.extractDataForTheDashboard(months, debug, manuallyTriggered); Assertions.assertFalse(extractionDto.logMessages().isEmpty()); } + + private static Stream basicValues() { + return Stream.of( + Arguments.of(24, false, false), + Arguments.of(24, false, null), + Arguments.of(24, null, false), + Arguments.of(24, null, null), + Arguments.of(null, false, false), + Arguments.of(null, null, false), + Arguments.of(null, null, null), + Arguments.of(null, false, null) + ); + } + } diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/common/service/ForestClientServiceTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/common/service/ForestClientServiceTest.java index 67e20492..801a6a31 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/common/service/ForestClientServiceTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/common/service/ForestClientServiceTest.java @@ -12,10 +12,14 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) +@DisplayName("Unit Test | Forest Client Service") class ForestClientServiceTest { @Mock ForestClientApiProvider forestClientApiProvider; @@ -107,4 +111,19 @@ void getClientByNumber_notFound_shouldSucceed() { Assertions.assertTrue(clientDto.isEmpty()); } + + @ParameterizedTest + @ValueSource(strings = {"", " ", " ", " "}) + @NullAndEmptySource + @DisplayName("No client number should return empty") + void shouldGetEmptyWhenNoClientNumber(String clientNumber) { + + when(forestClientApiProvider.fetchClientByNumber("00000000")) + .thenReturn(Optional.empty()); + + Optional clientDtoOptional = + forestClientService.getClientByNumber(clientNumber); + + Assertions.assertTrue(clientDtoOptional.isEmpty()); + } } diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/common/service/RestServiceTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/common/service/OpenMapsServiceTest.java similarity index 75% rename from backend/src/test/java/ca/bc/gov/restapi/results/common/service/RestServiceTest.java rename to backend/src/test/java/ca/bc/gov/restapi/results/common/service/OpenMapsServiceTest.java index 1a1a41e8..01924c78 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/common/service/RestServiceTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/common/service/OpenMapsServiceTest.java @@ -15,7 +15,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.web.client.RestClient; -class RestServiceTest { +@DisplayName("Integrated Test | OpenMapsService") +class OpenMapsServiceIntegrationTest { @RegisterExtension static WireMockExtension clientApiStub = WireMockExtension @@ -30,7 +31,7 @@ class RestServiceTest { .configureStaticDsl(true) .build(); - private final RestService restService = new RestService( + private final OpenMapsService openMapsService = new OpenMapsService( RestClient.builder().baseUrl("http://localhost:10001").build() ); @@ -49,20 +50,20 @@ void getOpeningPolygonAndProperties_happyPath_shouldSucceed() { .withQueryParam("SrsName", equalTo("EPSG:4326")) .withQueryParam("PROPERTYNAME", equalTo("OPENING_ID," - + "GEOMETRY," - + "REGION_NAME," - + "REGION_CODE," - + "DISTRICT_NAME," - + "DISTRICT_CODE," - + "CLIENT_NAME," - + "CLIENT_NUMBER," - + "OPENING_WHEN_CREATED" + + "GEOMETRY," + + "REGION_NAME," + + "REGION_CODE," + + "DISTRICT_NAME," + + "DISTRICT_CODE," + + "CLIENT_NAME," + + "CLIENT_NUMBER," + + "OPENING_WHEN_CREATED" )) .withQueryParam("CQL_FILTER", equalTo("OPENING_ID=" + openingId)) .willReturn(okJson("{}")) ); - Object response = restService.getOpeningPolygonAndProperties(openingId); + Object response = openMapsService.getOpeningPolygonAndProperties(openingId); Assertions.assertNotNull(response); } @@ -82,20 +83,20 @@ void getOpeningPolygonAndProperties_notFound_shouldFail() { .withQueryParam("SrsName", equalTo("EPSG:4326")) .withQueryParam("PROPERTYNAME", equalTo("OPENING_ID," - + "GEOMETRY," - + "REGION_NAME," - + "REGION_CODE," - + "DISTRICT_NAME," - + "DISTRICT_CODE," - + "CLIENT_NAME," - + "CLIENT_NUMBER," - + "OPENING_WHEN_CREATED" + + "GEOMETRY," + + "REGION_NAME," + + "REGION_CODE," + + "DISTRICT_NAME," + + "DISTRICT_CODE," + + "CLIENT_NAME," + + "CLIENT_NUMBER," + + "OPENING_WHEN_CREATED" )) .withQueryParam("CQL_FILTER", equalTo("OPENING_ID=" + openingId)) .willReturn(notFound()) ); - Object response = restService.getOpeningPolygonAndProperties(openingId); + Object response = openMapsService.getOpeningPolygonAndProperties(openingId); Assertions.assertNull(response); } diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/common/util/TimestampUtilTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/common/util/TimestampUtilTest.java index c885e0b6..3415e966 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/common/util/TimestampUtilTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/common/util/TimestampUtilTest.java @@ -1,45 +1,122 @@ package ca.bc.gov.restapi.results.common.util; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; +@DisplayName("Unit Test | Timestamp Util") class TimestampUtilTest { - @Test - void parseDateStringTest() { - LocalDateTime dateTimeParsed = TimestampUtil.parseDateString("2024-04-09"); - Assertions.assertNotNull(dateTimeParsed); - Assertions.assertEquals(2024, dateTimeParsed.getYear()); - Assertions.assertEquals(4, dateTimeParsed.getMonthValue()); - Assertions.assertEquals(9, dateTimeParsed.getDayOfMonth()); + @ParameterizedTest + @MethodSource("startEndOfDay") + @DisplayName("Date begin and end of day") + void shouldGetStartOrEndOfDay(LocalDate date, boolean start){ + + LocalDateTime dateTime = TimestampUtil.parseDateString(date, start ? LocalTime.MIN : LocalTime.MAX); + + Assertions.assertEquals(date, dateTime.toLocalDate()); + - LocalDateTime dateTimeNull = TimestampUtil.parseDateString(null); + if(start){ + Assertions.assertEquals(0, dateTime.getHour()); + Assertions.assertEquals(0, dateTime.getMinute()); + Assertions.assertEquals(0, dateTime.getSecond()); + Assertions.assertEquals(0, dateTime.getNano()); + } else { + Assertions.assertEquals(23, dateTime.getHour()); + Assertions.assertEquals(59, dateTime.getMinute()); + Assertions.assertEquals(59, dateTime.getSecond()); + Assertions.assertEquals(999999999, dateTime.getNano()); + } + + } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("Parse date string null or empty") + void parseDateStringTest(String value) { + LocalDateTime dateTimeNull = TimestampUtil.parseDateString(value); Assertions.assertNull(dateTimeNull); } - @Test - void getLocalDateTimeIndexTest() { + @ParameterizedTest(name = "Parse date string {0} to {1}") + @MethodSource("parseValuesString") + @DisplayName("Parse date string") + void parseDateStringTest( + String dateStr, int year, int month, int day + ) { + LocalDateTime dateTimeParsed = TimestampUtil.parseDateString(dateStr); + Assertions.assertNotNull(dateTimeParsed); + Assertions.assertEquals(year, dateTimeParsed.getYear()); + Assertions.assertEquals(month, dateTimeParsed.getMonthValue()); + Assertions.assertEquals(day, dateTimeParsed.getDayOfMonth()); + } + + @ParameterizedTest(name = "Get local date time index for {1} months ago as {0}") + @MethodSource("indexValues") + @DisplayName("Get local date time index") + void getLocalDateTimeIndexTest( + int expectedIndex, long monthsAgo + ) { LocalDateTime now = LocalDateTime.now(); + LocalDateTime dateTime = now.minusMonths(monthsAgo); + Assertions.assertEquals(expectedIndex, TimestampUtil.getLocalDateTimeIndex(dateTime)); + } + + private static Stream indexValues() { + return Stream.of( + Arguments.of(0, 1L), + Arguments.of(0, 2L), + Arguments.of(0, 3L), + Arguments.of(0, 4L), + Arguments.of(0, 5L), + Arguments.of(1, 6L), + Arguments.of(1, 7L), + Arguments.of(1, 8L), + Arguments.of(1, 9L), + Arguments.of(1, 10L), + Arguments.of(1, 11L), + Arguments.of(2, 12L), + Arguments.of(2, 13L), + Arguments.of(2, 14L), + Arguments.of(2, 15L), + Arguments.of(2, 16L), + Arguments.of(2, 17L), + Arguments.of(3, 18L), + Arguments.of(3, 36L) + ); + } + + private static Stream parseValuesString(){ + return Stream.of( + Arguments.of("2024-04-09", 2024, 4, 9), + Arguments.of("1987-01-01", 1987,1,1), + Arguments.of("2022-12-31", 2022,12,31), + Arguments.of("2023-02-28", 2023,2,28), + Arguments.of("2024-02-29", 2024,2,29) + ); + } - Assertions.assertEquals(0, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(1L))); - Assertions.assertEquals(0, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(2L))); - Assertions.assertEquals(0, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(3L))); - Assertions.assertEquals(0, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(4L))); - Assertions.assertEquals(0, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(5L))); - Assertions.assertEquals(1, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(6L))); - Assertions.assertEquals(1, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(7L))); - Assertions.assertEquals(1, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(8L))); - Assertions.assertEquals(1, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(9L))); - Assertions.assertEquals(1, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(10L))); - Assertions.assertEquals(1, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(11L))); - Assertions.assertEquals(2, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(12L))); - Assertions.assertEquals(2, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(13L))); - Assertions.assertEquals(2, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(14L))); - Assertions.assertEquals(2, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(15L))); - Assertions.assertEquals(2, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(16L))); - Assertions.assertEquals(2, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(17L))); - Assertions.assertEquals(3, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(18L))); - Assertions.assertEquals(3, TimestampUtil.getLocalDateTimeIndex(now.minusMonths(36L))); + private static Stream startEndOfDay(){ + return Stream.of( + Arguments.of(LocalDate.of(2024, 4, 9), true), + Arguments.of(LocalDate.of(1987, 1, 1), true), + Arguments.of(LocalDate.of(2022, 12, 31), true), + Arguments.of(LocalDate.of(2023, 2, 28), true), + Arguments.of(LocalDate.of(2024, 2, 29), true), + Arguments.of(LocalDate.of(2024, 4, 9), false), + Arguments.of(LocalDate.of(1987, 1, 1), false), + Arguments.of(LocalDate.of(2022, 12, 31), false), + Arguments.of(LocalDate.of(2023, 2, 28), false), + Arguments.of(LocalDate.of(2024, 2, 29), false) + ); } } diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/extensions/WithMockJwt.java b/backend/src/test/java/ca/bc/gov/restapi/results/extensions/WithMockJwt.java new file mode 100644 index 00000000..391f4eb1 --- /dev/null +++ b/backend/src/test/java/ca/bc/gov/restapi/results/extensions/WithMockJwt.java @@ -0,0 +1,23 @@ +package ca.bc.gov.restapi.results.extensions; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.springframework.security.test.context.support.WithSecurityContext; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@WithSecurityContext(factory = WithMockJwtSecurityContextFactory.class) +public @interface WithMockJwt { + String value() default "test"; + String[] roles() default {"user_read"}; + String email() default "test@test.ca"; + String idp() default "idir"; + String displayName() default "Test, Automated WLRS:EX"; + +} diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/extensions/WithMockJwtSecurityContextFactory.java b/backend/src/test/java/ca/bc/gov/restapi/results/extensions/WithMockJwtSecurityContextFactory.java new file mode 100644 index 00000000..281f0026 --- /dev/null +++ b/backend/src/test/java/ca/bc/gov/restapi/results/extensions/WithMockJwtSecurityContextFactory.java @@ -0,0 +1,49 @@ +package ca.bc.gov.restapi.results.extensions; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.security.test.context.support.WithSecurityContextFactory; + +public class WithMockJwtSecurityContextFactory implements WithSecurityContextFactory { + + @Override + public SecurityContext createSecurityContext(WithMockJwt annotation) { + Jwt jwt = Jwt + .withTokenValue("token") + .header("alg", "none") + .claim("sub", annotation.value()) + .claim("client_roles", listToString(annotation.roles())) + .claim("custom:idp_name", annotation.idp()) + .claim("custom:idp_username", annotation.value()) + .claim("custom:idp_display_name", annotation.displayName()) + .claim("email", annotation.email()) + .build(); + + List authorities = AuthorityUtils.createAuthorityList(annotation.roles()); + JwtAuthenticationToken token = new JwtAuthenticationToken(jwt, authorities); + + SecurityContext context = SecurityContextHolder.createEmptyContext(); + context.setAuthentication(token); + return context; + + } + + private String listToString(String[] list) { + return listToString(Arrays.asList(list)); + } + + private String listToString(List list) { + return + list + .stream() + .map(content -> String.format("\"%s\"", content)) + .collect(Collectors.joining(",", "[", "]")); + } +} diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/security/UserServiceTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/security/LoggedUserServiceTest.java similarity index 98% rename from backend/src/test/java/ca/bc/gov/restapi/results/security/UserServiceTest.java rename to backend/src/test/java/ca/bc/gov/restapi/results/security/LoggedUserServiceTest.java index 0c8d4b39..d4daa6b3 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/security/UserServiceTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/security/LoggedUserServiceTest.java @@ -18,7 +18,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) -class UserServiceTest { +@DisplayName("Unit Test | LoggedUserService") +class LoggedUserServiceTest { @Mock UserAuthenticationHelper userAuthenticationHelper; diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/security/UserAuthenticationHelperTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/security/UserAuthenticationHelperTest.java index 051f2966..97d8d54c 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/security/UserAuthenticationHelperTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/security/UserAuthenticationHelperTest.java @@ -20,6 +20,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) +@DisplayName("Unit Test | UserAuthenticationHelper") class UserAuthenticationHelperTest { private UserAuthenticationHelper userAuthenticationHelper;