package com.walkguide.controller; import com.fasterxml.jackson.databind.ObjectMapper; import com.walkguide.dto.request.FcmTokenRequest; import com.walkguide.dto.request.LoginRequest; import com.walkguide.dto.request.RefreshTokenRequest; import com.walkguide.dto.request.RegisterRequest; import com.walkguide.dto.response.AuthDataResponse; import com.walkguide.security.SecurityHelper; import com.walkguide.service.AuthService; import org.junit.jupiter.api.BeforeEach; 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.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import com.walkguide.security.JwtAuthFilter; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; 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.*; @AutoConfigureMockMvc(addFilters = false) @WebMvcTest(AuthController.class) @DisplayName("AuthController Unit Tests") class AuthControllerTest { @MockBean private JwtAuthFilter jwtAuthFilter; @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; @MockBean private AuthService authService; private AuthDataResponse sampleAuthData; @BeforeEach void setUp() { sampleAuthData = AuthDataResponse.builder() .accessToken("access-token-xyz") .refreshToken("refresh-token-xyz") .role("ROLE_USER") .userId(1L) .displayName("Test User") .uniqueUserId("ABC123DEF456") .build(); } // ===== PING ===== @Test @DisplayName("GET /api/v1/auth/ping - harus return 200 pong") void ping_shouldReturn200WithPong() throws Exception { mockMvc.perform(get("/api/v1/auth/ping")) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.data").value("pong")) .andExpect(jsonPath("$.message").value("Server aktif")); } // ===== REGISTER ===== @Test @DisplayName("POST /api/v1/auth/register - valid request harus return 200 dengan token") void register_validRequest_shouldReturn200() throws Exception { RegisterRequest req = new RegisterRequest(); req.setEmail("new@test.com"); req.setPassword("password123"); req.setDisplayName("New User"); req.setRole("USER"); when(authService.register(any(RegisterRequest.class))).thenReturn(sampleAuthData); mockMvc.perform(post("/api/v1/auth/register") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.message").value("Registrasi berhasil")) .andExpect(jsonPath("$.data.accessToken").value("access-token-xyz")) .andExpect(jsonPath("$.data.role").value("ROLE_USER")); verify(authService).register(any(RegisterRequest.class)); } @Test @DisplayName("POST /api/v1/auth/register - email kosong harus return 400") void register_emptyEmail_shouldReturn400() throws Exception { RegisterRequest req = new RegisterRequest(); req.setEmail(""); req.setPassword("password123"); req.setDisplayName("New User"); req.setRole("USER"); mockMvc.perform(post("/api/v1/auth/register") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isBadRequest()); verify(authService, never()).register(any()); } @Test @DisplayName("POST /api/v1/auth/register - password kurang dari 6 karakter harus return 400") void register_shortPassword_shouldReturn400() throws Exception { RegisterRequest req = new RegisterRequest(); req.setEmail("user@test.com"); req.setPassword("abc"); req.setDisplayName("User"); req.setRole("USER"); mockMvc.perform(post("/api/v1/auth/register") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isBadRequest()); verify(authService, never()).register(any()); } @Test @DisplayName("POST /api/v1/auth/register - format email tidak valid harus return 400") void register_invalidEmailFormat_shouldReturn400() throws Exception { RegisterRequest req = new RegisterRequest(); req.setEmail("bukan-email"); req.setPassword("password123"); req.setDisplayName("User"); req.setRole("USER"); mockMvc.perform(post("/api/v1/auth/register") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isBadRequest()); verify(authService, never()).register(any()); } // ===== LOGIN ===== @Test @DisplayName("POST /api/v1/auth/login - valid credentials harus return 200 dengan token") void login_validCredentials_shouldReturn200() throws Exception { LoginRequest req = new LoginRequest(); req.setEmail("user@test.com"); req.setPassword("password123"); when(authService.login(any(LoginRequest.class))).thenReturn(sampleAuthData); mockMvc.perform(post("/api/v1/auth/login") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.message").value("Login berhasil")) .andExpect(jsonPath("$.data.accessToken").value("access-token-xyz")) .andExpect(jsonPath("$.data.userId").value(1)); verify(authService).login(any(LoginRequest.class)); } @Test @DisplayName("POST /api/v1/auth/login - service throw RuntimeException harus return 500") void login_serviceThrows_shouldReturn500() throws Exception { LoginRequest req = new LoginRequest(); req.setEmail("user@test.com"); req.setPassword("wrong"); when(authService.login(any(LoginRequest.class))) .thenThrow(new RuntimeException("Password salah")); mockMvc.perform(post("/api/v1/auth/login") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isInternalServerError()); } // ===== REFRESH ===== @Test @DisplayName("POST /api/v1/auth/refresh - token valid harus return token baru") void refresh_validToken_shouldReturn200() throws Exception { RefreshTokenRequest req = new RefreshTokenRequest(); req.setRefreshToken("valid-refresh-token"); AuthDataResponse newData = AuthDataResponse.builder() .accessToken("new-access-token") .refreshToken("valid-refresh-token") .role("ROLE_USER") .userId(1L) .build(); when(authService.refreshToken("valid-refresh-token")).thenReturn(newData); mockMvc.perform(post("/api/v1/auth/refresh") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.message").value("Token diperbarui")) .andExpect(jsonPath("$.data.accessToken").value("new-access-token")); verify(authService).refreshToken("valid-refresh-token"); } // ===== LOGOUT ===== @Test @DisplayName("POST /api/v1/auth/logout - authenticated user harus return 200") @WithMockUser(username = "1", roles = "USER") void logout_authenticated_shouldReturn200() throws Exception { try (MockedStatic securityHelper = mockStatic(SecurityHelper.class)) { securityHelper.when(SecurityHelper::getCurrentUserId).thenReturn(1L); doNothing().when(authService).logout(1L); mockMvc.perform(post("/api/v1/auth/logout") .with(csrf())) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.message").value("Logout berhasil")); verify(authService).logout(1L); } } // ===== UPDATE FCM TOKEN ===== @Test @DisplayName("PUT /api/v1/auth/fcm-token - authenticated user harus update FCM token") @WithMockUser(username = "1", roles = "USER") void updateFcmToken_authenticated_shouldReturn200() throws Exception { try (MockedStatic securityHelper = mockStatic(SecurityHelper.class)) { securityHelper.when(SecurityHelper::getCurrentUserId).thenReturn(1L); FcmTokenRequest req = new FcmTokenRequest(); req.setFcmToken("fcm-token-new-value"); doNothing().when(authService).updateFcmToken(eq(1L), eq("fcm-token-new-value")); mockMvc.perform(put("/api/v1/auth/fcm-token") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(req))) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.message").value("FCM token diperbarui")); verify(authService).updateFcmToken(1L, "fcm-token-new-value"); } } }