test: add SosServiceTest unit test
This commit is contained in:
parent
d428c8fc35
commit
dd9c51c0bc
@ -0,0 +1,253 @@
|
|||||||
|
package com.walkguide.service;
|
||||||
|
|
||||||
|
import com.walkguide.dto.request.SosRequest;
|
||||||
|
import com.walkguide.dto.response.SosEventResponse;
|
||||||
|
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.exception.ResourceNotFoundException;
|
||||||
|
import com.walkguide.repository.*;
|
||||||
|
import com.walkguide.websocket.LocationBroadcaster;
|
||||||
|
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.ArgumentCaptor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageImpl;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
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("SosService Unit Tests")
|
||||||
|
class SosServiceTest {
|
||||||
|
|
||||||
|
@Mock SosEventRepository sosEventRepository;
|
||||||
|
@Mock PairingRelationRepository pairingRelationRepository;
|
||||||
|
@Mock UserRepository userRepository;
|
||||||
|
@Mock ActivityLogService activityLogService;
|
||||||
|
@Mock FcmService fcmService;
|
||||||
|
@Mock LocationBroadcaster locationBroadcaster;
|
||||||
|
|
||||||
|
@InjectMocks SosService sosService;
|
||||||
|
|
||||||
|
private User guardian;
|
||||||
|
private User user;
|
||||||
|
private PairingRelation activePairing;
|
||||||
|
private SosEvent savedSos;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
guardian = User.builder()
|
||||||
|
.id(1L).email("guardian@test.com").displayName("Guardian Test")
|
||||||
|
.fcmToken("guardian-fcm-token").build();
|
||||||
|
|
||||||
|
user = User.builder()
|
||||||
|
.id(2L).email("user@test.com").displayName("User Test")
|
||||||
|
.fcmToken("user-fcm-token").build();
|
||||||
|
|
||||||
|
activePairing = PairingRelation.builder()
|
||||||
|
.id(1L).guardian(guardian).user(user).status(PairingStatus.ACTIVE).build();
|
||||||
|
|
||||||
|
savedSos = SosEvent.builder()
|
||||||
|
.id(50L).userId(2L).triggerType("MANUAL")
|
||||||
|
.lat(-7.257).lng(112.752)
|
||||||
|
.status(SosStatus.TRIGGERED)
|
||||||
|
.createdAt(LocalDateTime.now()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== triggerSos TESTS =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("triggerSos - MANUAL dengan koordinat: harus simpan SOS dan return response")
|
||||||
|
void triggerSos_manualWithCoords_shouldSaveAndReturnResponse() {
|
||||||
|
SosRequest req = new SosRequest();
|
||||||
|
req.setTriggerType("MANUAL");
|
||||||
|
req.setLat(-7.257);
|
||||||
|
req.setLng(112.752);
|
||||||
|
|
||||||
|
when(sosEventRepository.save(any(SosEvent.class))).thenReturn(savedSos);
|
||||||
|
when(userRepository.findById(2L)).thenReturn(Optional.of(user));
|
||||||
|
when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
|
||||||
|
.thenReturn(Optional.empty()); // tidak ada guardian → skip FCM
|
||||||
|
doNothing().when(activityLogService).createLog(any(), any(), any(), any());
|
||||||
|
|
||||||
|
SosEventResponse result = sosService.triggerSos(2L, req);
|
||||||
|
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
assertThat(result.getId()).isEqualTo(50L);
|
||||||
|
assertThat(result.getUserId()).isEqualTo(2L);
|
||||||
|
assertThat(result.getTriggerType()).isEqualTo("MANUAL");
|
||||||
|
assertThat(result.getStatus()).isEqualTo("TRIGGERED");
|
||||||
|
verify(sosEventRepository).save(any(SosEvent.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("triggerSos - triggerType null: harus default ke MANUAL")
|
||||||
|
void triggerSos_nullTriggerType_shouldDefaultToManual() {
|
||||||
|
SosRequest req = new SosRequest();
|
||||||
|
req.setTriggerType(null);
|
||||||
|
req.setLat(-7.257);
|
||||||
|
req.setLng(112.752);
|
||||||
|
|
||||||
|
when(sosEventRepository.save(any(SosEvent.class))).thenReturn(savedSos);
|
||||||
|
when(userRepository.findById(2L)).thenReturn(Optional.of(user));
|
||||||
|
when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
|
||||||
|
.thenReturn(Optional.empty());
|
||||||
|
doNothing().when(activityLogService).createLog(any(), any(), any(), any());
|
||||||
|
|
||||||
|
ArgumentCaptor<SosEvent> captor = ArgumentCaptor.forClass(SosEvent.class);
|
||||||
|
sosService.triggerSos(2L, req);
|
||||||
|
|
||||||
|
verify(sosEventRepository).save(captor.capture());
|
||||||
|
assertThat(captor.getValue().getTriggerType()).isEqualTo("MANUAL");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("triggerSos - ada pairing aktif: harus kirim FCM dan WebSocket ke guardian")
|
||||||
|
void triggerSos_activePairing_shouldNotifyGuardianViaFcmAndWebSocket() {
|
||||||
|
SosRequest req = new SosRequest();
|
||||||
|
req.setTriggerType("BUTTON");
|
||||||
|
req.setLat(-7.257);
|
||||||
|
req.setLng(112.752);
|
||||||
|
|
||||||
|
when(sosEventRepository.save(any(SosEvent.class))).thenReturn(savedSos);
|
||||||
|
when(userRepository.findById(2L)).thenReturn(Optional.of(user));
|
||||||
|
when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
|
||||||
|
.thenReturn(Optional.of(activePairing));
|
||||||
|
doNothing().when(activityLogService).createLog(any(), any(), any(), any());
|
||||||
|
|
||||||
|
sosService.triggerSos(2L, req);
|
||||||
|
|
||||||
|
verify(fcmService).sendHighPriority(
|
||||||
|
eq("guardian-fcm-token"),
|
||||||
|
contains("SOS ALERT"),
|
||||||
|
anyString(),
|
||||||
|
anyMap()
|
||||||
|
);
|
||||||
|
verify(locationBroadcaster).broadcastSos(eq(1L), any(SosEventResponse.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("triggerSos - user tidak ditemukan: harus throw ResourceNotFoundException")
|
||||||
|
void triggerSos_userNotFound_shouldThrowException() {
|
||||||
|
SosRequest req = new SosRequest();
|
||||||
|
req.setTriggerType("MANUAL");
|
||||||
|
|
||||||
|
when(sosEventRepository.save(any(SosEvent.class))).thenReturn(savedSos);
|
||||||
|
when(userRepository.findById(99L)).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> sosService.triggerSos(99L, req))
|
||||||
|
.isInstanceOf(ResourceNotFoundException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== acknowledgeSos TESTS =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("acknowledgeSos - SOS ditemukan: harus ubah status ke ACKNOWLEDGED")
|
||||||
|
void acknowledgeSos_sosFound_shouldChangeStatusToAcknowledged() {
|
||||||
|
when(sosEventRepository.findById(50L)).thenReturn(Optional.of(savedSos));
|
||||||
|
when(userRepository.findById(2L)).thenReturn(Optional.of(user));
|
||||||
|
when(sosEventRepository.save(any(SosEvent.class))).thenAnswer(inv -> inv.getArgument(0));
|
||||||
|
when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
|
||||||
|
.thenReturn(Optional.empty());
|
||||||
|
doNothing().when(activityLogService).createLog(any(), any(), any(), any());
|
||||||
|
|
||||||
|
SosEventResponse result = sosService.acknowledgeSos(1L, 50L);
|
||||||
|
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
assertThat(result.getStatus()).isEqualTo("ACKNOWLEDGED");
|
||||||
|
ArgumentCaptor<SosEvent> captor = ArgumentCaptor.forClass(SosEvent.class);
|
||||||
|
verify(sosEventRepository).save(captor.capture());
|
||||||
|
assertThat(captor.getValue().getStatus()).isEqualTo(SosStatus.ACKNOWLEDGED);
|
||||||
|
assertThat(captor.getValue().getAcknowledgedAt()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("acknowledgeSos - ada pairing user: harus kirim FCM ke user bahwa guardian sudah respon")
|
||||||
|
void acknowledgeSos_activePairingForUser_shouldNotifyUser() {
|
||||||
|
when(sosEventRepository.findById(50L)).thenReturn(Optional.of(savedSos));
|
||||||
|
when(userRepository.findById(2L)).thenReturn(Optional.of(user));
|
||||||
|
when(sosEventRepository.save(any(SosEvent.class))).thenAnswer(inv -> inv.getArgument(0));
|
||||||
|
when(pairingRelationRepository.findByUser_IdAndStatus(2L, PairingStatus.ACTIVE))
|
||||||
|
.thenReturn(Optional.of(activePairing));
|
||||||
|
doNothing().when(activityLogService).createLog(any(), any(), any(), any());
|
||||||
|
|
||||||
|
sosService.acknowledgeSos(1L, 50L);
|
||||||
|
|
||||||
|
verify(fcmService).sendToToken(
|
||||||
|
eq("user-fcm-token"),
|
||||||
|
contains("Guardian Merespons SOS"),
|
||||||
|
anyString(),
|
||||||
|
anyMap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("acknowledgeSos - SOS tidak ditemukan: harus throw ResourceNotFoundException")
|
||||||
|
void acknowledgeSos_sosNotFound_shouldThrowException() {
|
||||||
|
when(sosEventRepository.findById(999L)).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> sosService.acknowledgeSos(1L, 999L))
|
||||||
|
.isInstanceOf(ResourceNotFoundException.class)
|
||||||
|
.hasMessageContaining("SOS event tidak ditemukan");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== getSosEvents TESTS =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("getSosEvents - harus return halaman SOS milik user")
|
||||||
|
void getSosEvents_shouldReturnPagedSosEvents() {
|
||||||
|
Pageable pageable = PageRequest.of(0, 10);
|
||||||
|
Page<SosEvent> page = new PageImpl<>(List.of(savedSos), pageable, 1);
|
||||||
|
|
||||||
|
when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(2L, pageable)).thenReturn(page);
|
||||||
|
|
||||||
|
Page<SosEventResponse> result = sosService.getSosEvents(2L, pageable);
|
||||||
|
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
assertThat(result.getContent()).hasSize(1);
|
||||||
|
assertThat(result.getContent().get(0).getId()).isEqualTo(50L);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== getSosEventsForGuardian TESTS =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("getSosEventsForGuardian - ada pairing aktif: harus return SOS milik user yang dipair")
|
||||||
|
void getSosEventsForGuardian_activePairing_shouldReturnUserSosEvents() {
|
||||||
|
Pageable pageable = PageRequest.of(0, 10);
|
||||||
|
Page<SosEvent> page = new PageImpl<>(List.of(savedSos), pageable, 1);
|
||||||
|
|
||||||
|
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
|
||||||
|
.thenReturn(Optional.of(activePairing));
|
||||||
|
when(sosEventRepository.findByUserIdOrderByCreatedAtDesc(2L, pageable)).thenReturn(page);
|
||||||
|
|
||||||
|
Page<SosEventResponse> result = sosService.getSosEventsForGuardian(1L, pageable);
|
||||||
|
|
||||||
|
assertThat(result.getContent()).hasSize(1);
|
||||||
|
assertThat(result.getContent().get(0).getUserId()).isEqualTo(2L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("getSosEventsForGuardian - tidak ada pairing: harus throw ResourceNotFoundException")
|
||||||
|
void getSosEventsForGuardian_noPairing_shouldThrowException() {
|
||||||
|
when(pairingRelationRepository.findByGuardian_IdAndStatus(1L, PairingStatus.ACTIVE))
|
||||||
|
.thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> sosService.getSosEventsForGuardian(1L, PageRequest.of(0, 10)))
|
||||||
|
.isInstanceOf(ResourceNotFoundException.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user