diff --git a/src/main/java/com/shiftsl/backend/Service/LeaveService.java b/src/main/java/com/shiftsl/backend/Service/LeaveService.java index 5ab60c8..70f6058 100644 --- a/src/main/java/com/shiftsl/backend/Service/LeaveService.java +++ b/src/main/java/com/shiftsl/backend/Service/LeaveService.java @@ -84,9 +84,7 @@ public String reject(Long leaveID) { return "leave request rejected"; } catch(Exception e) { logger.error("Error while trying to reject Leave Request for LeaveID={}", leaveID, e); - throw new LeaveNotSavedException(String.format( - "Failed to update leave status in database for leaveId=%d", leaveID - )); + throw new LeaveNotSavedException(String.format("Failed to update leave status in database for leaveId=%d", leaveID)); } } diff --git a/src/main/java/com/shiftsl/backend/Service/ShiftService.java b/src/main/java/com/shiftsl/backend/Service/ShiftService.java index 508c7b7..9bbb64e 100644 --- a/src/main/java/com/shiftsl/backend/Service/ShiftService.java +++ b/src/main/java/com/shiftsl/backend/Service/ShiftService.java @@ -73,7 +73,7 @@ public List getAvailableShifts() { // Doctor claims a shift from the shift pool @Transactional public void claimShift(Long doctorId, Long shiftId) { - String lockErrorMessage = null; + String errorMessage = null; try { logger.info("Claiming shift " + shiftId); Shift shift = getShiftWithLock(shiftId); @@ -82,7 +82,9 @@ public void claimShift(Long doctorId, Long shiftId) { int sizeD = shift.getNoOfDoctors(); if (sizeD >= shift.getTotalDoctors()) { - throw new DoctorCountExceededException("Number of assigned doctors exceeds the allowed limit."); + logger.error("Number of doctors is greater than total number of doctors allowed"); + errorMessage = "Number of assigned doctors exceeds the allowed limit."; + throw new DoctorCountExceededException(errorMessage); } Set doctors = shift.getDoctors(); @@ -93,8 +95,8 @@ public void claimShift(Long doctorId, Long shiftId) { shiftRepo.save(shift); } catch (LockTimeoutException | PessimisticLockException e) { logger.error("Too many threads are trying to claim shift."); - lockErrorMessage = "System is experiencing high load. Please try again later. " + e.getMessage(); - throw new ShiftClaimFailedException(lockErrorMessage); + errorMessage = "System is experiencing high load. Please try again later. " + e.getMessage(); + throw new ShiftClaimFailedException(errorMessage); } catch (Exception e) { logger.error("Error occurred while trying to store shift {} for doctor {} in database", shiftId, doctorId); @@ -102,8 +104,8 @@ public void claimShift(Long doctorId, Long shiftId) { "Unable to claim shiftId: %d for doctorId: %d", shiftId, doctorId ); - if (lockErrorMessage != null) { - fullMessage += ". " + lockErrorMessage; + if (errorMessage != null) { + fullMessage += ". " + errorMessage; } else { fullMessage += ". " + e.getMessage(); } diff --git a/src/main/java/com/shiftsl/backend/Service/UserService.java b/src/main/java/com/shiftsl/backend/Service/UserService.java index 37d1b44..9e4e9b5 100644 --- a/src/main/java/com/shiftsl/backend/Service/UserService.java +++ b/src/main/java/com/shiftsl/backend/Service/UserService.java @@ -27,28 +27,23 @@ public class UserService { @Transactional public User registerUser(UserDTO userDTO) { - try { - logger.info("Checking if user is already registered with given phone number"); - userRepo.findByPhoneNo(userDTO.phoneNo()).ifPresent(user -> { - throw new PhoneAlreadyInUseException("Phone Number already in use."); - }); + logger.info("Checking if user is already registered with given phone number"); + userRepo.findByPhoneNo(userDTO.phoneNo()).ifPresent(user -> { + throw new PhoneAlreadyInUseException("Phone Number already in use."); + }); - logger.info("Creating User object for User {} {}, and number {}", userDTO.firstName(), userDTO.lastName(), userDTO.phoneNo()); - User user = new User(); - user.setFirstName(userDTO.firstName()); - user.setLastName(userDTO.lastName()); - user.setSlmcReg(userDTO.slmcReg()); - user.setFirebaseUid(firebaseAuthService.createUser(userDTO.email())); - user.setEmail(userDTO.email()); - user.setPhoneNo(userDTO.phoneNo()); - user.setRole(userDTO.role()); + logger.info("Creating User object for User {} {}, and number {}", userDTO.firstName(), userDTO.lastName(), userDTO.phoneNo()); + User user = new User(); + user.setFirstName(userDTO.firstName()); + user.setLastName(userDTO.lastName()); + user.setSlmcReg(userDTO.slmcReg()); + user.setFirebaseUid(firebaseAuthService.createUser(userDTO.email())); + user.setEmail(userDTO.email()); + user.setPhoneNo(userDTO.phoneNo()); + user.setRole(userDTO.role()); - logger.info("Registering User with phone number: {}", user.getPhoneNo()); - return userRepo.save(user); - } catch (Exception e) { - logger.error("Unable to save user to database", e); - throw new AccountNotCreatedException("Unable to register the current user"); - } + logger.info("Registering User with phone number: {}", user.getPhoneNo()); + return userRepo.save(user); } @Transactional diff --git a/src/test/java/com/shiftsl/backend/unittests/Service/LeaveServiceTest.java b/src/test/java/com/shiftsl/backend/unittests/Service/LeaveServiceTest.java index 1fa204b..f72afa6 100644 --- a/src/test/java/com/shiftsl/backend/unittests/Service/LeaveServiceTest.java +++ b/src/test/java/com/shiftsl/backend/unittests/Service/LeaveServiceTest.java @@ -2,6 +2,7 @@ import com.google.firebase.database.DatabaseException; import com.shiftsl.backend.DTO.LeaveDTO; +import com.shiftsl.backend.Exceptions.LeaveNotFoundException; import com.shiftsl.backend.Exceptions.LeaveNotSavedException; import com.shiftsl.backend.Exceptions.LeaveRetrievalException; import com.shiftsl.backend.Service.LeaveService; @@ -12,6 +13,8 @@ import com.shiftsl.backend.unittests.Extensions.TimingExtension; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -28,6 +31,7 @@ @ExtendWith(TimingExtension.class) @ExtendWith(MockitoExtension.class) +@Execution(ExecutionMode.CONCURRENT) public class LeaveServiceTest { @InjectMocks @@ -39,13 +43,9 @@ public class LeaveServiceTest { @Mock private ShiftService shiftService; - @Mock private User testDoctor; - @Mock private Shift testShift; - @Mock private Leave testLeave; - @Mock private LeaveDTO testLeaveDTO; private ArgumentCaptor captor; @@ -156,8 +156,16 @@ void requestLeaveExceptionTest() { @Test void getLeaveTest() { when(leaveRepo.findById(ID)).thenReturn(Optional.ofNullable(testLeave)); - underTest.getLeave(ID); + Leave result = underTest.getLeave(ID); verify(leaveRepo).findById(ID); + assertEquals(testLeave, result); + } + + @Test + void getLeaveExceptionTest() { + when(leaveRepo.findById(ID)).thenReturn(Optional.empty()); + Exception ex = assertThrows(LeaveNotFoundException.class,() -> underTest.getLeave(ID)); + assertEquals("User - (" + ID + ") not found.", ex.getMessage()); } @Test @@ -176,9 +184,10 @@ void rejectTest() { @Test void rejectExceptionTest() { - when(leaveRepo.findById(ID)).thenThrow(new DatabaseException("Unable to access the database")); + when(leaveRepo.findById(ID)).thenThrow(new RuntimeException()); + Exception e = assertThrows(LeaveNotSavedException.class,() -> underTest.reject(ID)); verify(leaveRepo, never()).save(testLeave); // verify that the leave object was never saved to database - Exception e = assertThrows(LeaveNotSavedException.class,() -> underTest.approve(ID)); + verify(leaveRepo).findById(ID); assertEquals(String.format("Failed to update leave status in database for leaveId=%d", ID), e.getMessage()); } diff --git a/src/test/java/com/shiftsl/backend/unittests/Service/ShiftServiceTest.java b/src/test/java/com/shiftsl/backend/unittests/Service/ShiftServiceTest.java new file mode 100644 index 0000000..3425669 --- /dev/null +++ b/src/test/java/com/shiftsl/backend/unittests/Service/ShiftServiceTest.java @@ -0,0 +1,334 @@ +package com.shiftsl.backend.unittests.Service; + +import com.shiftsl.backend.DTO.ShiftDTO; +import com.shiftsl.backend.Exceptions.*; +import com.shiftsl.backend.Service.ShiftService; +import com.shiftsl.backend.Service.UserService; +import com.shiftsl.backend.Service.WardService; +import com.shiftsl.backend.model.Shift; +import com.shiftsl.backend.model.User; +import com.shiftsl.backend.model.Ward; +import com.shiftsl.backend.repo.ShiftRepo; +import com.shiftsl.backend.unittests.Extensions.TimingExtension; +import jakarta.transaction.TransactionalException; +import jakarta.persistence.PessimisticLockException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + + +@ExtendWith(TimingExtension.class) +@ExtendWith(MockitoExtension.class) +@Execution(ExecutionMode.CONCURRENT) +public class ShiftServiceTest { + + @InjectMocks + private ShiftService underTest; + @Mock + private ShiftRepo shiftRepo; + @Mock(strictness = Mock.Strictness.LENIENT) + private UserService userService; + @Mock + private WardService wardService; + + private final Long id = 1L; + + private static Shift testShift; + private static Ward testWard; + private static User testDoctor; + + private static Set doctorIds; + private static Set doctors; + private static List shifts; + private ArgumentCaptor shiftCaptor = ArgumentCaptor.forClass(Shift.class); + + @BeforeEach + void init() { + testShift = new Shift(); + testShift.setId(1L); + testShift.setDoctors(doctors); + testShift.setNoOfDoctors(1); + testShift.setTotalDoctors(2); + testShift.setStartTime(LocalDateTime.MIN); + testShift.setEndTime(LocalDateTime.MAX); + testShift.setWard(testWard); + } + + @BeforeAll + static void setUp() { + doctorIds = new HashSet<>(Set.of( + 1L + )); + + doctors = new HashSet<>(Set.of( + new User() + )); + + shifts = new ArrayList<>(List.of( + new Shift() + )); + testWard = new Ward(); + testDoctor = new User(); testDoctor.setId(1L); + } + + @Test + void createShiftTest() { + ShiftDTO testShiftDTO = new ShiftDTO(1, LocalDateTime.MIN, LocalDateTime.MAX, doctorIds); + User doctor1 = new User(); doctor1.setId(1L); + when(userService.getUserById(1L)).thenReturn(doctor1); + when(wardService.getWardByID(id)).thenReturn(testWard); + when(shiftRepo.save(any(Shift.class))).thenReturn(testShift); + + underTest.createShift(testShiftDTO, id); + + verify(wardService).getWardByID(id); + verify(shiftRepo).save(shiftCaptor.capture()); + + Shift result = shiftCaptor.getValue(); + + assertEquals(testShiftDTO.startTime(), result.getStartTime()); + assertEquals(testShiftDTO.endTime(), result.getEndTime()); + assertEquals(testShiftDTO.totalDoctors(), result.getTotalDoctors()); + } + + @Test + void createShiftFailTest() { + ShiftDTO testShiftDTO = new ShiftDTO(0, LocalDateTime.MIN, LocalDateTime.MAX, doctorIds); + User doctor1 = new User(); doctor1.setId(1L); + when(userService.getUserById(1L)).thenReturn(doctor1); + when(wardService.getWardByID(id)).thenReturn(testWard); + + Exception ex = assertThrows(DoctorCountExceededException.class, () -> underTest.createShift(testShiftDTO, id)); + verify(wardService).getWardByID(id); + verify(userService).getUserById(1L); + verify(shiftRepo, never()).save(any(Shift.class)); + assertEquals("Number of assigned doctors exceeds the allowed limit.", ex.getMessage()); + } + + @Test + void getAvailableShiftsTest() { + when(shiftRepo.findAvailableShifts()).thenReturn(List.of(new Shift())); + List result = underTest.getAvailableShifts(); + verify(shiftRepo).findAvailableShifts(); + assertEquals(1, result.size()); + } + + @Test + void getAvailableShiftsFailTest() { + when(shiftRepo.findAvailableShifts()).thenThrow(TransactionalException.class); + Exception ex = assertThrows(ShiftRetrievalException.class, () -> underTest.getAvailableShifts()); + verify(shiftRepo).findAvailableShifts(); + assertEquals("Error occurred while trying to retrieve available shifts from database", ex.getMessage()); + } + + @Test + void claimShiftTest() { + when(shiftRepo.findShiftWithLock(id)).thenReturn(Optional.of(testShift)); + when(userService.getUserById(id)).thenReturn(testDoctor); + + underTest.claimShift(id, id); + verify(shiftRepo).save(shiftCaptor.capture()); + verify(userService).getUserById(id); + verify(shiftRepo).findShiftWithLock(id); + + Shift result = shiftCaptor.getValue(); + assertEquals(testShift, result); + } + + @Test + void claimShiftLockExceptionTest() { + when(shiftRepo.findShiftWithLock(id)).thenThrow(new PessimisticLockException("Unable to get lock", new SQLException(), "some sql string")); + + Exception ex = assertThrows(ShiftClaimFailedException.class, () -> underTest.claimShift(id, id)); + verify(shiftRepo).findShiftWithLock(id); + verify(userService, never()).getUserById(id); + verify(shiftRepo, never()).save(any(Shift.class)); + assertEquals("System is experiencing high load. Please try again later. Unable to get lock", ex.getMessage()); + } + + @Test + void claimShiftDoctorCountExceededExceptionTest() { + Shift badShift = new Shift(); + badShift.setNoOfDoctors(10); + badShift.setTotalDoctors(10); + when(shiftRepo.findShiftWithLock(id)).thenReturn(Optional.of(badShift)); + when(userService.getUserById(id)).thenReturn(testDoctor); + Exception ex = assertThrows(ShiftClaimFailedException.class, () -> underTest.claimShift(id, id)); + verify(shiftRepo).findShiftWithLock(id); + verify(userService).getUserById(id); + verify(shiftRepo, never()).save(any(Shift.class)); + assertEquals("Unable to claim shiftId: " + id + " for doctorId: " + id + ". Number of assigned doctors exceeds the allowed limit.", ex.getMessage()); + } + + @Test + void claimShiftGeneralExceptionTest() { + when(shiftRepo.findShiftWithLock(id)).thenThrow(new TransactionalException("Error while trying to retrieve shift details from database", new SQLException())); + + Exception ex = assertThrows(ShiftClaimFailedException.class, () -> underTest.claimShift(id, id)); + verify(shiftRepo).findShiftWithLock(id); + verify(userService, never()).getUserById(id); + verify(shiftRepo, never()).save(any(Shift.class)); + assertEquals(String.format("Unable to claim shiftId: %d for doctorId: %d. Error while trying to retrieve shift details from database", 1, 1), ex.getMessage()); + } + + @Test + void getShiftByIdTest() { + when(shiftRepo.findById(id)).thenReturn(Optional.of(testShift)); + Shift result = underTest.getShiftByID(id); + verify(shiftRepo).findById(id); + assertEquals(testShift, result); + } + + @Test + void getShiftByIdFailTest() { + when(shiftRepo.findById(id)).thenReturn(Optional.empty()); + Exception ex = assertThrows(ShiftNotFoundException.class, () -> underTest.getShiftByID(id)); + assertEquals("Shift ID - (" + id + ") not found.", ex.getMessage()); + verify(shiftRepo).findById(id); + } + + @Test + void getShiftWithLock() { + when(shiftRepo.findShiftWithLock(id)).thenReturn(Optional.of(testShift)); + Shift result = underTest.getShiftWithLock(id); + verify(shiftRepo).findShiftWithLock(id); + assertEquals(testShift, result); + } + + @Test + void getShiftWithLockFailTest() { + when(shiftRepo.findShiftWithLock(id)).thenReturn(Optional.empty()); + Exception ex = assertThrows(ShiftNotFoundException.class, () -> underTest.getShiftWithLock(id)); + assertEquals("Shift ID - (" + id + ") not found.", ex.getMessage()); + verify(shiftRepo).findShiftWithLock(id); + } + + @Test + void getShiftsForDoctorTest() { + when(userService.getUserById(id)).thenReturn(testDoctor); + when(shiftRepo.findByDoctors_Id(id)).thenReturn(shifts); + + List result = underTest.getShiftsForDoctor(id); + verify(shiftRepo).findByDoctors_Id(id); + verify(userService).getUserById(id); + assertArrayEquals(shifts.toArray(), result.toArray()); + } + + @Test + void getShiftsForDoctorFailTest() { + when(shiftRepo.findByDoctors_Id(id)).thenReturn(new ArrayList<>()); + Exception ex = assertThrows(ShiftsNotFoundException.class, () -> underTest.getShiftsForDoctor(id)); + verify(userService).getUserById(id); + verify(shiftRepo).findByDoctors_Id(id); + assertEquals("No shifts found for doctor with ID " + id, ex.getMessage()); + } + + @Test + void getShiftsForDoctorForNonExistingDoctorTest() { + when(userService.getUserById(id)).thenThrow(new UserNotFoundException("User - (" + id + ") not found.")); + Exception ex = assertThrows(UserNotFoundException.class, () -> underTest.getShiftsForDoctor(id)); + verify(userService).getUserById(id); + verify(shiftRepo, never()).findByDoctors_Id(id); + assertEquals("User - (" + id + ") not found.", ex.getMessage()); + } + + @Test + void getAllShiftsTest() { + // test for non-empty database + when(shiftRepo.findAll()).thenReturn(shifts); + List result = underTest.getAllShifts(); + verify(shiftRepo).findAll(); + assertArrayEquals(shifts.toArray(), result.toArray()); + assertFalse(result.isEmpty()); + + // test for empty database + when(shiftRepo.findAll()).thenReturn(new ArrayList<>()); + List resultEmpty = underTest.getAllShifts(); + verify(shiftRepo, times(2)).findAll(); + assertTrue(resultEmpty.isEmpty()); + + } + + @Test + void getAllShiftsFailTest() { + when(shiftRepo.findAll()).thenThrow(new RuntimeException()); + Exception ex = assertThrows(Exception.class, () -> underTest.getAllShifts()); + verify(shiftRepo).findAll(); + assertEquals("Error occurred while trying to retrieve all shifts from database", ex.getMessage()); + } + + @Test + void deleteShiftByIdTest() { + doNothing().when(shiftRepo).delete(testShift); + doReturn(Optional.of(testShift)).when(shiftRepo).findById(20L); + + underTest.deleteShiftByID(20L); + verify(shiftRepo).findById(20L); + verify(shiftRepo).delete(testShift); + } + + @Test + void deleteShiftByIdFailTest() { + when(shiftRepo.findById(3L)).thenReturn(Optional.empty()); + Exception ex = assertThrows(ShiftRetrievalException.class, () -> underTest.deleteShiftByID(3L)); + assertEquals("Shift ID - (" + 3 + ") not found.", ex.getMessage()); + } + + @Test + void updateShiftByIdTest() { + when(shiftRepo.findById(testShift.getId())).thenReturn(Optional.of(testShift)); + when(shiftRepo.save(testShift)).thenReturn(testShift); + Shift result = underTest.updateShiftByID(testShift); + verify(shiftRepo).findById(testShift.getId()); + assertEquals(testShift, result); + } + + @Test + void getRosterTest() { + when(shiftRepo.findByStartTimeBetween(any(LocalDateTime.class), any(LocalDateTime.class))).thenReturn(shifts); + int month = 1; + if (LocalDateTime.now().getMonthValue() == 12) { + month = 12; + } else { + month = LocalDateTime.now().getMonthValue() + 1; + } + List result = underTest.getRoster(month); + verify(shiftRepo).findByStartTimeBetween(any(LocalDateTime.class), any(LocalDateTime.class)); + assertArrayEquals(shifts.toArray(), result.toArray()); + } + + @Test + void getRosterForInvalidMonthTest() { + Exception ex = assertThrows(IllegalArgumentException.class, () -> underTest.getRoster(-1)); + assertEquals("Invalid month. Please provide a value between 1 and 12.", ex.getMessage()); + + Exception ex2 = assertThrows(IllegalArgumentException.class, () -> underTest.getRoster(13)); + assertEquals("Invalid month. Please provide a value between 1 and 12.", ex2.getMessage()); + + verify(shiftRepo, never()).findByStartTimeBetween(any(LocalDateTime.class), any(LocalDateTime.class)); + } + + @Test + void getRosterFailTest() { + when(shiftRepo.findByStartTimeBetween(any(LocalDateTime.class), any(LocalDateTime.class))).thenThrow(new RuntimeException()); + Exception ex = assertThrows(ShiftsNotFoundException.class, () -> underTest.getRoster(2)); + assertEquals("Unable to retrieve shifts for the given month from database", ex.getMessage()); + } + +} + diff --git a/src/test/java/com/shiftsl/backend/unittests/Service/ShiftSwapServiceTest.java b/src/test/java/com/shiftsl/backend/unittests/Service/ShiftSwapServiceTest.java new file mode 100644 index 0000000..fd86d55 --- /dev/null +++ b/src/test/java/com/shiftsl/backend/unittests/Service/ShiftSwapServiceTest.java @@ -0,0 +1,178 @@ +package com.shiftsl.backend.unittests.Service; + +import com.shiftsl.backend.Exceptions.ShiftNotFoundException; +import com.shiftsl.backend.Exceptions.ShiftSwapNotFoundException; +import com.shiftsl.backend.Service.ShiftService; +import com.shiftsl.backend.Service.ShiftSwapService; +import com.shiftsl.backend.Service.UserService; +import com.shiftsl.backend.model.*; +import com.shiftsl.backend.repo.ShiftSwapRepo; +import com.shiftsl.backend.unittests.Extensions.TimingExtension; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.LocalDateTime; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + + +@ExtendWith(TimingExtension.class) +@ExtendWith(MockitoExtension.class) +@Execution(ExecutionMode.CONCURRENT) +public class ShiftSwapServiceTest { + + @InjectMocks + private ShiftSwapService underTest; + @Mock + private ShiftSwapRepo shiftSwapRepo; + @Mock + private ShiftService shiftService; + @Mock + private UserService userService; + + private final Long id = 1L; + + private static Shift testShift; + private static Ward testWard; + private static User testDoctor; + private static ShiftSwap testShiftSwap; + + private static Set doctors = mock(Set.class); + private static List shiftSwaps = mock(List.class); + private final ArgumentCaptor shiftSwapCaptor = ArgumentCaptor.forClass(ShiftSwap.class); + + @BeforeEach + void init() { + testShift = new Shift(); + testShift.setId(1L); + testShift.setDoctors(doctors); + testShift.setNoOfDoctors(1); + testShift.setTotalDoctors(2); + testShift.setStartTime(LocalDateTime.MIN); + testShift.setEndTime(LocalDateTime.MAX); + testShift.setWard(testWard); + + testShiftSwap = new ShiftSwap(); + testShiftSwap.setId(1L); + testShiftSwap.setShift(testShift); + } + + @BeforeAll + static void setUp() { + testWard = new Ward(); + testDoctor = new User(); testDoctor.setId(1L); + + } + + @Test + void requestSwapTest() { + when(shiftService.getShiftByID(id)).thenReturn(testShift); + when(userService.getUserById(id)).thenReturn(testDoctor); + when(userService.getUserById(id)).thenReturn(testDoctor); + when(shiftSwapRepo.save(any(ShiftSwap.class))).thenReturn(new ShiftSwap()); + + underTest.requestSwap(id, id, id); + + verify(shiftService).getShiftByID(id); + verify(userService, times(2)).getUserById(id); + verify(shiftSwapRepo).save(shiftSwapCaptor.capture()); + + ShiftSwap result = shiftSwapCaptor.getValue(); + + assertNotNull(result); + assertEquals(result.getShift(), testShift); + assertEquals(result.getSender(), testDoctor); + assertEquals(result.getReceiver(), testDoctor); + assertEquals(result.getReceiverStat(), StatusSwap.PENDING); + } + + @Test + void getShiftSwapTest() { + when(shiftSwapRepo.findById(id)).thenReturn(Optional.of(testShiftSwap)); + ShiftSwap result = underTest.getShiftSwap(id); + assertNotNull(result); + verify(shiftSwapRepo).findById(id); + assertEquals(testShiftSwap, result); + } + + @Test + void getShiftSwapExceptionTest() { + when(shiftSwapRepo.findById(id)).thenReturn(Optional.empty()); + Exception ex = assertThrows(ShiftNotFoundException.class, () -> underTest.getShiftSwap(id)); + assertEquals("Shift swap " + id + "not found.", ex.getMessage()); + verify(shiftSwapRepo).findById(id); + } + + @Test + void acceptTest() { + when(shiftSwapRepo.findById(id)).thenReturn(Optional.of(testShiftSwap)); + when(shiftService.updateShiftByID(testShift)).thenReturn(testShift); + when(shiftSwapRepo.save(any(ShiftSwap.class))).thenReturn(new ShiftSwap()); + String result = underTest.accept(id); + + verify(shiftSwapRepo).findById(id); + verify(shiftSwapRepo).save(testShiftSwap); + verify(shiftService).updateShiftByID(testShift); + + verify(doctors).remove(testShiftSwap.getSender()); + verify(doctors).add(testShiftSwap.getReceiver()); + + assertEquals("shift swap accepted by the receiving doctor", result); + } + + @Test + void rejectTest() { + when(shiftSwapRepo.findById(id)).thenReturn(Optional.of(testShiftSwap)); + when(shiftSwapRepo.save(any(ShiftSwap.class))).thenReturn(testShiftSwap); + + String result = underTest.reject(id); + + verify(shiftSwapRepo).findById(id); + verify(shiftSwapRepo).save(shiftSwapCaptor.capture()); + + ShiftSwap savedSwap = shiftSwapCaptor.getValue(); + + assertEquals("shift swap rejected by the receiving doctor", result); + assertEquals(savedSwap, testShiftSwap); + } + + @Test + void getSwapReceivedByDoctorTest() { + when(shiftSwapRepo.findByReceiverId(id)).thenReturn(shiftSwaps); + List result = underTest.getSwapReceivedByDoctor(id); + verify(shiftSwapRepo).findByReceiverId(id); + assertNotNull(result); + } + + @Test + void getSwapReceivedByDoctorExceptionTest() { + when(shiftSwapRepo.findByReceiverId(id)).thenThrow(new RuntimeException()); + Exception ex = assertThrows(ShiftSwapNotFoundException.class, () -> underTest.getSwapReceivedByDoctor(id)); + assertEquals("Unable to find shift swap request details for doctor " + id, ex.getMessage()); + } + + @Test + void getSwapSentByDoctorTest() { + when(shiftSwapRepo.findBySenderId(id)).thenReturn(shiftSwaps); + List result = underTest.getSwapSentByDoctor(id); + verify(shiftSwapRepo).findBySenderId(id); + assertNotNull(result); + } + + @Test + void getSwapSentByDoctorExceptionTest() { + when(shiftSwapRepo.findBySenderId(id)).thenThrow(new RuntimeException()); + Exception ex = assertThrows(ShiftSwapNotFoundException.class, () -> underTest.getSwapSentByDoctor(id)); + assertEquals("Unable to find shift swap request details by doctor " + id, ex.getMessage()); + } +} diff --git a/src/test/java/com/shiftsl/backend/unittests/Service/UserServiceTest.java b/src/test/java/com/shiftsl/backend/unittests/Service/UserServiceTest.java new file mode 100644 index 0000000..91eac5d --- /dev/null +++ b/src/test/java/com/shiftsl/backend/unittests/Service/UserServiceTest.java @@ -0,0 +1,163 @@ +package com.shiftsl.backend.unittests.Service; + +import com.shiftsl.backend.DTO.UserDTO; +import com.shiftsl.backend.Exceptions.PhoneAlreadyInUseException; +import com.shiftsl.backend.Exceptions.UserNotFoundException; +import com.shiftsl.backend.Exceptions.UserNotUpdatedException; +import com.shiftsl.backend.Service.FirebaseAuthService; +import com.shiftsl.backend.Service.UserService; +import com.shiftsl.backend.model.Role; +import com.shiftsl.backend.model.User; +import com.shiftsl.backend.repo.UserRepo; +import com.shiftsl.backend.unittests.Extensions.TimingExtension; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@ExtendWith(TimingExtension.class) +@Execution(ExecutionMode.CONCURRENT) +public class UserServiceTest { + + @InjectMocks + private UserService underTest; + @Mock + private UserRepo userRepo; + @Mock + private FirebaseAuthService firebaseAuthService; + + private final Long id = 1L; + private final String firebaseUid = "123456"; + private final List users = mock(List.class); + private final User mockUser = mock(User.class); + private final ArgumentCaptor userCaptor = ArgumentCaptor.forClass(User.class); + + private static UserDTO fakeUserDTO; + + @BeforeAll + static void setUp() { + fakeUserDTO = new UserDTO("John", "Doe", "1234567", "john.doe@gmail.com", "0771234598", Role.HR_ADMIN); + } + + @Test + void registerUserTest() { + when(userRepo.findByPhoneNo(fakeUserDTO.phoneNo())).thenReturn(Optional.empty()); + when(firebaseAuthService.createUser(fakeUserDTO.email())).thenReturn(firebaseUid); + when(userRepo.save(any(User.class))).thenReturn(mockUser); + + underTest.registerUser(fakeUserDTO); + verify(userRepo).findByPhoneNo(fakeUserDTO.phoneNo()); + verify(firebaseAuthService).createUser(fakeUserDTO.email()); + verify(userRepo).save(userCaptor.capture()); + + User result = userCaptor.getValue(); + assertEquals(fakeUserDTO.firstName(), result.getFirstName()); + assertEquals(fakeUserDTO.lastName(), result.getLastName()); + assertEquals(fakeUserDTO.email(), result.getEmail()); + assertEquals(fakeUserDTO.phoneNo(), result.getPhoneNo()); + assertEquals(fakeUserDTO.role(), result.getRole()); + assertEquals(fakeUserDTO.slmcReg(), result.getSlmcReg()); + } + + @Test + void registerExistingUserTest() { + when(userRepo.findByPhoneNo(fakeUserDTO.phoneNo())).thenReturn(Optional.of(mockUser)); + Exception ex = assertThrows(PhoneAlreadyInUseException.class, () -> underTest.registerUser(fakeUserDTO)); + assertEquals("Phone Number already in use.", ex.getMessage()); + verify(userRepo).findByPhoneNo(fakeUserDTO.phoneNo()); + verify(userRepo, never()).save(any(User.class)); + verify(firebaseAuthService, never()).createUser(any(String.class)); + } + + @Test + void getUserByIdTest() { + when(userRepo.findById(id)).thenReturn(Optional.of(mockUser)); + User result = underTest.getUserById(id); + verify(userRepo).findById(id); + assertNotNull(result); + } + + @Test + void getUserByIdExceptionTest() { + when(userRepo.findById(id)).thenReturn(Optional.empty()); + Exception ex = assertThrows(UserNotFoundException.class, () -> underTest.getUserById(id)); + assertEquals("User - (" + id + ") not found.", ex.getMessage()); + } + + @Test + void getUserByRoleTest() { + when(userRepo.findByRoleIn(List.of(Role.HR_ADMIN))).thenReturn(users); + List result = underTest.getUsersByRole(Role.HR_ADMIN); + assertNotNull(result); + verify(userRepo).findByRoleIn(List.of(Role.HR_ADMIN)); + } + + @Test + void updateUserByIdTest() { + when(userRepo.findById(id)).thenReturn(Optional.of(mockUser)); + when(userRepo.save(mockUser)).thenReturn(mockUser); + User result = underTest.updateUserById(id, mockUser); + + verify(userRepo).findById(id); + verify(userRepo).save(mockUser); + assertNotNull(result); + } + + @Test + void updateUserByIdExceptionTest() { + when(userRepo.findById(id)).thenReturn(Optional.empty()); + assertThrows(UserNotUpdatedException.class, () -> underTest.updateUserById(id, mockUser)); + } + + @Test + void deleteUserByIdTest() { + doNothing().when(userRepo).deleteById(id); + String result = underTest.deleteUserById(id); + verify(userRepo).deleteById(id); + assertEquals("User deleted successfully.", result); + } + + @Test + void findUserByFirebaseUidTest() { + when(userRepo.findByFirebaseUid(firebaseUid)).thenReturn(Optional.ofNullable(mockUser)); + User result = underTest.findUserByFirebaseUid(firebaseUid); + assertNotNull(result); + verify(userRepo).findByFirebaseUid(firebaseUid); + } + + @Test + void findUserByFirebaseUidExceptionTest() { + when(userRepo.findByFirebaseUid(firebaseUid)).thenReturn(Optional.empty()); + Exception ex = assertThrows(UserNotFoundException.class, () -> underTest.findUserByFirebaseUid(firebaseUid)); + assertEquals("Unable to find user by Firebase UID - " + firebaseUid, ex.getMessage()); + } + + @Test + void getAllUsersTest() { + when(userRepo.findAll()).thenReturn(users); + List result = underTest.getAllUsers(); + assertNotNull(result); + verify(userRepo).findAll(); + } + + @Test + void getDoctorsTest() { + when(userRepo.findByRoleIn(List.of(Role.DOCTOR_PERM, Role.DOCTOR_TEMP))).thenReturn(users); + List result = underTest.getDoctors(); + verify(userRepo).findByRoleIn(List.of(Role.DOCTOR_PERM, Role.DOCTOR_TEMP)); + assertNotNull(result); + } +} diff --git a/src/test/java/com/shiftsl/backend/unittests/Service/WardServiceTest.java b/src/test/java/com/shiftsl/backend/unittests/Service/WardServiceTest.java new file mode 100644 index 0000000..ae896cb --- /dev/null +++ b/src/test/java/com/shiftsl/backend/unittests/Service/WardServiceTest.java @@ -0,0 +1,125 @@ +package com.shiftsl.backend.unittests.Service; + +import com.shiftsl.backend.Exceptions.NotAWardAdminException; +import com.shiftsl.backend.Exceptions.WardNotFoundException; +import com.shiftsl.backend.Service.UserService; +import com.shiftsl.backend.Service.WardService; +import com.shiftsl.backend.model.Role; +import com.shiftsl.backend.model.User; +import com.shiftsl.backend.model.Ward; +import com.shiftsl.backend.repo.WardRepo; +import com.shiftsl.backend.unittests.Extensions.TimingExtension; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@ExtendWith(TimingExtension.class) +@Execution(ExecutionMode.CONCURRENT) +public class WardServiceTest { + + @InjectMocks + private WardService underTest; + @Mock + private WardRepo wardRepo; + @Mock + private UserService userService; + + private final Long id = 1L; + private final String wardName = "testWard"; + private final List wards = mock(List.class); + private final Ward mockWard = mock(Ward.class); + private final ArgumentCaptor wardCaptor = ArgumentCaptor.forClass(Ward.class); + + private static User wardAdmin; + + @BeforeAll + static void setUp() { + wardAdmin = new User(); + wardAdmin.setRole(Role.WARD_ADMIN); + } + + @Test + void createWardTest() { + when(userService.getUserById(id)).thenReturn(wardAdmin); + when(wardRepo.save(any(Ward.class))).thenReturn(mockWard); + underTest.createWard(id, wardName); + + verify(userService).getUserById(id); + verify(wardRepo).save(wardCaptor.capture()); + Ward result = wardCaptor.getValue(); + assertEquals(wardName, result.getName()); + assertEquals(wardAdmin, result.getWardAdmin()); + } + + @Test + void createWithoutWardAdminTest() { + User notWardAdmin = new User(); + notWardAdmin.setRole(Role.HR_ADMIN); + when(userService.getUserById(id)).thenReturn(notWardAdmin); + Exception ex = assertThrows(NotAWardAdminException.class, () -> underTest.createWard(id, wardName)); + assertEquals("Given user is not a ward admin", ex.getMessage()); + verify(userService).getUserById(id); + verify(wardRepo, never()).save(any(Ward.class)); + } + + @Test + void updateWardTest() { + when(wardRepo.save(mockWard)).thenReturn(mockWard); + Ward result = underTest.updateWard(mockWard); + verify(wardRepo).save(mockWard); + assertNotNull(result); + } + + @Test + void findByNameTest() { + when(wardRepo.findByName(wardName)).thenReturn(Optional.ofNullable(mockWard)); + Optional result = underTest.findByName(wardName); + verify(wardRepo).findByName(wardName); + assertNotNull(result); + } + + @Test + void getWardList() { + when(wardRepo.findAll()).thenReturn(wards); + List result = underTest.getWardList(); + assertNotNull(result); + verify(wardRepo).findAll(); + } + + @Test + void deleteWardTest() { + doNothing().when(wardRepo).deleteById(id); + underTest.deleteWardById(id); + verify(wardRepo).deleteById(id); + } + + @Test + void getWardByIDTest() { + when(wardRepo.findById(id)).thenReturn(Optional.ofNullable(mockWard)); + Ward result = underTest.getWardByID(id); + assertNotNull(result); + verify(wardRepo).findById(id); + } + + @Test + void getWardByIDForNonExistingWardTest() { + when(wardRepo.findById(id)).thenReturn(Optional.empty()); + Exception ex = assertThrows(WardNotFoundException.class, () -> underTest.getWardByID(id)); + assertEquals("Ward (ID - " + id + " not found.", ex.getMessage()); + } + +} diff --git a/src/test/resources/junit-platform.properties b/src/test/resources/junit-platform.properties index ab42ab5..8e3db4e 100644 --- a/src/test/resources/junit-platform.properties +++ b/src/test/resources/junit-platform.properties @@ -1,3 +1,3 @@ -junit.jupiter.execution.parallel.enabled=true -junit.jupiter.execution.parallel.mode.default=concurrent -junit.jupiter.execution.parallel.config.strategy=dynamic +#junit.jupiter.execution.parallel.enabled=true +#junit.jupiter.execution.parallel.mode.default=same_thread +#junit.jupiter.execution.parallel.mode.classes.default= same_thread