diff --git a/data-api/open-api-specification.yml b/data-api/open-api-specification.yml index 4f79cc4..f55f45a 100644 --- a/data-api/open-api-specification.yml +++ b/data-api/open-api-specification.yml @@ -64,6 +64,36 @@ paths: description: 'Not found' '500': description: 'Internal server error' + /users/{login-id}/notifications/summary: + get: + tags: + - notifications + summary: 'Get User Notification Summary' + operationId: 'getUserNotificationSummary' + parameters: + - name: 'login-id' + in: 'path' + required: true + schema: + type: 'string' + example: 'abc123' + responses: + '200': + description: 'Successful operation' + content: + application/json: + schema: + $ref: "#/components/schemas/notificationSummary" + '400': + description: 'Bad request' + '401': + description: 'Unauthorized' + '403': + description: 'Forbidden' + '404': + description: 'Not found' + '500': + description: 'Internal server error' /providers/{provider-id}: get: tags: @@ -795,7 +825,7 @@ paths: - name: 'type' in: 'query' schema: - type: 'String' + type: 'string' responses: '200': description: 'Successful operation' @@ -1044,6 +1074,15 @@ components: type: 'array' items: type: 'string' + notificationSummary: + type: 'object' + properties: + notifications: + type: 'integer' + standard_actions: + type: 'integer' + overdue_actions: + type: 'integer' contactDetail: type: 'object' properties: diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/controller/NotificationsController.java b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/NotificationsController.java new file mode 100644 index 0000000..7d34b1a --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/controller/NotificationsController.java @@ -0,0 +1,31 @@ +package uk.gov.laa.ccms.data.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RestController; +import uk.gov.laa.ccms.data.api.NotificationsApi; +import uk.gov.laa.ccms.data.model.NotificationSummary; +import uk.gov.laa.ccms.data.service.NotificationService; + +/** + * Controller class responsible for handling notification-related requests. + * + *

This controller serves as an interface to return requested user notification + * information. It delegates the business logic to the {@link NotificationService}. + * + *

This class implements the {@link NotificationsApi} interface and provides + * endpoints for retrieving notification summaries for users. + */ +@RestController +@RequiredArgsConstructor +public class NotificationsController implements NotificationsApi { + + private final NotificationService notificationService; + + @Override + public ResponseEntity getUserNotificationSummary(String loginId) { + return notificationService.getUserNotificationSummary(loginId).map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } +} + diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationCount.java b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationCount.java new file mode 100644 index 0000000..f41e102 --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationCount.java @@ -0,0 +1,53 @@ +package uk.gov.laa.ccms.data.entity; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Immutable; + +/** + * Represents an immutable notification count entity corresponding to the + * "XXCCMS_NOTIFICATION_COUNT_V" database view. + * + *

The entity tracks the total number of notifications for a specific user login + * and notification type. + */ +@Entity +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "XXCCMS_NOTIFICATION_COUNT_V") +@Immutable +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class NotificationCount { + + public static final String NOTIFICATION_TYPE_NOTIFICATIONS = "Notifications"; + public static final String NOTIFICATION_TYPE_ACTIONS = "Actions"; + public static final String NOTIFICATION_TYPE_OVERDUE = "Overdue"; + + /** + * The composite key for the NotificationCount entity. + * This key is used to uniquely identify a notification count entry + * based on the user login ID and notification type. + */ + @EmbeddedId + private NotificationCountId id; + + /** + * Represents the total number of notifications associated with a specific + * user login and notification type. This field is stored in the + * "NOTIFICATION_COUNT" column of the corresponding database view. + */ + @Column(name = "NOTIFICATION_COUNT") + private Integer notificationCount; + + +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationCountId.java b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationCountId.java new file mode 100644 index 0000000..3cd1564 --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationCountId.java @@ -0,0 +1,36 @@ +package uk.gov.laa.ccms.data.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Represents a composite primary key for the NotificationCount entity in the form of a + * NotificationCountId class. This key uniquely identifies a notification count record based on a + * combination of user login ID and notification type. + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class NotificationCountId implements Serializable { + + /** + * A unique identifier representing a user's login session. This field is mapped to the + * "USER_LOGIN_ID" column in the database. + */ + @Column(name = "USER_LOGIN_ID") + private String userLoginId; + + /** + * Represents the type of notification associated with a user login session. This field is mapped + * to the "NOTIFICATION_TYPE" column in the database and uses the NotificationTypeConverter to + * translate between the NotificationType enumeration and its corresponding String representation + * for database storage. + */ + @Column(name = "NOTIFICATION_TYPE") + @Convert(converter = NotificationTypeConverter.class) + private NotificationType notificationType; +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationType.java b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationType.java new file mode 100644 index 0000000..bfa8675 --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationType.java @@ -0,0 +1,35 @@ +package uk.gov.laa.ccms.data.entity; + +import lombok.Getter; + +/** + * Defines the type of notification. This enumeration is used to categorize + * different types of notifications within the system. + * + *

Each type has a corresponding database value which is a string representation + * of the notification type stored in the database. + * + *

The available types are: + *

+ * The enum provides a mechanism to easily handle notification types + * programmatically by using the associated string value, + * allowing seamless conversion between the programmatic representation and + * the database representation. + */ +@Getter +public enum NotificationType { + NOTIFICATIONS("Notification"), + ACTION("Action"), + OVERDUE("Overdue"); + + private final String dbValue; + + NotificationType(String dbValue) { + this.dbValue = dbValue; + } +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationTypeConverter.java b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationTypeConverter.java new file mode 100644 index 0000000..ae47ae1 --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/entity/NotificationTypeConverter.java @@ -0,0 +1,62 @@ +package uk.gov.laa.ccms.data.entity; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +/** + * Converter implementation for mapping the NotificationType enumeration to a String + * representation in the database and vice versa. + * + *

This converter is automatically applied to all entity attributes of type NotificationType + * across the JPA context due to the use of the @Converter annotation with autoApply set to true. + * + *

The convertToDatabaseColumn method translates a NotificationType enumeration value to its + * corresponding String representation for storage in the database. If the provided + * NotificationType is null, the database column is set to null. + * + *

The convertToEntityAttribute method takes a String from the database and converts it back + * to its corresponding NotificationType value. If the database value is null, it returns null + * as the entity attribute. If the database String does not match any known NotificationType, + * an IllegalArgumentException is thrown. + */ +@Converter(autoApply = true) +public class NotificationTypeConverter implements AttributeConverter { + + /** + * Converts a NotificationType enumeration value to its corresponding String representation + * for storage in the database. + * + * @param notificationType the NotificationType to be converted to a database column value; + * may be null, in which case the method returns null. + * @return the String representation of the given NotificationType for database storage, + * or null if the NotificationType is null. + */ + @Override + public String convertToDatabaseColumn(NotificationType notificationType) { + return notificationType != null ? notificationType.getDbValue() : null; + } + + /** + * Converts a database column value (String) to the corresponding + * NotificationType enumeration value. + * + * @param dbData the database column value that represents a + * NotificationType; may be null. + * @return the NotificationType corresponding to the given database value, + * or null if the input is null. + * @throws IllegalArgumentException if dbData does not match any known NotificationType values. + */ + @Override + public NotificationType convertToEntityAttribute(String dbData) { + if (dbData == null) { + return null; + } + + return switch (dbData) { + case "Notification" -> NotificationType.NOTIFICATIONS; + case "Action" -> NotificationType.ACTION; + case "Overdue" -> NotificationType.OVERDUE; + default -> throw new IllegalArgumentException("Unknown database value: " + dbData); + }; + } +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/mapper/NotificationSummaryMapper.java b/data-service/src/main/java/uk/gov/laa/ccms/data/mapper/NotificationSummaryMapper.java new file mode 100644 index 0000000..678f99a --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/mapper/NotificationSummaryMapper.java @@ -0,0 +1,68 @@ +package uk.gov.laa.ccms.data.mapper; + +import static java.util.Objects.isNull; + +import jakarta.validation.constraints.NotNull; +import java.util.List; +import java.util.function.Predicate; +import org.mapstruct.Mapper; +import uk.gov.laa.ccms.data.entity.NotificationCount; +import uk.gov.laa.ccms.data.entity.NotificationType; +import uk.gov.laa.ccms.data.model.NotificationSummary; + +/** + * NotificationSummaryMapper is an interface for mapping notification count data + * to a notification summary. Utilizes Java streams to process lists of NotificationCount and + * derive a summary. This interface uses the MapStruct framework for object mapping and + * is defined as a Spring component. + * + *

Note: It computes the sum of notifications, actions, and overdue actions from a + * list of NotificationCount. + * + * @see NotificationSummary + * @see NotificationCount + * @see NotificationType + */ +@Mapper(componentModel = "spring") +public interface NotificationSummaryMapper { + + Predicate IS_NOTIFICATION_TYPE = + x -> NotificationType.NOTIFICATIONS.equals(x.getId().getNotificationType()); + Predicate IS_ACTION_TYPE = + x -> NotificationType.ACTION.equals(x.getId().getNotificationType()); + Predicate IS_OVERDUE_TYPE = + x -> NotificationType.OVERDUE.equals(x.getId().getNotificationType()); + + /** + * Converts a list of NotificationCount objects into a NotificationSummary object + * by calculating the sum of different types of notifications. + * + * @param notificationCount a list containing instances of NotificationCount, each with a + * type and count of notifications + * @return a NotificationSummary object with the total count of notifications, standard actions, + * and overdue actions derived from the provided list + */ + default NotificationSummary toNotificationSummary( + @NotNull List notificationCount) { + + int notificationSum = 0; + int actionSum = 0; + int overdueSum = 0; + if (!isNull(notificationCount)) { + // There shouldn't really be an instance where there are multiple of the same notification + // type, however if there are due to inconsistencies in the DB, use Java streams to get + // the sum. + notificationSum = notificationCount.stream() + .filter(IS_NOTIFICATION_TYPE) + .mapToInt(NotificationCount::getNotificationCount).sum(); + actionSum = notificationCount.stream() + .filter(IS_ACTION_TYPE) + .mapToInt(NotificationCount::getNotificationCount).sum(); + overdueSum = notificationCount.stream() + .filter(IS_OVERDUE_TYPE) + .mapToInt(NotificationCount::getNotificationCount).sum(); + } + return new NotificationSummary().notifications(notificationSum).standardActions(actionSum) + .overdueActions(overdueSum); + } +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/repository/NotificationCountRepository.java b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/NotificationCountRepository.java new file mode 100644 index 0000000..307273e --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/repository/NotificationCountRepository.java @@ -0,0 +1,24 @@ +package uk.gov.laa.ccms.data.repository; + +import java.util.List; +import org.springframework.stereotype.Repository; +import uk.gov.laa.ccms.data.entity.NotificationCount; +import uk.gov.laa.ccms.data.entity.NotificationCountId; + +/** + * Repository interface for accessing {@link NotificationCount} entities. + * + *

This repository extends the {@link ReadOnlyRepository} interface, it supports read-only + * operations for the {@code NotificationCount} entity, with the primary key of type + * {@code String}.

+ * + * @author Jamie Briggs + * @see NotificationCount + * @see ReadOnlyRepository + */ +@Repository +public interface NotificationCountRepository + extends ReadOnlyRepository { + + List findAllByIdUserLoginId(String userLoginId); +} diff --git a/data-service/src/main/java/uk/gov/laa/ccms/data/service/NotificationService.java b/data-service/src/main/java/uk/gov/laa/ccms/data/service/NotificationService.java new file mode 100644 index 0000000..6c3b5ee --- /dev/null +++ b/data-service/src/main/java/uk/gov/laa/ccms/data/service/NotificationService.java @@ -0,0 +1,50 @@ +package uk.gov.laa.ccms.data.service; + +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import uk.gov.laa.ccms.data.entity.NotificationCount; +import uk.gov.laa.ccms.data.mapper.NotificationSummaryMapper; +import uk.gov.laa.ccms.data.model.NotificationSummary; +import uk.gov.laa.ccms.data.repository.NotificationCountRepository; + +/** + * Service class responsible for handling notification-related operations. + * + *

This class serves to manage the retrieval of notification summaries + * for users, bridging the interactions between repositories, mappers, + * and other services required to obtain and synthesize notification data. + * + * @author Jamie Briggs + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class NotificationService { + + private final NotificationCountRepository notificationCountRepository; + private final NotificationSummaryMapper notificationSummaryMapper; + private final UserService userService; + + /** + * Retrieves the summary of notifications for a specific user. + * + * @param userId the unique identifier of the user for whom to retrieve the notification summary + * @return a NotificationSummary object representing the summary of notifications for the + * specified user + */ + @Transactional + public Optional getUserNotificationSummary(String userId) { + // Check if user exists + if (userService.getUser(userId).isPresent()) { + List allByIdUserLoginId = + notificationCountRepository.findAllByIdUserLoginId(userId); + return Optional.ofNullable( + notificationSummaryMapper.toNotificationSummary(allByIdUserLoginId)); + } + return Optional.empty(); + } +} diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/controller/NotificationsControllerTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/NotificationsControllerTest.java new file mode 100644 index 0000000..1cd349d --- /dev/null +++ b/data-service/src/test/java/uk/gov/laa/ccms/data/controller/NotificationsControllerTest.java @@ -0,0 +1,77 @@ +package uk.gov.laa.ccms.data.controller; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +import java.util.Optional; +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.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.context.WebApplicationContext; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; +import uk.gov.laa.ccms.data.model.NotificationSummary; +import uk.gov.laa.ccms.data.service.NotificationService; + +@ExtendWith({SpringExtension.class}) +@ContextConfiguration +@WebAppConfiguration +class NotificationsControllerTest { + + @Mock + private NotificationService notificationService; + + @InjectMocks + private NotificationsController notificationsController; + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext webApplicationContext; + + private ObjectMapper objectMapper; + + @BeforeEach + public void setup() { + mockMvc = standaloneSetup(notificationsController).build(); + objectMapper = new ObjectMapper(); + } + + @Test + @DisplayName("getUserNotificationSummary: Returns data") + void getUserNotificationSummary_returnsData() throws Exception { + //Given + String loginId = "123"; + NotificationSummary expected = new NotificationSummary().notifications(1).standardActions(1) + .overdueActions(1); + when(notificationService.getUserNotificationSummary(loginId)).thenReturn(Optional.of(expected)); + // Then + String jsonContent = objectMapper.writeValueAsString(expected); + this.mockMvc.perform(get("/users/{loginId}/notifications/summary", loginId)) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().json(jsonContent)); + } + + @Test + @DisplayName("getUserNotificationSummary: Not found") + void getUserNotificationSummary_notFound() throws Exception { + // Given + String loginId = "123"; + // Then + this.mockMvc.perform(get("/users/{loginId}/notifications/summary", loginId)) + .andDo(print()) + .andExpect(status().isNotFound()); + } +} \ No newline at end of file diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/entity/NotificationTypeConverterTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/entity/NotificationTypeConverterTest.java new file mode 100644 index 0000000..6b911cd --- /dev/null +++ b/data-service/src/test/java/uk/gov/laa/ccms/data/entity/NotificationTypeConverterTest.java @@ -0,0 +1,50 @@ +package uk.gov.laa.ccms.data.entity; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import uk.gov.laa.ccms.data.entity.NotificationType; + +public class NotificationTypeConverterTest { + + NotificationTypeConverter converter = new NotificationTypeConverter(); + + @Test + @DisplayName("Should convert Notification DB Value to Entity value") + public void shouldConvertNotificationDbValuetoEntityValue() { + assertEquals(NotificationType.NOTIFICATIONS, converter.convertToEntityAttribute("Notification")); + } + + @Test + @DisplayName("Should convert Action DB Value to Entity value") + public void shouldConvertActionDbValuetoEntityValue() { + assertEquals(NotificationType.ACTION, converter.convertToEntityAttribute("Action")); + } + + @Test + @DisplayName("Should convert Overdue DB Value to Entity value") + public void shouldConvertOverdueDbValuetoEntityValue() { + assertEquals(NotificationType.OVERDUE, converter.convertToEntityAttribute("Overdue")); + } + + @Test + @DisplayName("Should convert Notification Entity value to DB value") + public void shouldConvertNotificationEntityValuetoDBValue() { + assertEquals("Notification", converter.convertToDatabaseColumn(NotificationType.NOTIFICATIONS)); + } + + @Test + @DisplayName("Should convert Action Entity value to DB value") + public void shouldConvertActionEntityValuetoDBValue() { + assertEquals("Action", converter.convertToDatabaseColumn(NotificationType.ACTION)); + } + + @Test + @DisplayName("Should convert Overdue Entity value to DB value") + public void shouldConvertOverdueEntityValuetoDBValue() { + assertEquals("Overdue", converter.convertToDatabaseColumn(NotificationType.OVERDUE)); + } + +} diff --git a/data-service/src/test/java/uk/gov/laa/ccms/data/mapper/NotificationSummaryMapperImplTest.java b/data-service/src/test/java/uk/gov/laa/ccms/data/mapper/NotificationSummaryMapperImplTest.java new file mode 100644 index 0000000..efd9986 --- /dev/null +++ b/data-service/src/test/java/uk/gov/laa/ccms/data/mapper/NotificationSummaryMapperImplTest.java @@ -0,0 +1,178 @@ +package uk.gov.laa.ccms.data.mapper; + +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.gov.laa.ccms.data.entity.NotificationCount; +import uk.gov.laa.ccms.data.entity.NotificationCountId; +import uk.gov.laa.ccms.data.entity.NotificationType; +import uk.gov.laa.ccms.data.model.NotificationSummary; + +@ExtendWith(MockitoExtension.class) +class NotificationSummaryMapperImplTest { + + NotificationSummaryMapperImpl mapper = new NotificationSummaryMapperImpl(); + + @Test + @DisplayName("Should return notification summary with zeros when empty list passed") + void shouldReturnNotificationSummaryWithZerosWhenEmptyListPassed() { + // Given + List counts = emptyList(); + // When + NotificationSummary result = mapper.toNotificationSummary(counts); + // Then + assertEquals(new NotificationSummary().notifications(0).standardActions(0).overdueActions(0), + result, "Should only contain zeros"); + } + + @Test + @DisplayName("Should return notification summary with zeros when null passed") + void shouldReturnNotificationSummaryWithZerosWhenNullPassed() { + // Given + // When + NotificationSummary result = mapper.toNotificationSummary(null); + // Then + assertEquals(new NotificationSummary().notifications(0).standardActions(0).overdueActions(0), + result, "Should only contain zeros"); + } + + @Test + @DisplayName("Should return notification summary with list of zeros passed") + void shouldReturnNotificationSummaryWithListOfZerosPassed() { + // Given + List counts = Arrays.asList( + NotificationCount.builder().id(withNotificationCountId(NotificationType.NOTIFICATIONS)).notificationCount(0).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.ACTION)).notificationCount(0).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.OVERDUE)).notificationCount(0).build() + ); + // When + NotificationSummary result = mapper.toNotificationSummary(counts); + // Then + assertEquals(new NotificationSummary().notifications(0).standardActions(0).overdueActions(0), + result, "Should only contain zeros"); + } + + @Test + @DisplayName("Should return notification summary with 3 notifications") + void shouldReturnNotificationSummaryWith3Notifications() { + // Given + List counts = Arrays.asList( + NotificationCount.builder().id(withNotificationCountId(NotificationType.NOTIFICATIONS)).notificationCount(3).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.ACTION)).notificationCount(0).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.OVERDUE)).notificationCount(0).build() + ); + // When + NotificationSummary result = mapper.toNotificationSummary(counts); + // Then + assertEquals(new NotificationSummary().notifications(3).standardActions(0).overdueActions(0), + result, "Notification summary should only have 3 notifications"); + } + + @Test + @DisplayName("Should return notification summary with 5 notifications due to multiple NotificationCount objects") + void shouldReturnNotificationSummaryWith5NotificationsDueToMultipleNotificationCountObjects() { + // Given + List counts = Arrays.asList( + NotificationCount.builder().id(withNotificationCountId(NotificationType.NOTIFICATIONS)).notificationCount(2).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.NOTIFICATIONS)).notificationCount(3).build() + ); + // When + NotificationSummary result = mapper.toNotificationSummary(counts); + // Then + assertEquals(new NotificationSummary().notifications(5).standardActions(0).overdueActions(0), + result, "Notification summary should only have 5 notifications"); + } + + @Test + @DisplayName("Should return notification summary with 5 actions") + void shouldReturnNotificationSummaryWith5Actions() { + // Given + List counts = Arrays.asList( + NotificationCount.builder().id(withNotificationCountId(NotificationType.NOTIFICATIONS)).notificationCount(0).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.ACTION)).notificationCount(5).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.OVERDUE)).notificationCount(0).build() + ); + // When + NotificationSummary result = mapper.toNotificationSummary(counts); + // Then + assertEquals(new NotificationSummary().notifications(0).standardActions(5).overdueActions(0), + result, "Notification summary should only have 5 actions"); + } + + @Test + @DisplayName("Should return notification summary with 6 actions due to multiple NotificationCount objects") + void shouldReturnNotificationSummaryWith6ActionsDueToMultipleNotificationCountObjects() { + // Given + List counts = Arrays.asList( + NotificationCount.builder().id(withNotificationCountId(NotificationType.ACTION)).notificationCount(2).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.ACTION)).notificationCount(4).build() + ); + // When + NotificationSummary result = mapper.toNotificationSummary(counts); + // Then + assertEquals(new NotificationSummary().notifications(0).standardActions(6).overdueActions(0), + result, "Notification summary should only have 6 actions"); + } + + + @Test + @DisplayName("Should return notification summary with 7 overdue") + void shouldReturnNotificationSummaryWith7Overdue() { + // Given + List counts = Arrays.asList( + NotificationCount.builder().id(withNotificationCountId(NotificationType.NOTIFICATIONS)).notificationCount(0).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.ACTION)).notificationCount(0).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.OVERDUE)).notificationCount(7).build() + ); + // When + NotificationSummary result = mapper.toNotificationSummary(counts); + // Then + assertEquals(new NotificationSummary().notifications(0).standardActions(0).overdueActions(7), + result, "Notification summary should only have 7 actions"); + } + + @Test + @DisplayName("Should return notification summary with 9 overdue due to multiple NotificationCount objects") + void shouldReturnNotificationSummaryWith9OverdueDueToMultipleNotificationCountObjects() { + // Given + List counts = Arrays.asList( + NotificationCount.builder().id(withNotificationCountId(NotificationType.OVERDUE)).notificationCount(5).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.OVERDUE)).notificationCount(4).build() + ); + // When + NotificationSummary result = mapper.toNotificationSummary(counts); + // Then + assertEquals(new NotificationSummary().notifications(0).standardActions(0).overdueActions(9), + result, "Notification summary should only have 9 overdue"); + } + + @Test + @DisplayName("Should return notification summary with multiple notification types") + void shouldReturnNotificationSummaryWithMultipleNotificationTypes() { + // Given + List counts = Arrays.asList( + NotificationCount.builder().id(withNotificationCountId(NotificationType.NOTIFICATIONS)).notificationCount(2).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.ACTION)).notificationCount(4).build(), + NotificationCount.builder().id(withNotificationCountId(NotificationType.OVERDUE)).notificationCount(6).build() + ); + // When + NotificationSummary result = mapper.toNotificationSummary(counts); + // Then + assertEquals(new NotificationSummary().notifications(2).standardActions(4).overdueActions(6), + result, "Notification summary should only have 7 actions"); + } + + + + private static NotificationCountId withNotificationCountId(NotificationType notificationType) { + return new NotificationCountId("User", notificationType); + } + + +} \ No newline at end of file