From 31568492ed5dca574a888c1c873f90041e0015c4 Mon Sep 17 00:00:00 2001 From: Mohamed Date: Sun, 11 May 2025 21:11:50 +0300 Subject: [PATCH 1/3] test: add unit and integration tests for courier analytics --- docker-compose.yml | 9 +- pom.xml | 5 + .../repositories/CourierRepository.java | 1 - .../services/CourierAnalyticsService.java | 4 +- .../analytics/util/MetricCalculator.java | 9 +- .../CourierAnalyticsControllerTest.java | 319 ++++++++++++++++ .../FulfillmentReportControllerTest.java | 3 +- .../services/CourierAnalyticsServiceTest.java | 345 ++++++++++++++++++ 8 files changed, 680 insertions(+), 15 deletions(-) create mode 100644 src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java rename src/test/java/com/Podzilla/analytics/{api => }/controllers/FulfillmentReportControllerTest.java (99%) create mode 100644 src/test/java/com/Podzilla/analytics/services/CourierAnalyticsServiceTest.java diff --git a/docker-compose.yml b/docker-compose.yml index d335ee2..1e41b2d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,8 +7,8 @@ services: - "8080:8080" environment: SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/analytics_db_dev - SPRING_DATASOURCE_USERNAME: analytics_user - SPRING_DATASOURCE_PASSWORD: password + SPRING_DATASOURCE_USERNAME: postgres + SPRING_DATASOURCE_PASSWORD: 123 depends_on: - db @@ -19,8 +19,8 @@ services: - "5432:5432" environment: POSTGRES_DB: analytics_db_dev - POSTGRES_USER: analytics_user - POSTGRES_PASSWORD: password + POSTGRES_USER: postgres + POSTGRES_PASSWORD: 123 volumes: - db_data:/var/lib/postgresql/data @@ -43,4 +43,3 @@ services: volumes: db_data: - \ No newline at end of file diff --git a/pom.xml b/pom.xml index b9290bf..57191c1 100644 --- a/pom.xml +++ b/pom.xml @@ -84,6 +84,11 @@ springdoc-openapi-starter-webmvc-ui 2.5.0 + + org.hibernate.validator + hibernate-validator + 8.0.1.Final + diff --git a/src/main/java/com/Podzilla/analytics/repositories/CourierRepository.java b/src/main/java/com/Podzilla/analytics/repositories/CourierRepository.java index 3da0777..bc4daa0 100644 --- a/src/main/java/com/Podzilla/analytics/repositories/CourierRepository.java +++ b/src/main/java/com/Podzilla/analytics/repositories/CourierRepository.java @@ -23,7 +23,6 @@ public interface CourierRepository extends JpaRepository { + "LEFT JOIN orders o " + "ON c.id = o.courier_id " + "AND o.final_status_timestamp BETWEEN :startDate AND :endDate " - + "AND o.status IN ('COMPLETED', 'FAILED') " + "GROUP BY c.id, c.name " + "ORDER BY courierId", nativeQuery = true) List findCourierPerformanceBetweenDates( diff --git a/src/main/java/com/Podzilla/analytics/services/CourierAnalyticsService.java b/src/main/java/com/Podzilla/analytics/services/CourierAnalyticsService.java index 89a9340..5106e0a 100644 --- a/src/main/java/com/Podzilla/analytics/services/CourierAnalyticsService.java +++ b/src/main/java/com/Podzilla/analytics/services/CourierAnalyticsService.java @@ -53,7 +53,7 @@ public List getCourierSuccessRate( .courierId(data.getCourierId()) .courierName(data.getCourierName()) .successRate( - MetricCalculator.calculatePercentage( + MetricCalculator.calculateRate( data.getCompletedCount(), data.getDeliveryCount())) .build()) @@ -81,7 +81,7 @@ public List getCourierPerformanceReport( .courierName(data.getCourierName()) .deliveryCount(data.getDeliveryCount()) .successRate( - MetricCalculator.calculatePercentage( + MetricCalculator.calculateRate( data.getCompletedCount(), data.getDeliveryCount())) .averageRating(data.getAverageRating()) diff --git a/src/main/java/com/Podzilla/analytics/util/MetricCalculator.java b/src/main/java/com/Podzilla/analytics/util/MetricCalculator.java index 1e91256..a32839b 100644 --- a/src/main/java/com/Podzilla/analytics/util/MetricCalculator.java +++ b/src/main/java/com/Podzilla/analytics/util/MetricCalculator.java @@ -6,8 +6,6 @@ public final class MetricCalculator { private static final int DEFAULT_SCALE = 2; - private static final BigDecimal ONE_HUNDRED = new BigDecimal("100"); - private MetricCalculator() { throw new UnsupportedOperationException( "This is a utility class and cannot be instantiated"); @@ -25,7 +23,7 @@ private MetricCalculator() { * @return The calculated percentage as a BigDecimal, or BigDecimal.ZERO * if the denominator is zero. */ - public static BigDecimal calculatePercentage(final long numerator, + public static BigDecimal calculateRate(final long numerator, final long denominator, final int scale, final RoundingMode roundingMode) { if (denominator == 0) { @@ -35,7 +33,6 @@ public static BigDecimal calculatePercentage(final long numerator, return BigDecimal.ZERO; } return BigDecimal.valueOf(numerator) - .multiply(ONE_HUNDRED) .divide(BigDecimal.valueOf(denominator), scale, roundingMode); } @@ -47,9 +44,9 @@ public static BigDecimal calculatePercentage(final long numerator, * @return The calculated percentage (scale 2, HALF_UP rounding), or * BigDecimal.ZERO if denominator is zero. */ - public static BigDecimal calculatePercentage(final long numerator, + public static BigDecimal calculateRate(final long numerator, final long denominator) { - return calculatePercentage(numerator, denominator, DEFAULT_SCALE, + return calculateRate(numerator, denominator, DEFAULT_SCALE, RoundingMode.HALF_UP); } } diff --git a/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java b/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java new file mode 100644 index 0000000..7bfa23f --- /dev/null +++ b/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java @@ -0,0 +1,319 @@ +package com.Podzilla.analytics.controllers; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import jakarta.persistence.EntityManager; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; +import org.springframework.transaction.annotation.Transactional; + +import com.Podzilla.analytics.models.Courier; +import com.Podzilla.analytics.models.Customer; +import com.Podzilla.analytics.models.Order; +import com.Podzilla.analytics.models.Region; +import com.Podzilla.analytics.repositories.CourierRepository; +import com.Podzilla.analytics.repositories.CustomerRepository; +import com.Podzilla.analytics.repositories.OrderRepository; +import com.Podzilla.analytics.repositories.RegionRepository; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.hamcrest.Matchers.*; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +public class CourierAnalyticsControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private CourierRepository courierRepository; + @Autowired + private CustomerRepository customerRepository; + @Autowired + private RegionRepository regionRepository; + @Autowired + private OrderRepository orderRepository; + + @Autowired + private EntityManager entityManager; + + private static final DateTimeFormatter ISO_LOCAL_DATE = DateTimeFormatter.ISO_LOCAL_DATE; + + private Customer customer1; + private Region region1; + private Courier courierJane; + private Courier courierJohn; + + @BeforeEach + void setUp() { + orderRepository.deleteAll(); + courierRepository.deleteAll(); + customerRepository.deleteAll(); + regionRepository.deleteAll(); + + entityManager.flush(); + entityManager.clear(); + + customer1 = customerRepository.save(Customer.builder().name("John Doe").build()); + region1 = regionRepository.save(Region.builder() + .city("Sample City") + .state("Sample State") + .country("Sample Country") + .postalCode("12345") + .build()); + + courierJane = courierRepository.save(Courier.builder() + .name("Jane Smith") + .status(Courier.CourierStatus.ACTIVE) + .build()); + + courierJohn = courierRepository.save(Courier.builder() + .name("John Doe") + .status(Courier.CourierStatus.ACTIVE) + .build()); + + orderRepository.save(Order.builder() + .totalAmount(new BigDecimal("50.00")) + .finalStatusTimestamp(LocalDateTime.now().minusDays(3)) + .status(Order.OrderStatus.COMPLETED) + .numberOfItems(1) + .courierRating(new BigDecimal("4.0")) + .customer(customer1) + .courier(courierJane) + .region(region1) + .build()); + + orderRepository.save(Order.builder() + .totalAmount(new BigDecimal("75.00")) + .finalStatusTimestamp(LocalDateTime.now().minusDays(3)) + .status(Order.OrderStatus.COMPLETED) + .numberOfItems(1) + .courierRating(new BigDecimal("4.0")) + .customer(customer1) + .courier(courierJane) + .region(region1) + .build()); + + orderRepository.save(Order.builder() + .totalAmount(new BigDecimal("120.00")) + .finalStatusTimestamp(LocalDateTime.now().minusDays(1)) + .status(Order.OrderStatus.COMPLETED) + .numberOfItems(2) + .courierRating(new BigDecimal("5.0")) + .customer(customer1) + .courier(courierJane) + .region(region1) + .build()); + + orderRepository.save(Order.builder() + .totalAmount(new BigDecimal("30.00")) + .finalStatusTimestamp(LocalDateTime.now().minusDays(2)) + .status(Order.OrderStatus.FAILED) + .numberOfItems(1) + .courierRating(null) + .customer(customer1) + .courier(courierJohn) + .region(region1) + .build()); + + orderRepository.save(Order.builder() + .totalAmount(new BigDecimal("90.00")) + .finalStatusTimestamp(LocalDateTime.now().minusDays(2)) + .status(Order.OrderStatus.COMPLETED) + .numberOfItems(1) + .courierRating(new BigDecimal("3.0")) + .customer(customer1) + .courier(courierJohn) + .region(region1) + .build()); + + entityManager.flush(); + entityManager.clear(); + } + + @AfterEach + void tearDown() { + orderRepository.deleteAll(); + courierRepository.deleteAll(); + customerRepository.deleteAll(); + regionRepository.deleteAll(); + entityManager.flush(); + entityManager.clear(); + } + + @Test + void contextLoads() { + } + + @Test + void getCourierDeliveryCounts_shouldReturnCountsForSpecificDateRange() throws Exception { + LocalDate startDate = LocalDate.now().minusDays(4); + LocalDate endDate = LocalDate.now().minusDays(2); + + mockMvc.perform(get("/couriers/delivery-counts") + .param("startDate", startDate.format( + ISO_LOCAL_DATE)) + .param("endDate", endDate.format( + ISO_LOCAL_DATE))) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].deliveryCount").value(hasItem(2))) + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].deliveryCount").value(hasItem(2))); + } + + @Test + void getCourierDeliveryCounts_shouldReturnZeroCountsWhenNoDeliveriesInDateRange() + throws Exception { + LocalDate startDate = LocalDate.now().plusDays(1); + LocalDate endDate = LocalDate.now().plusDays(2); + + mockMvc.perform(get("/couriers/delivery-counts") + .param("startDate", startDate.format(ISO_LOCAL_DATE)) + .param("endDate", endDate.format(ISO_LOCAL_DATE))) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].courierName").value(hasItem("Jane Smith"))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].deliveryCount").value(hasItem(0))) + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].courierName").value(hasItem("John Doe"))) + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].deliveryCount").value(hasItem(0))); + } + + @Test + void getCourierDeliveryCounts_shouldHandleInvalidDateRange_startDateAfterEndDate() + throws Exception { + LocalDateTime startDate = LocalDateTime.now().minusDays(1); + LocalDateTime endDate = LocalDateTime.now().minusDays(3); + + mockMvc.perform(get("/couriers/delivery-counts") + .param("startDate", startDate.format( + ISO_LOCAL_DATE)) + .param("endDate", endDate.format( + ISO_LOCAL_DATE))) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isBadRequest()); + } + + @Test + void getCourierSuccessRate_shouldReturnSuccessRatesForSpecificDateRange() + throws Exception { + LocalDateTime startDate = LocalDateTime.now().minusDays(4); + LocalDateTime endDate = LocalDateTime.now().minusDays(2); + + mockMvc.perform(get("/couriers/success-rate") + .param("startDate", startDate.format( + ISO_LOCAL_DATE)) + .param("endDate", endDate.format( + ISO_LOCAL_DATE))) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect( + jsonPath("$[?(@.courierName == 'Jane Smith')].successRate").value(hasItem(closeTo(1.0, 0.001)))) + .andExpect( + jsonPath("$[?(@.courierName == 'John Doe')].successRate").value(hasItem(closeTo(0.5, 0.001)))); + } + + @Test + void getCourierAverageRating_shouldReturnAllAverageRatingsWhenNoDateRangeProvided() + throws Exception { + LocalDateTime startDate = LocalDateTime.now().minusDays(4); + LocalDateTime endDate = LocalDateTime.now(); + mockMvc.perform(get("/couriers/average-rating") + .param("startDate", startDate.format( + ISO_LOCAL_DATE)) + .param("endDate", endDate.format( + ISO_LOCAL_DATE))) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].averageRating") + .value(hasItem(closeTo(4.333, 0.001)))) + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].averageRating") + .value(hasItem(closeTo(3.0, 0.001)))); + } + + @Test + void getCourierAverageRating_shouldReturnAverageRatingsForSpecificDateRange() + throws Exception { + + LocalDateTime startDate = LocalDateTime.now().minusDays(4); + LocalDateTime endDate = LocalDateTime.now().minusDays(2); + + mockMvc.perform(get("/couriers/average-rating") + .param("startDate", startDate.format(ISO_LOCAL_DATE)) + .param("endDate", endDate.format(ISO_LOCAL_DATE))) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].averageRating") + .value(hasItem(closeTo(4.0, 0.001)))) + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].averageRating") + .value(hasItem(closeTo(3.0, 0.001)))); + } + + @Test + void getCourierPerformanceReport_shouldReturnAllPerformanceReportsWhenNoDateRangeProvided() + throws Exception { + LocalDateTime startDate = LocalDateTime.now().minusDays(4); + LocalDateTime endDate = LocalDateTime.now(); + mockMvc.perform(get("/couriers/performance-report") + .param("startDate", startDate.format( + ISO_LOCAL_DATE)) + .param("endDate", endDate.format( + ISO_LOCAL_DATE))) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].deliveryCount").value(hasItem(3))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].successRate") + .value(hasItem(closeTo(1.0, 0.001)))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].averageRating") + .value(hasItem(closeTo(4.333, 0.001)))) + + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].deliveryCount").value(hasItem(2))) + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].successRate") + .value(hasItem(closeTo(0.5, 0.001)))) + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].averageRating") + .value(hasItem(closeTo(3.0, 0.001)))); + } + + @Test + void getCourierPerformanceReport_shouldReturnPerformanceReportsForSpecificDateRange() + throws Exception { + LocalDateTime startDate = LocalDateTime.now().minusDays(4); + LocalDateTime endDate = LocalDateTime.now().minusDays(2); + + mockMvc.perform(get("/couriers/performance-report") + .param("startDate", startDate.format(ISO_LOCAL_DATE)) + .param("endDate", endDate.format(ISO_LOCAL_DATE))) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].deliveryCount").value(hasItem(2))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].successRate") + .value(hasItem(closeTo(1.0, 0.001)))) + .andExpect(jsonPath("$[?(@.courierName == 'Jane Smith')].averageRating") + .value(hasItem(closeTo(4.0, 0.001)))) + + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].deliveryCount").value(hasItem(2))) + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].successRate") + .value(hasItem(closeTo(0.5, 0.001)))) + .andExpect(jsonPath("$[?(@.courierName == 'John Doe')].averageRating") + .value(hasItem(closeTo(3.0, 0.001)))); + } +} diff --git a/src/test/java/com/Podzilla/analytics/api/controllers/FulfillmentReportControllerTest.java b/src/test/java/com/Podzilla/analytics/controllers/FulfillmentReportControllerTest.java similarity index 99% rename from src/test/java/com/Podzilla/analytics/api/controllers/FulfillmentReportControllerTest.java rename to src/test/java/com/Podzilla/analytics/controllers/FulfillmentReportControllerTest.java index a72a54e..2d55ac5 100644 --- a/src/test/java/com/Podzilla/analytics/api/controllers/FulfillmentReportControllerTest.java +++ b/src/test/java/com/Podzilla/analytics/controllers/FulfillmentReportControllerTest.java @@ -1,4 +1,4 @@ -package com.Podzilla.analytics.api.controllers; +package com.Podzilla.analytics.controllers; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -18,6 +18,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import com.Podzilla.analytics.api.controllers.FulfillmentReportController; import com.Podzilla.analytics.api.dtos.fulfillment.FulfillmentPlaceToShipRequest; import com.Podzilla.analytics.api.dtos.fulfillment.FulfillmentShipToDeliverRequest; import com.Podzilla.analytics.api.dtos.fulfillment.FulfillmentShipToDeliverRequest.ShipToDeliverGroupBy; diff --git a/src/test/java/com/Podzilla/analytics/services/CourierAnalyticsServiceTest.java b/src/test/java/com/Podzilla/analytics/services/CourierAnalyticsServiceTest.java new file mode 100644 index 0000000..0faa003 --- /dev/null +++ b/src/test/java/com/Podzilla/analytics/services/CourierAnalyticsServiceTest.java @@ -0,0 +1,345 @@ +package com.Podzilla.analytics.services; + +import com.Podzilla.analytics.api.dtos.courier.CourierAverageRatingResponse; +import com.Podzilla.analytics.api.dtos.courier.CourierDeliveryCountResponse; +import com.Podzilla.analytics.api.dtos.courier.CourierPerformanceReportResponse; +import com.Podzilla.analytics.api.dtos.courier.CourierSuccessRateResponse; +import com.Podzilla.analytics.api.projections.CourierPerformanceProjection; +import com.Podzilla.analytics.repositories.CourierRepository; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class CourierAnalyticsServiceTest { + + @Mock + private CourierRepository courierRepository; + + @InjectMocks + private CourierAnalyticsService courierAnalyticsService; + + private LocalDate testStartDate; + private LocalDate testEndDate; + private LocalDateTime expectedStartDateTime; + private LocalDateTime expectedEndDateTime; + + @BeforeEach + void setUp() { + testStartDate = LocalDate.of(2024, 1, 1); + testEndDate = LocalDate.of(2024, 1, 31); + expectedStartDateTime = testStartDate.atStartOfDay(); + expectedEndDateTime = testEndDate.atTime(LocalTime.MAX); + } + + private CourierPerformanceProjection createMockProjection( + Long courierId, String courierName, Long deliveryCount, Long completedCount, BigDecimal averageRating) { + CourierPerformanceProjection mockProjection = Mockito.mock(CourierPerformanceProjection.class); + Mockito.lenient().when(mockProjection.getCourierId()).thenReturn(courierId); + Mockito.lenient().when(mockProjection.getCourierName()).thenReturn(courierName); + Mockito.lenient().when(mockProjection.getDeliveryCount()).thenReturn(deliveryCount); + Mockito.lenient().when(mockProjection.getCompletedCount()).thenReturn(completedCount); + Mockito.lenient().when(mockProjection.getAverageRating()).thenReturn(averageRating); + return mockProjection; + } + + @Test + void getCourierDeliveryCounts_shouldReturnCorrectCountsForMultipleCouriers() { + // Arrange + CourierPerformanceProjection janeData = createMockProjection( + 1L, "Jane", 10L, 8L, new BigDecimal("4.5")); + CourierPerformanceProjection johnData = createMockProjection( + 2L, "John", 5L, 5L, new BigDecimal("4.0")); + + when(courierRepository.findCourierPerformanceBetweenDates( + any(LocalDateTime.class), any(LocalDateTime.class))) + .thenReturn(Arrays.asList(janeData, johnData)); + + // Act + List result = courierAnalyticsService + .getCourierDeliveryCounts(testStartDate, testEndDate); + + // Assert + assertNotNull(result); + assertEquals(2, result.size()); + + CourierDeliveryCountResponse janeResponse = result.stream() + .filter(r -> r.getCourierName().equals("Jane")) + .findFirst().orElse(null); + assertNotNull(janeResponse); + assertEquals(1L, janeResponse.getCourierId()); + assertEquals(10, janeResponse.getDeliveryCount()); + + CourierDeliveryCountResponse johnResponse = result.stream() + .filter(r -> r.getCourierName().equals("John")) + .findFirst().orElse(null); + assertNotNull(johnResponse); + assertEquals(2L, johnResponse.getCourierId()); + assertEquals(5, johnResponse.getDeliveryCount()); + + // Verify repository method was called with correct LocalDateTime arguments + Mockito.verify(courierRepository).findCourierPerformanceBetweenDates( + expectedStartDateTime, expectedEndDateTime); + } + + @Test + void getCourierDeliveryCounts_shouldReturnEmptyListWhenNoData() { + // Arrange + when(courierRepository.findCourierPerformanceBetweenDates( + any(LocalDateTime.class), any(LocalDateTime.class))) + .thenReturn(Collections.emptyList()); + + // Act + List result = courierAnalyticsService + .getCourierDeliveryCounts(testStartDate, testEndDate); + + // Assert + assertNotNull(result); + assertTrue(result.isEmpty()); + + Mockito.verify(courierRepository).findCourierPerformanceBetweenDates( + expectedStartDateTime, expectedEndDateTime); + } + + @Test + void getCourierSuccessRate_shouldReturnCorrectRates() { + // Arrange + // Jane: 8 completed out of 10 deliveries = 80% + CourierPerformanceProjection janeData = createMockProjection( + 1L, "Jane", 10L, 8L, new BigDecimal("4.5")); + // John: 5 completed out of 5 deliveries = 100% + CourierPerformanceProjection johnData = createMockProjection( + 2L, "John", 5L, 5L, new BigDecimal("4.0")); + // Peter: 0 completed out of 2 deliveries = 0% + CourierPerformanceProjection peterData = createMockProjection( + 3L, "Peter", 2L, 0L, new BigDecimal("3.0")); + + when(courierRepository.findCourierPerformanceBetweenDates( + any(LocalDateTime.class), any(LocalDateTime.class))) + .thenReturn(Arrays.asList(janeData, johnData, peterData)); + + // Act + List result = courierAnalyticsService + .getCourierSuccessRate(testStartDate, testEndDate); + + // Assert + assertNotNull(result); + assertEquals(3, result.size()); + + CourierSuccessRateResponse janeResponse = result.stream() + .filter(r -> r.getCourierName().equals("Jane")) + .findFirst().orElse(null); + assertNotNull(janeResponse); + assertEquals(1L, janeResponse.getCourierId()); + assertTrue(janeResponse.getSuccessRate().compareTo(new BigDecimal("0.80")) == 0); + + CourierSuccessRateResponse johnResponse = result.stream() + .filter(r -> r.getCourierName().equals("John")) + .findFirst().orElse(null); + assertNotNull(johnResponse); + assertEquals(2L, johnResponse.getCourierId()); + assertTrue(johnResponse.getSuccessRate().compareTo(new BigDecimal("1.00")) == 0); + + CourierSuccessRateResponse peterResponse = result.stream() + .filter(r -> r.getCourierName().equals("Peter")) + .findFirst().orElse(null); + assertNotNull(peterResponse); + assertEquals(3L, peterResponse.getCourierId()); + assertTrue(peterResponse.getSuccessRate().compareTo(new BigDecimal("0.00")) == 0); + + Mockito.verify(courierRepository).findCourierPerformanceBetweenDates( + expectedStartDateTime, expectedEndDateTime); + } + + @Test + void getCourierSuccessRate_shouldHandleZeroDeliveryCountGracefully() { + // Arrange + // Mark: 0 completed out of 0 deliveries. MetricCalculator should handle this + // (e.g., return 0 or null) + CourierPerformanceProjection markData = createMockProjection( + 4L, "Mark", 0L, 0L, new BigDecimal("0.0")); + + when(courierRepository.findCourierPerformanceBetweenDates( + any(LocalDateTime.class), any(LocalDateTime.class))) + .thenReturn(Collections.singletonList(markData)); + + // Act + List result = courierAnalyticsService + .getCourierSuccessRate(testStartDate, testEndDate); + + // Assert + assertNotNull(result); + assertEquals(1, result.size()); + CourierSuccessRateResponse markResponse = result.get(0); + assertEquals(4L, markResponse.getCourierId()); + assertTrue(markResponse.getSuccessRate().compareTo(new BigDecimal("0.00")) == 0); + + Mockito.verify(courierRepository).findCourierPerformanceBetweenDates( + expectedStartDateTime, expectedEndDateTime); + } + + @Test + void getCourierSuccessRate_shouldReturnEmptyListWhenNoData() { + // Arrange + when(courierRepository.findCourierPerformanceBetweenDates( + any(LocalDateTime.class), any(LocalDateTime.class))) + .thenReturn(Collections.emptyList()); + + // Act + List result = courierAnalyticsService + .getCourierSuccessRate(testStartDate, testEndDate); + + // Assert + assertNotNull(result); + assertTrue(result.isEmpty()); + + Mockito.verify(courierRepository).findCourierPerformanceBetweenDates( + expectedStartDateTime, expectedEndDateTime); + } + + @Test + void getCourierAverageRating_shouldReturnCorrectAverageRatings() { + // Arrange + CourierPerformanceProjection janeData = createMockProjection( + 1L, "Jane", 10L, 8L, new BigDecimal("4.5")); + CourierPerformanceProjection johnData = createMockProjection( + 2L, "John", 5L, 5L, new BigDecimal("4.0")); + // Peter: No rating available or 0.0 rating (depends on projection and database) + CourierPerformanceProjection peterData = createMockProjection( + 3L, "Peter", 2L, 0L, null); // Assuming null for no rating + + when(courierRepository.findCourierPerformanceBetweenDates( + any(LocalDateTime.class), any(LocalDateTime.class))) + .thenReturn(Arrays.asList(janeData, johnData, peterData)); + + // Act + List result = courierAnalyticsService + .getCourierAverageRating(testStartDate, testEndDate); + + // Assert + assertNotNull(result); + assertEquals(3, result.size()); + + CourierAverageRatingResponse janeResponse = result.stream() + .filter(r -> r.getCourierName().equals("Jane")) + .findFirst().orElse(null); + assertNotNull(janeResponse); + assertEquals(1L, janeResponse.getCourierId()); + assertEquals(new BigDecimal("4.5"), janeResponse.getAverageRating()); + + CourierAverageRatingResponse johnResponse = result.stream() + .filter(r -> r.getCourierName().equals("John")) + .findFirst().orElse(null); + assertNotNull(johnResponse); + assertEquals(2L, johnResponse.getCourierId()); + assertEquals(new BigDecimal("4.0"), johnResponse.getAverageRating()); + + CourierAverageRatingResponse peterResponse = result.stream() + .filter(r -> r.getCourierName().equals("Peter")) + .findFirst().orElse(null); + assertNotNull(peterResponse); + assertNull(peterResponse.getAverageRating()); + + Mockito.verify(courierRepository).findCourierPerformanceBetweenDates( + expectedStartDateTime, expectedEndDateTime); + } + + @Test + void getCourierAverageRating_shouldReturnEmptyListWhenNoData() { + // Arrange + when(courierRepository.findCourierPerformanceBetweenDates( + any(LocalDateTime.class), any(LocalDateTime.class))) + .thenReturn(Collections.emptyList()); + + // Act + List result = courierAnalyticsService + .getCourierAverageRating(testStartDate, testEndDate); + + // Assert + assertNotNull(result); + assertTrue(result.isEmpty()); + + Mockito.verify(courierRepository).findCourierPerformanceBetweenDates( + expectedStartDateTime, expectedEndDateTime); + } + + @Test + void getCourierPerformanceReport_shouldReturnComprehensiveReport() { + // Arrange + // Jane: 8 completed out of 10 deliveries = 80%, Avg Rating 4.5 + CourierPerformanceProjection janeData = createMockProjection( + 1L, "Jane", 10L, 8L, new BigDecimal("4.5")); + // John: 5 completed out of 5 deliveries = 100%, Avg Rating 4.0 + CourierPerformanceProjection johnData = createMockProjection( + 2L, "John", 5L, 5L, new BigDecimal("4.0")); + + when(courierRepository.findCourierPerformanceBetweenDates( + any(LocalDateTime.class), any(LocalDateTime.class))) + .thenReturn(Arrays.asList(janeData, johnData)); + + // Act + List result = courierAnalyticsService + .getCourierPerformanceReport(testStartDate, testEndDate); + + // Assert + assertNotNull(result); + assertEquals(2, result.size()); + + CourierPerformanceReportResponse janeResponse = result.stream() + .filter(r -> r.getCourierName().equals("Jane")) + .findFirst().orElse(null); + assertNotNull(janeResponse); + assertEquals(1L, janeResponse.getCourierId()); + assertEquals(10, janeResponse.getDeliveryCount()); + assertTrue(janeResponse.getSuccessRate().compareTo(new BigDecimal("0.80")) == 0); + assertTrue(janeResponse.getAverageRating().compareTo(new BigDecimal("4.5")) == 0); + + CourierPerformanceReportResponse johnResponse = result.stream() + .filter(r -> r.getCourierName().equals("John")) + .findFirst().orElse(null); + assertNotNull(johnResponse); + assertEquals(2L, johnResponse.getCourierId()); + assertEquals(5, johnResponse.getDeliveryCount()); + assertTrue(johnResponse.getSuccessRate().compareTo(new BigDecimal("1.00")) == 0); + assertTrue(johnResponse.getAverageRating().compareTo(new BigDecimal("4.0")) == 0); + + Mockito.verify(courierRepository).findCourierPerformanceBetweenDates( + expectedStartDateTime, expectedEndDateTime); + } + + @Test + void getCourierPerformanceReport_shouldReturnEmptyListWhenNoData() { + // Arrange + when(courierRepository.findCourierPerformanceBetweenDates( + any(LocalDateTime.class), any(LocalDateTime.class))) + .thenReturn(Collections.emptyList()); + + // Act + List result = courierAnalyticsService + .getCourierPerformanceReport(testStartDate, testEndDate); + + // Assert + assertNotNull(result); + assertTrue(result.isEmpty()); + + Mockito.verify(courierRepository).findCourierPerformanceBetweenDates( + expectedStartDateTime, expectedEndDateTime); + } +} \ No newline at end of file From fb6e960415f9dfee7b8ead9a9daf582bc957f83f Mon Sep 17 00:00:00 2001 From: Mohamed Date: Sun, 11 May 2025 21:45:38 +0300 Subject: [PATCH 2/3] test: Configure in-memory H2 for integration tests --- pom.xml | 5 +++++ .../controllers/CourierAnalyticsControllerTest.java | 2 ++ src/test/resources/application-test.properties | 7 +++++++ 3 files changed, 14 insertions(+) create mode 100644 src/test/resources/application-test.properties diff --git a/pom.xml b/pom.xml index 57191c1..1496e18 100644 --- a/pom.xml +++ b/pom.xml @@ -89,6 +89,11 @@ hibernate-validator 8.0.1.Final + + com.h2database + h2 + test + diff --git a/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java b/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java index 7bfa23f..7e7675f 100644 --- a/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java +++ b/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java @@ -13,6 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.transaction.annotation.Transactional; @@ -33,6 +34,7 @@ @SpringBootTest @AutoConfigureMockMvc @Transactional +@ActiveProfiles("test") public class CourierAnalyticsControllerTest { @Autowired diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties new file mode 100644 index 0000000..454c37d --- /dev/null +++ b/src/test/resources/application-test.properties @@ -0,0 +1,7 @@ +# H2 Database Configuration for Tests +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect \ No newline at end of file From e2978ea98b1e187a1b3ddce7aed0726ac696a6ed Mon Sep 17 00:00:00 2001 From: Mohamed Date: Thu, 15 May 2025 19:19:03 +0300 Subject: [PATCH 3/3] chore: merge latest changes from dev branch --- .../CourierAnalyticsControllerTest.java | 16 +- .../FulfillmentReportControllerTest.java | 653 +++++++++--------- .../services/CourierAnalyticsServiceTest.java | 2 +- 3 files changed, 337 insertions(+), 334 deletions(-) diff --git a/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java b/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java index 7e7675f..f383c56 100644 --- a/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java +++ b/src/test/java/com/Podzilla/analytics/controllers/CourierAnalyticsControllerTest.java @@ -165,7 +165,7 @@ void getCourierDeliveryCounts_shouldReturnCountsForSpecificDateRange() throws Ex LocalDate startDate = LocalDate.now().minusDays(4); LocalDate endDate = LocalDate.now().minusDays(2); - mockMvc.perform(get("/couriers/delivery-counts") + mockMvc.perform(get("/courier-analytics/delivery-counts") .param("startDate", startDate.format( ISO_LOCAL_DATE)) .param("endDate", endDate.format( @@ -183,7 +183,7 @@ void getCourierDeliveryCounts_shouldReturnZeroCountsWhenNoDeliveriesInDateRange( LocalDate startDate = LocalDate.now().plusDays(1); LocalDate endDate = LocalDate.now().plusDays(2); - mockMvc.perform(get("/couriers/delivery-counts") + mockMvc.perform(get("/courier-analytics/delivery-counts") .param("startDate", startDate.format(ISO_LOCAL_DATE)) .param("endDate", endDate.format(ISO_LOCAL_DATE))) .andDo(MockMvcResultHandlers.print()) @@ -201,7 +201,7 @@ void getCourierDeliveryCounts_shouldHandleInvalidDateRange_startDateAfterEndDate LocalDateTime startDate = LocalDateTime.now().minusDays(1); LocalDateTime endDate = LocalDateTime.now().minusDays(3); - mockMvc.perform(get("/couriers/delivery-counts") + mockMvc.perform(get("/courier-analytics/delivery-counts") .param("startDate", startDate.format( ISO_LOCAL_DATE)) .param("endDate", endDate.format( @@ -216,7 +216,7 @@ void getCourierSuccessRate_shouldReturnSuccessRatesForSpecificDateRange() LocalDateTime startDate = LocalDateTime.now().minusDays(4); LocalDateTime endDate = LocalDateTime.now().minusDays(2); - mockMvc.perform(get("/couriers/success-rate") + mockMvc.perform(get("/courier-analytics/success-rate") .param("startDate", startDate.format( ISO_LOCAL_DATE)) .param("endDate", endDate.format( @@ -235,7 +235,7 @@ void getCourierAverageRating_shouldReturnAllAverageRatingsWhenNoDateRangeProvide throws Exception { LocalDateTime startDate = LocalDateTime.now().minusDays(4); LocalDateTime endDate = LocalDateTime.now(); - mockMvc.perform(get("/couriers/average-rating") + mockMvc.perform(get("/courier-analytics/average-rating") .param("startDate", startDate.format( ISO_LOCAL_DATE)) .param("endDate", endDate.format( @@ -256,7 +256,7 @@ void getCourierAverageRating_shouldReturnAverageRatingsForSpecificDateRange() LocalDateTime startDate = LocalDateTime.now().minusDays(4); LocalDateTime endDate = LocalDateTime.now().minusDays(2); - mockMvc.perform(get("/couriers/average-rating") + mockMvc.perform(get("/courier-analytics/average-rating") .param("startDate", startDate.format(ISO_LOCAL_DATE)) .param("endDate", endDate.format(ISO_LOCAL_DATE))) .andDo(MockMvcResultHandlers.print()) @@ -273,7 +273,7 @@ void getCourierPerformanceReport_shouldReturnAllPerformanceReportsWhenNoDateRang throws Exception { LocalDateTime startDate = LocalDateTime.now().minusDays(4); LocalDateTime endDate = LocalDateTime.now(); - mockMvc.perform(get("/couriers/performance-report") + mockMvc.perform(get("/courier-analytics/performance-report") .param("startDate", startDate.format( ISO_LOCAL_DATE)) .param("endDate", endDate.format( @@ -300,7 +300,7 @@ void getCourierPerformanceReport_shouldReturnPerformanceReportsForSpecificDateRa LocalDateTime startDate = LocalDateTime.now().minusDays(4); LocalDateTime endDate = LocalDateTime.now().minusDays(2); - mockMvc.perform(get("/couriers/performance-report") + mockMvc.perform(get("/courier-analytics/performance-report") .param("startDate", startDate.format(ISO_LOCAL_DATE)) .param("endDate", endDate.format(ISO_LOCAL_DATE))) .andDo(MockMvcResultHandlers.print()) diff --git a/src/test/java/com/Podzilla/analytics/controllers/FulfillmentReportControllerTest.java b/src/test/java/com/Podzilla/analytics/controllers/FulfillmentReportControllerTest.java index 6fc8ef2..61e3254 100644 --- a/src/test/java/com/Podzilla/analytics/controllers/FulfillmentReportControllerTest.java +++ b/src/test/java/com/Podzilla/analytics/controllers/FulfillmentReportControllerTest.java @@ -1,22 +1,22 @@ package com.Podzilla.analytics.controllers; -// import static org.junit.jupiter.api.Assertions.assertEquals; -// import static org.junit.jupiter.api.Assertions.assertNotNull; -// import static org.junit.jupiter.api.Assertions.assertTrue; -// import static org.mockito.ArgumentMatchers.any; -// import static org.mockito.Mockito.mock; -// import static org.mockito.Mockito.when; - -// import java.math.BigDecimal; -// import java.time.LocalDate; -// import java.util.Arrays; -// import java.util.Collections; -// import java.util.List; - -// import org.junit.jupiter.api.BeforeEach; -// import org.junit.jupiter.api.Test; -// import org.springframework.http.HttpStatus; -// import org.springframework.http.ResponseEntity; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import com.Podzilla.analytics.api.controllers.FulfillmentReportController; import com.Podzilla.analytics.api.dtos.fulfillment.FulfillmentPlaceToShipRequest; @@ -26,311 +26,314 @@ import com.Podzilla.analytics.api.dtos.fulfillment.FulfillmentPlaceToShipRequest.PlaceToShipGroupBy; import com.Podzilla.analytics.services.FulfillmentAnalyticsService; -// public class FulfillmentReportControllerTest { - -// private FulfillmentReportController controller; -// private FulfillmentAnalyticsService mockService; - -// private LocalDate startDate; -// private LocalDate endDate; -// private List overallTimeResponses; -// private List regionTimeResponses; -// private List courierTimeResponses; - -// @BeforeEach -// public void setup() { -// mockService = mock(FulfillmentAnalyticsService.class); -// controller = new FulfillmentReportController(mockService); - -// startDate = LocalDate.of(2024, 1, 1); -// endDate = LocalDate.of(2024, 1, 31); - -// // Setup test data -// overallTimeResponses = Arrays.asList( -// FulfillmentTimeResponse.builder() -// .groupByValue("OVERALL") -// .averageDuration(BigDecimal.valueOf(24.5)) -// .build()); - -// regionTimeResponses = Arrays.asList( -// FulfillmentTimeResponse.builder() -// .groupByValue("RegionID_1") -// .averageDuration(BigDecimal.valueOf(20.2)) -// .build(), -// FulfillmentTimeResponse.builder() -// .groupByValue("RegionID_2") -// .averageDuration(BigDecimal.valueOf(28.7)) -// .build()); - -// courierTimeResponses = Arrays.asList( -// FulfillmentTimeResponse.builder() -// .groupByValue("CourierID_1") -// .averageDuration(BigDecimal.valueOf(18.3)) -// .build(), -// FulfillmentTimeResponse.builder() -// .groupByValue("CourierID_2") -// .averageDuration(BigDecimal.valueOf(22.1)) -// .build()); -// } - -// @Test -// public void testGetPlaceToShipTime_Overall() { -// // Configure mock service -// when(mockService.getPlaceToShipTimeResponse( -// startDate, endDate, PlaceToShipGroupBy.OVERALL)) -// .thenReturn(overallTimeResponses); - -// // Create request -// FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( -// startDate, endDate, PlaceToShipGroupBy.OVERALL); - -// // Execute the method -// ResponseEntity> response = controller.getPlaceToShipTime(request); - -// // Verify response -// assertEquals(HttpStatus.OK, response.getStatusCode()); -// assertEquals(overallTimeResponses, response.getBody()); -// assertEquals(PlaceToShipGroupBy.OVERALL.toString(), response.getBody().get(0).getGroupByValue().toString()); -// assertEquals(BigDecimal.valueOf(24.5), response.getBody().get(0).getAverageDuration()); -// } - -// @Test -// public void testGetPlaceToShipTime_ByRegion() { -// // Configure mock service -// when(mockService.getPlaceToShipTimeResponse( -// startDate, endDate, PlaceToShipGroupBy.REGION)) -// .thenReturn(regionTimeResponses); - -// // Create request -// FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( -// startDate, endDate, PlaceToShipGroupBy.REGION); - -// // Execute the method -// ResponseEntity> response = controller.getPlaceToShipTime(request); - -// // Verify response -// assertEquals(HttpStatus.OK, response.getStatusCode()); -// assertEquals(regionTimeResponses, response.getBody()); -// assertEquals("RegionID_1", response.getBody().get(0).getGroupByValue()); -// assertEquals("RegionID_2", response.getBody().get(1).getGroupByValue()); -// } - -// @Test -// public void testGetShipToDeliverTime_Overall() { -// // Configure mock service -// when(mockService.getShipToDeliverTimeResponse( -// startDate, endDate, ShipToDeliverGroupBy.OVERALL)) -// .thenReturn(overallTimeResponses); - -// // Create request -// FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( -// startDate, endDate, ShipToDeliverGroupBy.OVERALL); - -// // Execute the method -// ResponseEntity> response = controller.getShipToDeliverTime(request); - -// // Verify response -// assertEquals(HttpStatus.OK, response.getStatusCode()); -// assertEquals(overallTimeResponses, response.getBody()); -// assertEquals(ShipToDeliverGroupBy.OVERALL.toString(), response.getBody().get(0).getGroupByValue().toString()); -// } - -// @Test -// public void testGetShipToDeliverTime_ByRegion() { -// // Configure mock service -// when(mockService.getShipToDeliverTimeResponse( -// startDate, endDate, ShipToDeliverGroupBy.REGION)) -// .thenReturn(regionTimeResponses); - -// // Create request -// FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( -// startDate, endDate, ShipToDeliverGroupBy.REGION); - -// // Execute the method -// ResponseEntity> response = controller.getShipToDeliverTime(request); - -// // Verify response -// assertEquals(HttpStatus.OK, response.getStatusCode()); -// assertEquals(regionTimeResponses, response.getBody()); -// assertEquals("RegionID_1", response.getBody().get(0).getGroupByValue()); -// assertEquals("RegionID_2", response.getBody().get(1).getGroupByValue()); -// } - -// @Test -// public void testGetShipToDeliverTime_ByCourier() { -// // Configure mock service -// when(mockService.getShipToDeliverTimeResponse( -// startDate, endDate, ShipToDeliverGroupBy.COURIER)) -// .thenReturn(courierTimeResponses); - -// // Create request -// FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( -// startDate, endDate, ShipToDeliverGroupBy.COURIER); - -// // Execute the method -// ResponseEntity> response = controller.getShipToDeliverTime(request); - -// // Verify response -// assertEquals(HttpStatus.OK, response.getStatusCode()); -// assertEquals(courierTimeResponses, response.getBody()); -// assertEquals("CourierID_1", response.getBody().get(0).getGroupByValue()); -// assertEquals("CourierID_2", response.getBody().get(1).getGroupByValue()); -// } - -// // Edge case tests - -// @Test -// public void testGetPlaceToShipTime_EmptyResponse() { -// // Configure mock service to return empty list -// when(mockService.getPlaceToShipTimeResponse( -// startDate, endDate, PlaceToShipGroupBy.OVERALL)) -// .thenReturn(Collections.emptyList()); - -// // Create request -// FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( -// startDate, endDate, PlaceToShipGroupBy.OVERALL); - -// // Execute the method -// ResponseEntity> response = controller.getPlaceToShipTime(request); - -// // Verify response -// assertEquals(HttpStatus.OK, response.getStatusCode()); -// assertNotNull(response.getBody()); -// assertTrue(response.getBody().isEmpty()); -// } - -// @Test -// public void testGetShipToDeliverTime_EmptyResponse() { -// // Configure mock service to return empty list -// when(mockService.getShipToDeliverTimeResponse( -// startDate, endDate, ShipToDeliverGroupBy.OVERALL)) -// .thenReturn(Collections.emptyList()); - -// // Create request -// FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( -// startDate, endDate, ShipToDeliverGroupBy.OVERALL); - -// // Execute the method -// ResponseEntity> response = controller.getShipToDeliverTime(request); - -// // Verify response -// assertEquals(HttpStatus.OK, response.getStatusCode()); -// assertNotNull(response.getBody()); -// assertTrue(response.getBody().isEmpty()); -// } - -// // @Test -// // public void testGetPlaceToShipTime_InvalidGroupBy() { -// // // Create request with invalid groupBy -// // FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( -// // startDate, endDate, null); - -// // // Execute the method - should return bad request due to validation error -// // ResponseEntity> response = controller.getPlaceToShipTime(request); - -// // // Verify response -// // assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); -// // } - -// // @Test -// // public void testGetShipToDeliverTime_InvalidGroupBy() { -// // // Create request with invalid groupBy -// // FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( -// // startDate, endDate, null); - -// // // Execute the method - should return bad request due to validation error -// // ResponseEntity> response = controller.getShipToDeliverTime(request); - -// // // Verify response -// // assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); -// // } - -// @Test -// public void testGetPlaceToShipTime_SameDayRange() { -// // Test same start and end date -// LocalDate sameDate = LocalDate.of(2024, 1, 1); - -// // Configure mock service -// when(mockService.getPlaceToShipTimeResponse( -// sameDate, sameDate, PlaceToShipGroupBy.OVERALL)) -// .thenReturn(overallTimeResponses); - -// // Create request with same start and end date -// FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( -// sameDate, sameDate, PlaceToShipGroupBy.OVERALL); - -// // Execute the method -// ResponseEntity> response = controller.getPlaceToShipTime(request); - -// // Verify response -// assertEquals(HttpStatus.OK, response.getStatusCode()); -// assertEquals(overallTimeResponses, response.getBody()); -// } - -// @Test -// public void testGetShipToDeliverTime_SameDayRange() { -// // Test same start and end date -// LocalDate sameDate = LocalDate.of(2024, 1, 1); - -// // Configure mock service -// when(mockService.getShipToDeliverTimeResponse( -// sameDate, sameDate, ShipToDeliverGroupBy.OVERALL)) -// .thenReturn(overallTimeResponses); - -// // Create request with same start and end date -// FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( -// sameDate, sameDate, ShipToDeliverGroupBy.OVERALL); - -// // Execute the method -// ResponseEntity> response = controller.getShipToDeliverTime(request); - -// // Verify response -// assertEquals(HttpStatus.OK, response.getStatusCode()); -// assertEquals(overallTimeResponses, response.getBody()); -// } - -// @Test -// public void testGetPlaceToShipTime_FutureDates() { -// // Test future dates -// LocalDate futureStart = LocalDate.now().plusDays(1); -// LocalDate futureEnd = LocalDate.now().plusDays(30); - -// // Configure mock service - should return empty for future dates -// when(mockService.getPlaceToShipTimeResponse( -// futureStart, futureEnd, PlaceToShipGroupBy.OVERALL)) -// .thenReturn(Collections.emptyList()); - -// // Create request with future dates -// FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( -// futureStart, futureEnd, PlaceToShipGroupBy.OVERALL); - -// // Execute the method -// ResponseEntity> response = controller.getPlaceToShipTime(request); - -// // Verify response -// assertEquals(HttpStatus.OK, response.getStatusCode()); -// assertNotNull(response.getBody()); -// assertTrue(response.getBody().isEmpty()); -// } - -// @Test -// public void testGetShipToDeliverTime_ServiceException() { -// // Configure mock service to throw exception -// when(mockService.getShipToDeliverTimeResponse( -// any(), any(), any())) -// .thenThrow(new RuntimeException("Service error")); - -// // Create request -// FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( -// startDate, endDate, ShipToDeliverGroupBy.OVERALL); - -// // Execute the method - controller should handle exception -// // Note: Actual behavior depends on how controller handles exceptions -// // This might need adjustment based on actual implementation -// try { -// controller.getShipToDeliverTime(request); -// } catch (RuntimeException e) { -// assertEquals("Service error", e.getMessage()); -// } -// } -// } \ No newline at end of file +public class FulfillmentReportControllerTest { + + private FulfillmentReportController controller; + private FulfillmentAnalyticsService mockService; + + private LocalDate startDate; + private LocalDate endDate; + private List overallTimeResponses; + private List regionTimeResponses; + private List courierTimeResponses; + + @BeforeEach + public void setup() { + mockService = mock(FulfillmentAnalyticsService.class); + controller = new FulfillmentReportController(mockService); + + startDate = LocalDate.of(2024, 1, 1); + endDate = LocalDate.of(2024, 1, 31); + + // Setup test data + overallTimeResponses = Arrays.asList( + FulfillmentTimeResponse.builder() + .groupByValue("OVERALL") + .averageDuration(BigDecimal.valueOf(24.5)) + .build()); + + regionTimeResponses = Arrays.asList( + FulfillmentTimeResponse.builder() + .groupByValue("RegionID_1") + .averageDuration(BigDecimal.valueOf(20.2)) + .build(), + FulfillmentTimeResponse.builder() + .groupByValue("RegionID_2") + .averageDuration(BigDecimal.valueOf(28.7)) + .build()); + + courierTimeResponses = Arrays.asList( + FulfillmentTimeResponse.builder() + .groupByValue("CourierID_1") + .averageDuration(BigDecimal.valueOf(18.3)) + .build(), + FulfillmentTimeResponse.builder() + .groupByValue("CourierID_2") + .averageDuration(BigDecimal.valueOf(22.1)) + .build()); + } + + @Test + public void testGetPlaceToShipTime_Overall() { + // Configure mock service + when(mockService.getPlaceToShipTimeResponse( + startDate, endDate, PlaceToShipGroupBy.OVERALL)) + .thenReturn(overallTimeResponses); + + // Create request + FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( + startDate, endDate, PlaceToShipGroupBy.OVERALL); + + // Execute the method + ResponseEntity> response = controller.getPlaceToShipTime(request); + + // Verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(overallTimeResponses, response.getBody()); + assertEquals(PlaceToShipGroupBy.OVERALL.toString(), response.getBody().get(0).getGroupByValue().toString()); + assertEquals(BigDecimal.valueOf(24.5), response.getBody().get(0).getAverageDuration()); + } + + @Test + public void testGetPlaceToShipTime_ByRegion() { + // Configure mock service + when(mockService.getPlaceToShipTimeResponse( + startDate, endDate, PlaceToShipGroupBy.REGION)) + .thenReturn(regionTimeResponses); + + // Create request + FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( + startDate, endDate, PlaceToShipGroupBy.REGION); + + // Execute the method + ResponseEntity> response = controller.getPlaceToShipTime(request); + + // Verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(regionTimeResponses, response.getBody()); + assertEquals("RegionID_1", response.getBody().get(0).getGroupByValue()); + assertEquals("RegionID_2", response.getBody().get(1).getGroupByValue()); + } + + @Test + public void testGetShipToDeliverTime_Overall() { + // Configure mock service + when(mockService.getShipToDeliverTimeResponse( + startDate, endDate, ShipToDeliverGroupBy.OVERALL)) + .thenReturn(overallTimeResponses); + + // Create request + FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( + startDate, endDate, ShipToDeliverGroupBy.OVERALL); + + // Execute the method + ResponseEntity> response = controller.getShipToDeliverTime(request); + + // Verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(overallTimeResponses, response.getBody()); + assertEquals(ShipToDeliverGroupBy.OVERALL.toString(), response.getBody().get(0).getGroupByValue().toString()); + } + + @Test + public void testGetShipToDeliverTime_ByRegion() { + // Configure mock service + when(mockService.getShipToDeliverTimeResponse( + startDate, endDate, ShipToDeliverGroupBy.REGION)) + .thenReturn(regionTimeResponses); + + // Create request + FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( + startDate, endDate, ShipToDeliverGroupBy.REGION); + + // Execute the method + ResponseEntity> response = controller.getShipToDeliverTime(request); + + // Verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(regionTimeResponses, response.getBody()); + assertEquals("RegionID_1", response.getBody().get(0).getGroupByValue()); + assertEquals("RegionID_2", response.getBody().get(1).getGroupByValue()); + } + + @Test + public void testGetShipToDeliverTime_ByCourier() { + // Configure mock service + when(mockService.getShipToDeliverTimeResponse( + startDate, endDate, ShipToDeliverGroupBy.COURIER)) + .thenReturn(courierTimeResponses); + + // Create request + FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( + startDate, endDate, ShipToDeliverGroupBy.COURIER); + + // Execute the method + ResponseEntity> response = controller.getShipToDeliverTime(request); + + // Verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(courierTimeResponses, response.getBody()); + assertEquals("CourierID_1", response.getBody().get(0).getGroupByValue()); + assertEquals("CourierID_2", response.getBody().get(1).getGroupByValue()); + } + + // Edge case tests + + @Test + public void testGetPlaceToShipTime_EmptyResponse() { + // Configure mock service to return empty list + when(mockService.getPlaceToShipTimeResponse( + startDate, endDate, PlaceToShipGroupBy.OVERALL)) + .thenReturn(Collections.emptyList()); + + // Create request + FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( + startDate, endDate, PlaceToShipGroupBy.OVERALL); + + // Execute the method + ResponseEntity> response = controller.getPlaceToShipTime(request); + + // Verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + assertTrue(response.getBody().isEmpty()); + } + + @Test + public void testGetShipToDeliverTime_EmptyResponse() { + // Configure mock service to return empty list + when(mockService.getShipToDeliverTimeResponse( + startDate, endDate, ShipToDeliverGroupBy.OVERALL)) + .thenReturn(Collections.emptyList()); + + // Create request + FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( + startDate, endDate, ShipToDeliverGroupBy.OVERALL); + + // Execute the method + ResponseEntity> response = controller.getShipToDeliverTime(request); + + // Verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + assertTrue(response.getBody().isEmpty()); + } + + // @Test + // public void testGetPlaceToShipTime_InvalidGroupBy() { + // // Create request with invalid groupBy + // FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( + // startDate, endDate, null); + + // // Execute the method - should return bad request due to validation error + // ResponseEntity> response = + // controller.getPlaceToShipTime(request); + + // // Verify response + // assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + // } + + // @Test + // public void testGetShipToDeliverTime_InvalidGroupBy() { + // // Create request with invalid groupBy + // FulfillmentShipToDeliverRequest request = new + // FulfillmentShipToDeliverRequest( + // startDate, endDate, null); + + // // Execute the method - should return bad request due to validation error + // ResponseEntity> response = + // controller.getShipToDeliverTime(request); + + // // Verify response + // assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + // } + + @Test + public void testGetPlaceToShipTime_SameDayRange() { + // Test same start and end date + LocalDate sameDate = LocalDate.of(2024, 1, 1); + + // Configure mock service + when(mockService.getPlaceToShipTimeResponse( + sameDate, sameDate, PlaceToShipGroupBy.OVERALL)) + .thenReturn(overallTimeResponses); + + // Create request with same start and end date + FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( + sameDate, sameDate, PlaceToShipGroupBy.OVERALL); + + // Execute the method + ResponseEntity> response = controller.getPlaceToShipTime(request); + + // Verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(overallTimeResponses, response.getBody()); + } + + @Test + public void testGetShipToDeliverTime_SameDayRange() { + // Test same start and end date + LocalDate sameDate = LocalDate.of(2024, 1, 1); + + // Configure mock service + when(mockService.getShipToDeliverTimeResponse( + sameDate, sameDate, ShipToDeliverGroupBy.OVERALL)) + .thenReturn(overallTimeResponses); + + // Create request with same start and end date + FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( + sameDate, sameDate, ShipToDeliverGroupBy.OVERALL); + + // Execute the method + ResponseEntity> response = controller.getShipToDeliverTime(request); + + // Verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(overallTimeResponses, response.getBody()); + } + + @Test + public void testGetPlaceToShipTime_FutureDates() { + // Test future dates + LocalDate futureStart = LocalDate.now().plusDays(1); + LocalDate futureEnd = LocalDate.now().plusDays(30); + + // Configure mock service - should return empty for future dates + when(mockService.getPlaceToShipTimeResponse( + futureStart, futureEnd, PlaceToShipGroupBy.OVERALL)) + .thenReturn(Collections.emptyList()); + + // Create request with future dates + FulfillmentPlaceToShipRequest request = new FulfillmentPlaceToShipRequest( + futureStart, futureEnd, PlaceToShipGroupBy.OVERALL); + + // Execute the method + ResponseEntity> response = controller.getPlaceToShipTime(request); + + // Verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + assertTrue(response.getBody().isEmpty()); + } + + @Test + public void testGetShipToDeliverTime_ServiceException() { + // Configure mock service to throw exception + when(mockService.getShipToDeliverTimeResponse( + any(), any(), any())) + .thenThrow(new RuntimeException("Service error")); + + // Create request + FulfillmentShipToDeliverRequest request = new FulfillmentShipToDeliverRequest( + startDate, endDate, ShipToDeliverGroupBy.OVERALL); + + // Execute the method - controller should handle exception + // Note: Actual behavior depends on how controller handles exceptions + // This might need adjustment based on actual implementation + try { + controller.getShipToDeliverTime(request); + } catch (RuntimeException e) { + assertEquals("Service error", e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/Podzilla/analytics/services/CourierAnalyticsServiceTest.java b/src/test/java/com/Podzilla/analytics/services/CourierAnalyticsServiceTest.java index 0faa003..1cb8c82 100644 --- a/src/test/java/com/Podzilla/analytics/services/CourierAnalyticsServiceTest.java +++ b/src/test/java/com/Podzilla/analytics/services/CourierAnalyticsServiceTest.java @@ -4,7 +4,7 @@ import com.Podzilla.analytics.api.dtos.courier.CourierDeliveryCountResponse; import com.Podzilla.analytics.api.dtos.courier.CourierPerformanceReportResponse; import com.Podzilla.analytics.api.dtos.courier.CourierSuccessRateResponse; -import com.Podzilla.analytics.api.projections.CourierPerformanceProjection; +import com.Podzilla.analytics.api.projections.courier.CourierPerformanceProjection; import com.Podzilla.analytics.repositories.CourierRepository; import org.junit.jupiter.api.BeforeEach;