2026-05-26 05:52:12 +07:00

272 lines
12 KiB
Java

package com.walkguide.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.walkguide.dto.request.InviteUserRequest;
import com.walkguide.dto.request.PairingResponseRequest;
import com.walkguide.dto.response.PairingStatusResponse;
import com.walkguide.security.SecurityHelper;
import com.walkguide.service.PairingService;
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.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Collection;
import java.util.List;
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(PairingController.class)
@DisplayName("PairingController Unit Tests")
class PairingControllerTest {
@MockBean private JwtAuthFilter jwtAuthFilter;
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private PairingService pairingService;
private PairingStatusResponse buildPairingStatus(String status) {
return PairingStatusResponse.builder()
.pairingId(1L)
.status(status)
.pairedWithName("Test Partner")
.pairedWithEmail("partner@test.com")
.uniqueUserId("ABC123")
.build();
}
// ===== INVITE =====
@Test
@WithMockUser(username = "2", roles = "GUARDIAN")
@DisplayName("POST /api/v1/shared/pairing/invite - Guardian undang User harus return 200")
void invite_asGuardian_shouldReturn200() throws Exception {
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L);
InviteUserRequest req = new InviteUserRequest();
req.setUniqueUserId("ABC123DEF456");
PairingStatusResponse resp = buildPairingStatus("PENDING");
when(pairingService.inviteUser(2L, "ABC123DEF456")).thenReturn(resp);
mockMvc.perform(post("/api/v1/shared/pairing/invite")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("Undangan dikirim ke user"))
.andExpect(jsonPath("$.data.status").value("PENDING"));
verify(pairingService).inviteUser(2L, "ABC123DEF456");
}
}
@Test
@WithMockUser(username = "2", roles = "GUARDIAN")
@DisplayName("POST /api/v1/shared/pairing/invite - uniqueUserId tidak ada harus return 500")
void invite_userNotFound_shouldReturn500() throws Exception {
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L);
InviteUserRequest req = new InviteUserRequest();
req.setPairingCode("BAD99999");
when(pairingService.inviteUser(2L, "BAD99999"))
.thenThrow(new RuntimeException("User dengan ID tersebut tidak ditemukan"));
mockMvc.perform(post("/api/v1/shared/pairing/invite")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req)))
.andExpect(status().isInternalServerError());
}
}
// ===== RESPOND =====
@Test
@WithMockUser(username = "1", roles = "USER")
@DisplayName("POST /api/v1/shared/pairing/respond - User terima pairing harus return 200")
void respond_accept_shouldReturn200WithAcceptMessage() throws Exception {
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
sh.when(SecurityHelper::getCurrentUserId).thenReturn(1L);
PairingResponseRequest req = new PairingResponseRequest();
req.setPairingId(5L);
req.setAccept(true);
PairingStatusResponse resp = buildPairingStatus("ACTIVE");
when(pairingService.respondToPairing(1L, 5L, true)).thenReturn(resp);
mockMvc.perform(post("/api/v1/shared/pairing/respond")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("Pairing diterima"))
.andExpect(jsonPath("$.data.status").value("ACTIVE"));
verify(pairingService).respondToPairing(1L, 5L, true);
}
}
@Test
@WithMockUser(username = "1", roles = "USER")
@DisplayName("POST /api/v1/shared/pairing/respond - User tolak pairing harus return 200 pesan ditolak")
void respond_reject_shouldReturn200WithRejectMessage() throws Exception {
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
sh.when(SecurityHelper::getCurrentUserId).thenReturn(1L);
PairingResponseRequest req = new PairingResponseRequest();
req.setPairingId(5L);
req.setAccept(false);
PairingStatusResponse resp = buildPairingStatus("REJECTED");
when(pairingService.respondToPairing(1L, 5L, false)).thenReturn(resp);
mockMvc.perform(post("/api/v1/shared/pairing/respond")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message").value("Pairing ditolak"))
.andExpect(jsonPath("$.data.status").value("REJECTED"));
verify(pairingService).respondToPairing(1L, 5L, false);
}
}
// ===== UNPAIR =====
@Test
@WithMockUser(username = "1", roles = "USER")
@DisplayName("DELETE /api/v1/shared/pairing/unpair - harus akhiri pairing dan return 200")
void unpair_shouldReturn200() throws Exception {
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
sh.when(SecurityHelper::getCurrentUserId).thenReturn(1L);
doNothing().when(pairingService).unpair(1L);
mockMvc.perform(delete("/api/v1/shared/pairing/unpair")
.with(csrf()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("Pairing diakhiri"));
verify(pairingService).unpair(1L);
}
}
@Test
@WithMockUser(username = "2", roles = "GUARDIAN")
@DisplayName("DELETE /api/v1/shared/pairing/unpair - Guardian juga bisa unpair")
void unpair_asGuardian_shouldReturn200() throws Exception {
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L);
doNothing().when(pairingService).unpair(2L);
mockMvc.perform(delete("/api/v1/shared/pairing/unpair")
.with(csrf()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message").value("Pairing diakhiri"));
verify(pairingService).unpair(2L);
}
}
@Test
@WithMockUser(username = "1", roles = "USER")
@DisplayName("DELETE /api/v1/shared/pairing/unpair - service throw harus return 500")
void unpair_serviceThrows_shouldReturn500() throws Exception {
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
sh.when(SecurityHelper::getCurrentUserId).thenReturn(1L);
doThrow(new RuntimeException("Tidak ada pairing aktif")).when(pairingService).unpair(1L);
mockMvc.perform(delete("/api/v1/shared/pairing/unpair")
.with(csrf()))
.andExpect(status().isInternalServerError());
}
}
// ===== STATUS =====
@Test
@WithMockUser(username = "1", roles = "USER")
@DisplayName("GET /api/v1/shared/pairing/status - User harus return status pairing")
void status_asUser_shouldReturn200() throws Exception {
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
sh.when(SecurityHelper::getCurrentUserId).thenReturn(1L);
sh.when(SecurityHelper::getCurrentUserRole).thenReturn("ROLE_USER");
PairingStatusResponse resp = buildPairingStatus("ACTIVE");
when(pairingService.getStatus(eq(1L), eq("ROLE_USER"))).thenReturn(resp);
mockMvc.perform(get("/api/v1/shared/pairing/status"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.message").value("Status pairing"))
.andExpect(jsonPath("$.data.status").value("ACTIVE"))
.andExpect(jsonPath("$.data.pairingId").value(1));
}
}
@Test
@WithMockUser(username = "2", roles = "GUARDIAN")
@DisplayName("GET /api/v1/shared/pairing/status - Guardian harus return status pairing dengan role GUARDIAN")
void status_asGuardian_shouldReturn200() throws Exception {
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
sh.when(SecurityHelper::getCurrentUserId).thenReturn(2L);
sh.when(SecurityHelper::getCurrentUserRole).thenReturn("ROLE_GUARDIAN");
PairingStatusResponse resp = buildPairingStatus("ACTIVE");
when(pairingService.getStatus(eq(2L), eq("ROLE_GUARDIAN"))).thenReturn(resp);
mockMvc.perform(get("/api/v1/shared/pairing/status"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message").value("Status pairing"));
}
}
@Test
@WithMockUser(username = "1", roles = "USER")
@DisplayName("GET /api/v1/shared/pairing/status - belum ada pairing harus return status NONE")
void status_noPairing_shouldReturnNoneStatus() throws Exception {
try (MockedStatic<SecurityHelper> sh = mockStatic(SecurityHelper.class)) {
sh.when(SecurityHelper::getCurrentUserId).thenReturn(1L);
sh.when(SecurityHelper::getCurrentUserRole).thenReturn("ROLE_USER");
PairingStatusResponse resp = PairingStatusResponse.builder()
.status("NONE")
.build();
when(pairingService.getStatus(eq(1L), anyString())).thenReturn(resp);
mockMvc.perform(get("/api/v1/shared/pairing/status"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.status").value("NONE"));
}
}
}