package com.mydreamjob.messaging; import com.mydreamjob.common.exception.BusinessException; import com.mydreamjob.common.exception.ErrorCode; import com.mydreamjob.messaging.dto.CreateConversationRequest; import com.mydreamjob.messaging.dto.MessagePageResponse; import com.mydreamjob.messaging.dto.SendMessageRequest; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import java.util.List; import java.util.Map; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("integration-test") class MessagingControllerTest { @Autowired private MockMvc mockMvc; @Autowired private com.mydreamjob.auth.JwtTokenProvider jwtTokenProvider; @MockBean private MessagingService messagingService; @MockBean private RedisTemplate redisTemplate; private final ObjectMapper mapper = new ObjectMapper(); @BeforeEach @SuppressWarnings("unchecked") void setUp() { ValueOperations ops = mock(ValueOperations.class); when(redisTemplate.opsForValue()).thenReturn(ops); when(redisTemplate.hasKey(any())).thenReturn(false); } private String token() { return "Bearer " + jwtTokenProvider.generateAccessToken(100L, "1", Map.of("roles", "STUDENT")); } @Test void listConversations_shouldReturn200() throws Exception { when(messagingService.listConversations(100L)).thenReturn(List.of()); mockMvc.perform(get("/conversations").header("Authorization", token())) .andExpect(status().isOk()).andExpect(jsonPath("$.code").value(200)); } @Test void getConversation_shouldReturn200() throws Exception { when(messagingService.getConversation(1L, 100L)).thenReturn(Map.of("id", 1, "type", "DIRECT")); mockMvc.perform(get("/conversations/1").header("Authorization", token())) .andExpect(status().isOk()).andExpect(jsonPath("$.data.id").value(1)); } @Test void createConversation_shouldReturn200() throws Exception { CreateConversationRequest req = new CreateConversationRequest(); req.setParticipantUserId(200L); when(messagingService.createOrGetDirectConversation(eq(100L), any())) .thenReturn(Map.of("id", 2, "type", "DIRECT")); mockMvc.perform(post("/conversations").header("Authorization", token()) .contentType(MediaType.APPLICATION_JSON).content(mapper.writeValueAsString(req))) .andExpect(status().isOk()).andExpect(jsonPath("$.data.id").value(2)); } @Test void sendMessage_shouldReturn200() throws Exception { SendMessageRequest req = new SendMessageRequest(); req.setConversationId(1L); req.setContent("Hello"); when(messagingService.sendMessage(eq(100L), any())).thenReturn(Map.of("id", 1, "content", "Hello")); mockMvc.perform(post("/conversations/messages").header("Authorization", token()) .contentType(MediaType.APPLICATION_JSON).content(mapper.writeValueAsString(req))) .andExpect(status().isOk()).andExpect(jsonPath("$.data.content").value("Hello")); } @Test void listMessages_shouldReturn200() throws Exception { when(messagingService.listMessages(eq(1L), eq(100L), eq(0), eq(20))) .thenReturn(MessagePageResponse.builder().items(List.of()).total(0).page(0).size(20).totalPages(0).build()); mockMvc.perform(get("/conversations/1/messages").header("Authorization", token())) .andExpect(status().isOk()).andExpect(jsonPath("$.code").value(200)); } @Test void markRead_shouldReturn200() throws Exception { doNothing().when(messagingService).markRead(1L, 100L); mockMvc.perform(post("/conversations/1/read").header("Authorization", token())) .andExpect(status().isOk()).andExpect(jsonPath("$.code").value(200)); } @Test void createConversation_noToken_shouldReturn401() throws Exception { String body = mapper.writeValueAsString(Map.of("participantUserId", 200)); mockMvc.perform(post("/conversations").contentType(MediaType.APPLICATION_JSON).content(body)) .andExpect(status().isUnauthorized()); } // ── 会话不存在 → 404/8305;非成员 → 403/2002 ──────────────── @Test void getConversation_notFound_shouldReturn404() throws Exception { when(messagingService.getConversation(1L, 100L)) .thenThrow(new BusinessException(ErrorCode.CONVERSATION_NOT_FOUND)); mockMvc.perform(get("/conversations/1").header("Authorization", token())) .andExpect(status().isNotFound()) .andExpect(jsonPath("$.code").value(8305)); } @Test void getConversation_asNonMember_shouldReturn403() throws Exception { when(messagingService.getConversation(1L, 100L)) .thenThrow(new BusinessException(ErrorCode.FORBIDDEN)); mockMvc.perform(get("/conversations/1").header("Authorization", token())) .andExpect(status().isForbidden()) .andExpect(jsonPath("$.code").value(2002)); } }