From def12a1bdbfd2506341e4585ff76df2767072fb9 Mon Sep 17 00:00:00 2001 From: 5803024019 Date: Fri, 15 May 2026 22:12:50 +0700 Subject: [PATCH] test: add ObstacleLogServiceTest unit test --- .../service/ObstacleLogServiceTest.java | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 walkguide-backend/demo/src/test/java/com/walkguide/service/ObstacleLogServiceTest.java diff --git a/walkguide-backend/demo/src/test/java/com/walkguide/service/ObstacleLogServiceTest.java b/walkguide-backend/demo/src/test/java/com/walkguide/service/ObstacleLogServiceTest.java new file mode 100644 index 0000000..32936b3 --- /dev/null +++ b/walkguide-backend/demo/src/test/java/com/walkguide/service/ObstacleLogServiceTest.java @@ -0,0 +1,212 @@ +package com.walkguide.service; + +import com.walkguide.dto.request.ObstacleLogRequest; +import com.walkguide.dto.response.ObstacleLogResponse; +import com.walkguide.entity.ObstacleLog; +import com.walkguide.entity.User; +import com.walkguide.enums.ActivityLogType; +import com.walkguide.exception.ResourceNotFoundException; +import com.walkguide.repository.ObstacleLogRepository; +import com.walkguide.repository.UserRepository; +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("ObstacleLogService Unit Tests") +class ObstacleLogServiceTest { + + @Mock + ObstacleLogRepository obstacleLogRepository; + + @Mock + UserRepository userRepository; + + @Mock + ActivityLogService activityLogService; + + @InjectMocks + ObstacleLogService obstacleLogService; + + private User sampleUser; + private ObstacleLogRequest sampleRequest; + + @BeforeEach + void setUp() { + sampleUser = User.builder() + .id(5L) + .email("user@test.com") + .role("ROLE_USER") + .displayName("Test User") + .build(); + + sampleRequest = new ObstacleLogRequest(); + sampleRequest.setLabel("Sepeda motor"); + sampleRequest.setConfidence(0.92); + sampleRequest.setDirection("kiri"); + sampleRequest.setEstimatedDist("1.5 meter"); + sampleRequest.setLat(-7.2575); + sampleRequest.setLng(112.7521); + } + + // ===== saveObstacle TESTS ===== + + @Test + @DisplayName("saveObstacle - harus simpan log dan return ObstacleLogResponse yang benar") + void saveObstacle_validRequest_shouldSaveAndReturnResponse() { + ObstacleLog savedLog = ObstacleLog.builder() + .id(1L) + .userId(5L) + .label("Sepeda motor") + .confidence(0.92) + .direction("kiri") + .estimatedDist("1.5 meter") + .lat(-7.2575) + .lng(112.7521) + .createdAt(LocalDateTime.now()) + .build(); + + when(obstacleLogRepository.save(any(ObstacleLog.class))).thenReturn(savedLog); + when(userRepository.findById(5L)).thenReturn(Optional.of(sampleUser)); + + ObstacleLogResponse result = obstacleLogService.saveObstacle(5L, sampleRequest); + + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(1L); + assertThat(result.getLabel()).isEqualTo("Sepeda motor"); + assertThat(result.getConfidence()).isEqualTo(0.92); + assertThat(result.getDirection()).isEqualTo("kiri"); + assertThat(result.getEstimatedDist()).isEqualTo("1.5 meter"); + assertThat(result.getLat()).isEqualTo(-7.2575); + assertThat(result.getLng()).isEqualTo(112.7521); + } + + @Test + @DisplayName("saveObstacle - harus simpan ObstacleLog dengan data dari request") + void saveObstacle_shouldPersistCorrectData() { + when(obstacleLogRepository.save(any(ObstacleLog.class))) + .thenAnswer(inv -> { + ObstacleLog log = inv.getArgument(0); + log = ObstacleLog.builder() + .id(10L).userId(log.getUserId()).label(log.getLabel()) + .confidence(log.getConfidence()).direction(log.getDirection()) + .estimatedDist(log.getEstimatedDist()) + .lat(log.getLat()).lng(log.getLng()).build(); + return log; + }); + when(userRepository.findById(5L)).thenReturn(Optional.of(sampleUser)); + + obstacleLogService.saveObstacle(5L, sampleRequest); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ObstacleLog.class); + verify(obstacleLogRepository).save(captor.capture()); + + ObstacleLog captured = captor.getValue(); + assertThat(captured.getUserId()).isEqualTo(5L); + assertThat(captured.getLabel()).isEqualTo("Sepeda motor"); + assertThat(captured.getConfidence()).isEqualTo(0.92); + assertThat(captured.getDirection()).isEqualTo("kiri"); + } + + @Test + @DisplayName("saveObstacle - harus mencatat OBSTACLE_DETECTED ke ActivityLog") + void saveObstacle_shouldCreateActivityLog() { + when(obstacleLogRepository.save(any(ObstacleLog.class))) + .thenReturn(ObstacleLog.builder().id(1L).label("Sepeda motor").direction("kiri").build()); + when(userRepository.findById(5L)).thenReturn(Optional.of(sampleUser)); + + obstacleLogService.saveObstacle(5L, sampleRequest); + + verify(activityLogService).createLog( + eq(sampleUser), + eq(ActivityLogType.OBSTACLE_DETECTED), + contains("Sepeda motor"), + anyString() + ); + } + + @Test + @DisplayName("saveObstacle - metadata ActivityLog harus mengandung label, direction, dan confidence") + void saveObstacle_activityLogMeta_shouldContainObstacleDetails() { + when(obstacleLogRepository.save(any(ObstacleLog.class))) + .thenReturn(ObstacleLog.builder().id(1L).label("Lubang").direction("depan").build()); + when(userRepository.findById(5L)).thenReturn(Optional.of(sampleUser)); + + sampleRequest.setLabel("Lubang"); + sampleRequest.setDirection("depan"); + sampleRequest.setConfidence(0.85); + obstacleLogService.saveObstacle(5L, sampleRequest); + + ArgumentCaptor metaCaptor = ArgumentCaptor.forClass(String.class); + verify(activityLogService).createLog(any(), any(), any(), metaCaptor.capture()); + + String meta = metaCaptor.getValue(); + assertThat(meta).contains("Lubang"); + assertThat(meta).contains("depan"); + assertThat(meta).contains("0.85"); + } + + @Test + @DisplayName("saveObstacle - user tidak ditemukan harus throw ResourceNotFoundException") + void saveObstacle_userNotFound_shouldThrow() { + when(obstacleLogRepository.save(any(ObstacleLog.class))) + .thenReturn(ObstacleLog.builder().id(1L).build()); + when(userRepository.findById(99L)).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> obstacleLogService.saveObstacle(99L, sampleRequest)) + .isInstanceOf(ResourceNotFoundException.class) + .hasMessageContaining("User tidak ditemukan"); + } + + // ===== getObstacleLogs TESTS ===== + + @Test + @DisplayName("getObstacleLogs - harus return page dari ObstacleLogResponse") + void getObstacleLogs_shouldReturnPagedResponse() { + ObstacleLog log1 = ObstacleLog.builder().id(1L).userId(5L).label("Trotoar").confidence(0.80) + .direction("kanan").estimatedDist("2 meter").lat(-7.25).lng(112.75).build(); + ObstacleLog log2 = ObstacleLog.builder().id(2L).userId(5L).label("Pohon").confidence(0.95) + .direction("depan").estimatedDist("1 meter").lat(-7.26).lng(112.76).build(); + + Page page = new PageImpl<>(List.of(log1, log2)); + Pageable pageable = PageRequest.of(0, 10); + + when(obstacleLogRepository.findByUserIdOrderByCreatedAtDesc(5L, pageable)).thenReturn(page); + + Page result = obstacleLogService.getObstacleLogs(5L, pageable); + + assertThat(result.getContent()).hasSize(2); + assertThat(result.getContent().get(0).getLabel()).isEqualTo("Trotoar"); + assertThat(result.getContent().get(1).getLabel()).isEqualTo("Pohon"); + } + + @Test + @DisplayName("getObstacleLogs - halaman kosong harus return page kosong tanpa error") + void getObstacleLogs_emptyPage_shouldReturnEmptyPage() { + Pageable pageable = PageRequest.of(0, 10); + when(obstacleLogRepository.findByUserIdOrderByCreatedAtDesc(5L, pageable)) + .thenReturn(Page.empty()); + + Page result = obstacleLogService.getObstacleLogs(5L, pageable); + + assertThat(result.getContent()).isEmpty(); + assertThat(result.getTotalElements()).isZero(); + } +} \ No newline at end of file