package com.walkguide.controller; import com.fasterxml.jackson.databind.ObjectMapper; import com.walkguide.dto.request.*; import com.walkguide.dto.response.*; import com.walkguide.security.SecurityHelper; import com.walkguide.service.*; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import java.util.List; import java.util.Optional; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(GuardianController.class) @WithMockUser(username = "2", roles = "GUARDIAN") @DisplayName("GuardianController Unit Tests") class GuardianControllerTest { @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; @MockBean private GuardianDashboardService dashboardService; @MockBean private LocationService locationService; @MockBean private ActivityLogService activityLogService; @MockBean private ObstacleLogService obstacleLogService; @MockBean private NotificationService notificationService; @MockBean private SosService sosService; @MockBean private AiConfigService aiConfigService; @MockBean private VoiceCommandService voiceCommandService; @MockBean private HardwareShortcutService hardwareShortcutService; @MockBean private GeofenceService geofenceService; @MockBean private UserSettingsService userSettingsService; // ===== DASHBOARD ===== @Test @DisplayName("GET /api/v1/guardian/dashboard - harus return dashboard") void dashboard_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); when(dashboardService.getDashboard(2L)).thenReturn(new DashboardResponse()); mockMvc.perform(get("/api/v1/guardian/dashboard")) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.message").value("Dashboard Guardian")); } } // ===== USER LOCATION ===== @Test @DisplayName("GET /api/v1/guardian/user-location - harus return lokasi terakhir user") void userLocation_locationExists_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); when(locationService.getLastLocationForGuardian(2L)) .thenReturn(Optional.of(new LocationResponse())); mockMvc.perform(get("/api/v1/guardian/user-location")) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.message").value("Lokasi terakhir user")); } } @Test @DisplayName("GET /api/v1/guardian/user-location - belum ada lokasi harus return data null") void userLocation_noLocation_shouldReturnNullData() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); when(locationService.getLastLocationForGuardian(2L)).thenReturn(Optional.empty()); mockMvc.perform(get("/api/v1/guardian/user-location")) .andExpect(status().isOk()) .andExpect(jsonPath("$.data").isEmpty()); } } // ===== LOCATION HISTORY ===== @Test @DisplayName("GET /api/v1/guardian/location-history - harus return paginated history") void locationHistory_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); Page page = new PageImpl<>(List.of()); when(locationService.getLocationHistory(eq(2L), any())).thenReturn(page); mockMvc.perform(get("/api/v1/guardian/location-history") .param("page", "0").param("size", "20")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Riwayat lokasi")); } } // ===== ACTIVITY LOGS ===== @Test @DisplayName("GET /api/v1/guardian/activity-logs - harus return log aktivitas user") void activityLogs_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); Page page = new PageImpl<>(List.of()); when(activityLogService.getLogsForGuardian(eq(2L), any())).thenReturn(page); mockMvc.perform(get("/api/v1/guardian/activity-logs")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Log aktivitas user")); } } // ===== OBSTACLE LOGS ===== @Test @DisplayName("GET /api/v1/guardian/obstacle-logs - harus return log obstacle user") void obstacleLogs_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); Page page = new PageImpl<>(List.of()); when(obstacleLogService.getObstacleLogs(eq(2L), any())).thenReturn(page); mockMvc.perform(get("/api/v1/guardian/obstacle-logs")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Log obstacle user")); } } // ===== SEND NOTIFICATION ===== @Test @DisplayName("POST /api/v1/guardian/notifications/send - harus kirim notifikasi dan return response") void sendNotif_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); SendNotificationRequest req = new SendNotificationRequest(); NotificationResponse resp = new NotificationResponse(); when(notificationService.sendNotification(eq(2L), any())).thenReturn(resp); mockMvc.perform(post("/api/v1/guardian/notifications/send") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Notifikasi terkirim")); verify(notificationService).sendNotification(eq(2L), any(SendNotificationRequest.class)); } } // ===== SOS EVENTS ===== @Test @DisplayName("GET /api/v1/guardian/sos-events - harus return paginated SOS events") void sosEvents_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); Page page = new PageImpl<>(List.of()); when(sosService.getSosEventsForGuardian(eq(2L), any())).thenReturn(page); mockMvc.perform(get("/api/v1/guardian/sos-events")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("SOS events")); } } @Test @DisplayName("PUT /api/v1/guardian/sos/{id}/acknowledge - harus akui SOS") void acknowledgeSos_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); SosEventResponse resp = new SosEventResponse(); when(sosService.acknowledgeSos(2L, 10L)).thenReturn(resp); mockMvc.perform(put("/api/v1/guardian/sos/10/acknowledge") .with(csrf())) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("SOS diakui")); verify(sosService).acknowledgeSos(2L, 10L); } } // ===== AI CONFIG ===== @Test @DisplayName("GET /api/v1/guardian/ai-config - harus return AI config user") void getAiConfig_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); when(aiConfigService.getConfig(2L)).thenReturn(new AiConfigResponse()); mockMvc.perform(get("/api/v1/guardian/ai-config")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("AI config")); } } @Test @DisplayName("PUT /api/v1/guardian/ai-config - harus update AI config dan return response baru") void updateAiConfig_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); AiConfigUpdateRequest req = new AiConfigUpdateRequest(); AiConfigResponse resp = new AiConfigResponse(); when(aiConfigService.updateConfigByGuardian(eq(2L), any())).thenReturn(resp); mockMvc.perform(put("/api/v1/guardian/ai-config") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("AI config diperbarui")); verify(aiConfigService).updateConfigByGuardian(eq(2L), any(AiConfigUpdateRequest.class)); } } // ===== VOICE COMMANDS ===== @Test @DisplayName("GET /api/v1/guardian/voice-commands - harus return voice commands user") void getVoiceCommands_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); when(voiceCommandService.getAll(2L)).thenReturn(List.of()); mockMvc.perform(get("/api/v1/guardian/voice-commands")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Voice commands")); } } @Test @DisplayName("PUT /api/v1/guardian/voice-commands - harus update voice command") void updateVoiceCommand_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); VoiceCommandUpdateRequest req = new VoiceCommandUpdateRequest(); VoiceCommandResponse resp = new VoiceCommandResponse(); when(voiceCommandService.updateByGuardian(eq(2L), any())).thenReturn(resp); mockMvc.perform(put("/api/v1/guardian/voice-commands") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Voice command diperbarui")); } } // ===== SHORTCUTS ===== @Test @DisplayName("GET /api/v1/guardian/shortcuts - harus return hardware shortcuts") void getShortcuts_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); when(hardwareShortcutService.getAll(2L)).thenReturn(List.of()); mockMvc.perform(get("/api/v1/guardian/shortcuts")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Hardware shortcuts")); } } // ===== GEOFENCE ===== @Test @DisplayName("GET /api/v1/guardian/geofence - harus return konfigurasi geofence") void getGeofence_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); when(geofenceService.getConfig(2L)).thenReturn(new GeofenceResponse()); mockMvc.perform(get("/api/v1/guardian/geofence")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Geofence config")); } } @Test @DisplayName("PUT /api/v1/guardian/geofence - harus update geofence config") void updateGeofence_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); GeofenceConfigRequest req = new GeofenceConfigRequest(); GeofenceResponse resp = new GeofenceResponse(); when(geofenceService.updateConfig(eq(2L), any())).thenReturn(resp); mockMvc.perform(put("/api/v1/guardian/geofence") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Geofence diperbarui")); verify(geofenceService).updateConfig(eq(2L), any(GeofenceConfigRequest.class)); } } // ===== USER SETTINGS (dikelola Guardian) ===== @Test @DisplayName("GET /api/v1/guardian/user-settings - harus return settings user") void getUserSettings_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); when(userSettingsService.getSettings(2L)).thenReturn(new UserSettingsResponse()); mockMvc.perform(get("/api/v1/guardian/user-settings")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("User settings")); } } @Test @DisplayName("PUT /api/v1/guardian/user-settings - harus update user settings") void updateUserSettings_shouldReturn200() throws Exception { try (MockedStatic sh = mockStatic(SecurityHelper.class)) { sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L); UserSettingsUpdateRequest req = new UserSettingsUpdateRequest(); UserSettingsResponse resp = new UserSettingsResponse(); when(userSettingsService.updateSettings(eq(2L), any())).thenReturn(resp); mockMvc.perform(put("/api/v1/guardian/user-settings") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("User settings diperbarui")); } } }