diff --git a/walkguide-backend/demo/src/test/java/com/walkguide/service/GuardianDashboardServiceTest.java b/walkguide-backend/demo/src/test/java/com/walkguide/service/GuardianDashboardServiceTest.java new file mode 100644 index 0000000..53deab4 --- /dev/null +++ b/walkguide-backend/demo/src/test/java/com/walkguide/service/GuardianDashboardServiceTest.java @@ -0,0 +1,184 @@ +package com.walkguide.service; + +import com.walkguide.dto.response.ActivityLogResponse; +import com.walkguide.dto.response.DashboardResponse; +import com.walkguide.entity.PairingRelation; +import com.walkguide.entity.SosEvent; +import com.walkguide.entity.User; +import com.walkguide.enums.PairingStatus; +import com.walkguide.enums.SosStatus; +import com.walkguide.repository.GuardianNotificationRepository; +import com.walkguide.repository.PairingRelationRepository; +import com.walkguide.repository.SosEventRepository; +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.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@DisplayName("GuardianDashboardService Unit Tests") +class GuardianDashboardServiceTest { + + @Mock PairingRelationRepository pairingRelationRepository; + @Mock LocationService locationService; + @Mock ActivityLogService activityLogService; + @Mock SosEventRepository sosEventRepository; + @Mock GuardianNotificationRepository notifRepository; + + @InjectMocks + GuardianDashboardService guardianDashboardService; + + private User pairedUser; + private PairingRelation activePairing; + + @BeforeEach + void setUp() { + pairedUser = User.builder() + .id(5L) + .email("user@test.com") + .displayName("Test User") + .role("ROLE_USER") + .uniqueUserId("ABCDEF123456") + .build(); + + activePairing = PairingRelation.builder() + .id(1L) + .guardian(User.builder().id(10L).email("guardian@test.com").build()) + .user(pairedUser) + .status(PairingStatus.ACTIVE) + .build(); + } + + // ===== getDashboard TESTS ===== + + @Test + @DisplayName("getDashboard - tidak ada pairing aktif: return empty dashboard") + void getDashboard_noPairing_shouldReturnEmptyDashboard() { + when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE)) + .thenReturn(Optional.empty()); + + DashboardResponse result = guardianDashboardService.getDashboard(10L); + + assertThat(result).isNotNull(); + assertThat(result.getPairedUserId()).isNull(); + assertThat(result.getRecentActivity()).isEmpty(); + assertThat(result.getUnreadSosCount()).isZero(); + assertThat(result.getUnreadNotifCount()).isZero(); + } + + @Test + @DisplayName("getDashboard - ada pairing aktif: return data lengkap user") + void getDashboard_withActivePairing_shouldReturnFullData() { + when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE)) + .thenReturn(Optional.of(activePairing)); + when(locationService.getLastLocation(5L)).thenReturn(Optional.empty()); + when(activityLogService.getLogs(eq(5L), any(PageRequest.class))) + .thenReturn(new PageImpl<>(List.of())); + when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(eq(5L), any(PageRequest.class))) + .thenReturn(List.of()); + when(notifRepository.countByUserIdAndIsReadFalse(5L)).thenReturn(0L); + + DashboardResponse result = guardianDashboardService.getDashboard(10L); + + assertThat(result.getPairedUserId()).isEqualTo(5L); + assertThat(result.getPairedUserName()).isEqualTo("Test User"); + assertThat(result.getPairedUserEmail()).isEqualTo("user@test.com"); + assertThat(result.getUniqueUserId()).isEqualTo("ABCDEF123456"); + } + + @Test + @DisplayName("getDashboard - SOS berstatus TRIGGERED harus dihitung sebagai unread") + void getDashboard_withTriggeredSos_shouldCountCorrectly() { + SosEvent triggered = SosEvent.builder() + .id(1L).userId(5L).status(SosStatus.TRIGGERED).build(); + SosEvent acknowledged = SosEvent.builder() + .id(2L).userId(5L).status(SosStatus.ACKNOWLEDGED).build(); + SosEvent resolved = SosEvent.builder() + .id(3L).userId(5L).status(SosStatus.RESOLVED).build(); + + when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE)) + .thenReturn(Optional.of(activePairing)); + when(locationService.getLastLocation(5L)).thenReturn(Optional.empty()); + when(activityLogService.getLogs(eq(5L), any(PageRequest.class))) + .thenReturn(new PageImpl<>(List.of())); + when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(eq(5L), any(PageRequest.class))) + .thenReturn(List.of(triggered, acknowledged, resolved)); + when(notifRepository.countByUserIdAndIsReadFalse(5L)).thenReturn(0L); + + DashboardResponse result = guardianDashboardService.getDashboard(10L); + + // Hanya yang TRIGGERED yang dihitung + assertThat(result.getUnreadSosCount()).isEqualTo(1L); + } + + @Test + @DisplayName("getDashboard - unreadNotifCount harus sesuai repository") + void getDashboard_withUnreadNotifs_shouldReturnCorrectCount() { + when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE)) + .thenReturn(Optional.of(activePairing)); + when(locationService.getLastLocation(5L)).thenReturn(Optional.empty()); + when(activityLogService.getLogs(eq(5L), any(PageRequest.class))) + .thenReturn(new PageImpl<>(List.of())); + when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(eq(5L), any(PageRequest.class))) + .thenReturn(List.of()); + when(notifRepository.countByUserIdAndIsReadFalse(5L)).thenReturn(7L); + + DashboardResponse result = guardianDashboardService.getDashboard(10L); + + assertThat(result.getUnreadNotifCount()).isEqualTo(7L); + } + + @Test + @DisplayName("getDashboard - recentActivity harus mengambil max 5 log terbaru") + void getDashboard_recentActivity_shouldFetchWithPageSizeFive() { + ActivityLogResponse log1 = ActivityLogResponse.builder().id(1L).build(); + ActivityLogResponse log2 = ActivityLogResponse.builder().id(2L).build(); + + when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE)) + .thenReturn(Optional.of(activePairing)); + when(locationService.getLastLocation(5L)).thenReturn(Optional.empty()); + when(activityLogService.getLogs(eq(5L), eq(PageRequest.of(0, 5)))) + .thenReturn(new PageImpl<>(List.of(log1, log2))); + when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(eq(5L), any(PageRequest.class))) + .thenReturn(List.of()); + when(notifRepository.countByUserIdAndIsReadFalse(5L)).thenReturn(0L); + + DashboardResponse result = guardianDashboardService.getDashboard(10L); + + assertThat(result.getRecentActivity()).hasSize(2); + // Verifikasi dipanggil dengan PageRequest(0,5) + verify(activityLogService).getLogs(eq(5L), eq(PageRequest.of(0, 5))); + } + + @Test + @DisplayName("getDashboard - semua SOS non-TRIGGERED tidak boleh dihitung sebagai unread") + void getDashboard_allNonTriggeredSos_shouldReturnZeroUnread() { + SosEvent ack = SosEvent.builder().id(1L).userId(5L).status(SosStatus.ACKNOWLEDGED).build(); + SosEvent res = SosEvent.builder().id(2L).userId(5L).status(SosStatus.RESOLVED).build(); + + when(pairingRelationRepository.findByGuardian_IdAndStatus(10L, PairingStatus.ACTIVE)) + .thenReturn(Optional.of(activePairing)); + when(locationService.getLastLocation(5L)).thenReturn(Optional.empty()); + when(activityLogService.getLogs(eq(5L), any(PageRequest.class))) + .thenReturn(new PageImpl<>(List.of())); + when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(eq(5L), any(PageRequest.class))) + .thenReturn(List.of(ack, res)); + when(notifRepository.countByUserIdAndIsReadFalse(5L)).thenReturn(0L); + + DashboardResponse result = guardianDashboardService.getDashboard(10L); + + assertThat(result.getUnreadSosCount()).isZero(); + } +} \ No newline at end of file