Skip to content

Commit e16161e

Browse files
authored
Merge branch 'dev' into Test/128
2 parents ed8c78e + b5fd217 commit e16161e

File tree

8 files changed

+1488
-3
lines changed

8 files changed

+1488
-3
lines changed

src/main/java/com/back/global/websocket/controller/WebSocketApiController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
import java.util.Map;
1515

1616
@RestController
17-
@RequestMapping("api/ws")
1817
@RequiredArgsConstructor
18+
@RequestMapping("api/ws")
1919
@Tag(name = "WebSocket REST API", description = "WebSocket 서버 상태 확인 + 실시간 연결 정보 제공 API")
2020
public class WebSocketApiController {
2121

src/main/java/com/back/global/websocket/webrtc/controller/WebRTCApiController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
@Slf4j
1717
@RestController
18-
@RequestMapping("/api/webrtc")
1918
@RequiredArgsConstructor
19+
@RequestMapping("/api/webrtc")
2020
@Tag(name = "WebRTC API", description = "WebRTC 시그널링 및 ICE 서버 관련 REST API")
2121
public class WebRTCApiController {
2222

src/main/java/com/back/global/websocket/webrtc/controller/WebRTCSignalingController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818

1919
import java.security.Principal;
2020

21+
@Slf4j
2122
@Controller
2223
@RequiredArgsConstructor
23-
@Slf4j
2424
public class WebRTCSignalingController {
2525

2626
private final SimpMessagingTemplate messagingTemplate;
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
package com.back.global.websocket.controller;
2+
3+
import com.back.global.websocket.service.WebSocketSessionManager;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import org.junit.jupiter.api.DisplayName;
6+
import org.junit.jupiter.api.Nested;
7+
import org.junit.jupiter.api.Test;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
10+
import org.springframework.boot.test.context.SpringBootTest;
11+
import org.springframework.http.MediaType;
12+
import org.springframework.security.test.context.support.WithMockUser;
13+
import org.springframework.test.context.bean.override.mockito.MockitoBean;
14+
import org.springframework.test.web.servlet.MockMvc;
15+
import org.springframework.test.web.servlet.MvcResult;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.mockito.BDDMockito.given;
19+
import static org.mockito.Mockito.verify;
20+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
21+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
22+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
23+
24+
@WithMockUser
25+
@SpringBootTest
26+
@AutoConfigureMockMvc
27+
@DisplayName("WebSocket REST API 컨트롤러")
28+
class WebSocketApiControllerTest {
29+
30+
@Autowired
31+
private MockMvc mockMvc;
32+
33+
@Autowired
34+
private ObjectMapper objectMapper;
35+
36+
@MockitoBean
37+
private WebSocketSessionManager sessionManager;
38+
39+
@Nested
40+
@DisplayName("헬스체크")
41+
class HealthCheckTest {
42+
43+
@Test
44+
@DisplayName("정상 - 서비스 상태 확인")
45+
void t1() throws Exception {
46+
// given
47+
long totalOnlineUsers = 5L;
48+
given(sessionManager.getTotalOnlineUserCount()).willReturn(totalOnlineUsers);
49+
50+
// when & then
51+
mockMvc.perform(get("/api/ws/health")
52+
.contentType(MediaType.APPLICATION_JSON))
53+
.andDo(print())
54+
.andExpect(status().isOk())
55+
.andExpect(jsonPath("$.code").value("SUCCESS_200"))
56+
.andExpect(jsonPath("$.message").value("WebSocket 서비스가 정상 동작중입니다."))
57+
.andExpect(jsonPath("$.success").value(true))
58+
.andExpect(jsonPath("$.data.service").value("WebSocket"))
59+
.andExpect(jsonPath("$.data.status").value("running"))
60+
.andExpect(jsonPath("$.data.timestamp").exists())
61+
.andExpect(jsonPath("$.data.sessionTTL").value("10분 (Heartbeat 방식)"))
62+
.andExpect(jsonPath("$.data.heartbeatInterval").value("5분"))
63+
.andExpect(jsonPath("$.data.totalOnlineUsers").value(totalOnlineUsers))
64+
.andExpect(jsonPath("$.data.endpoints").exists())
65+
.andExpect(jsonPath("$.data.endpoints.websocket").value("/ws"))
66+
.andExpect(jsonPath("$.data.endpoints.heartbeat").value("/app/heartbeat"))
67+
.andExpect(jsonPath("$.data.endpoints.activity").value("/app/activity"));
68+
69+
verify(sessionManager).getTotalOnlineUserCount();
70+
}
71+
72+
@Test
73+
@DisplayName("정상 - 온라인 유저 0명")
74+
void t2() throws Exception {
75+
// given
76+
given(sessionManager.getTotalOnlineUserCount()).willReturn(0L);
77+
78+
// when & then
79+
mockMvc.perform(get("/api/ws/health")
80+
.contentType(MediaType.APPLICATION_JSON))
81+
.andDo(print())
82+
.andExpect(status().isOk())
83+
.andExpect(jsonPath("$.data.totalOnlineUsers").value(0));
84+
85+
verify(sessionManager).getTotalOnlineUserCount();
86+
}
87+
88+
@Test
89+
@DisplayName("정상 - 응답 구조 검증")
90+
void t3() throws Exception {
91+
// given
92+
given(sessionManager.getTotalOnlineUserCount()).willReturn(10L);
93+
94+
// when & then
95+
MvcResult result = mockMvc.perform(get("/api/ws/health")
96+
.contentType(MediaType.APPLICATION_JSON))
97+
.andDo(print())
98+
.andExpect(status().isOk())
99+
.andReturn();
100+
101+
String content = result.getResponse().getContentAsString();
102+
103+
assertThat(content).contains("service");
104+
assertThat(content).contains("status");
105+
assertThat(content).contains("timestamp");
106+
assertThat(content).contains("sessionTTL");
107+
assertThat(content).contains("heartbeatInterval");
108+
assertThat(content).contains("totalOnlineUsers");
109+
assertThat(content).contains("endpoints");
110+
}
111+
112+
@Test
113+
@DisplayName("정상 - 엔드포인트 정보 포함")
114+
void t4() throws Exception {
115+
// given
116+
given(sessionManager.getTotalOnlineUserCount()).willReturn(3L);
117+
118+
// when & then
119+
mockMvc.perform(get("/api/ws/health"))
120+
.andExpect(status().isOk())
121+
.andExpect(jsonPath("$.data.endpoints.websocket").exists())
122+
.andExpect(jsonPath("$.data.endpoints.heartbeat").exists())
123+
.andExpect(jsonPath("$.data.endpoints.activity").exists())
124+
.andExpect(jsonPath("$.data.endpoints.joinRoom").exists())
125+
.andExpect(jsonPath("$.data.endpoints.leaveRoom").exists());
126+
}
127+
128+
@Test
129+
@DisplayName("정상 - Content-Type 없이도 동작")
130+
void t5() throws Exception {
131+
// given
132+
given(sessionManager.getTotalOnlineUserCount()).willReturn(1L);
133+
134+
// when & then
135+
mockMvc.perform(get("/api/ws/health"))
136+
.andExpect(status().isOk())
137+
.andExpect(jsonPath("$.success").value(true));
138+
}
139+
}
140+
141+
@Nested
142+
@DisplayName("연결 정보 조회")
143+
class ConnectionInfoTest {
144+
145+
@Test
146+
@DisplayName("정상 - 연결 정보 조회")
147+
void t6() throws Exception {
148+
// when & then
149+
mockMvc.perform(get("/api/ws/info")
150+
.contentType(MediaType.APPLICATION_JSON))
151+
.andDo(print())
152+
.andExpect(status().isOk())
153+
.andExpect(jsonPath("$.code").value("SUCCESS_200"))
154+
.andExpect(jsonPath("$.message").value("WebSocket 연결 정보"))
155+
.andExpect(jsonPath("$.success").value(true))
156+
.andExpect(jsonPath("$.data.websocketUrl").value("/ws"))
157+
.andExpect(jsonPath("$.data.sockjsSupport").value(true))
158+
.andExpect(jsonPath("$.data.stompVersion").value("1.2"))
159+
.andExpect(jsonPath("$.data.heartbeatInterval").value("5분"))
160+
.andExpect(jsonPath("$.data.sessionTTL").value("10분"));
161+
}
162+
163+
@Test
164+
@DisplayName("정상 - 구독 토픽 정보 포함")
165+
void t7() throws Exception {
166+
// when & then
167+
mockMvc.perform(get("/api/ws/info")
168+
.contentType(MediaType.APPLICATION_JSON))
169+
.andDo(print())
170+
.andExpect(status().isOk())
171+
.andExpect(jsonPath("$.data.subscribeTopics").exists())
172+
.andExpect(jsonPath("$.data.subscribeTopics.roomChat").value("/topic/rooms/{roomId}/chat"))
173+
.andExpect(jsonPath("$.data.subscribeTopics.privateMessage").value("/user/queue/messages"))
174+
.andExpect(jsonPath("$.data.subscribeTopics.notifications").value("/user/queue/notifications"));
175+
}
176+
177+
@Test
178+
@DisplayName("정상 - 전송 목적지 정보 포함")
179+
void t8() throws Exception {
180+
// when & then
181+
mockMvc.perform(get("/api/ws/info")
182+
.contentType(MediaType.APPLICATION_JSON))
183+
.andDo(print())
184+
.andExpect(status().isOk())
185+
.andExpect(jsonPath("$.data.sendDestinations").exists())
186+
.andExpect(jsonPath("$.data.sendDestinations.heartbeat").value("/app/heartbeat"))
187+
.andExpect(jsonPath("$.data.sendDestinations.activity").value("/app/activity"))
188+
.andExpect(jsonPath("$.data.sendDestinations.roomChat").value("/app/rooms/{roomId}/chat"));
189+
}
190+
191+
@Test
192+
@DisplayName("정상 - 응답 구조 검증")
193+
void t9() throws Exception {
194+
// when & then
195+
MvcResult result = mockMvc.perform(get("/api/ws/info")
196+
.contentType(MediaType.APPLICATION_JSON))
197+
.andDo(print())
198+
.andExpect(status().isOk())
199+
.andReturn();
200+
201+
String content = result.getResponse().getContentAsString();
202+
203+
assertThat(content).contains("websocketUrl");
204+
assertThat(content).contains("sockjsSupport");
205+
assertThat(content).contains("stompVersion");
206+
assertThat(content).contains("heartbeatInterval");
207+
assertThat(content).contains("sessionTTL");
208+
assertThat(content).contains("subscribeTopics");
209+
assertThat(content).contains("sendDestinations");
210+
}
211+
212+
@Test
213+
@DisplayName("정상 - SockJS 지원 확인")
214+
void t10() throws Exception {
215+
// when & then
216+
mockMvc.perform(get("/api/ws/info"))
217+
.andExpect(status().isOk())
218+
.andExpect(jsonPath("$.data.sockjsSupport").value(true))
219+
.andExpect(jsonPath("$.data.stompVersion").value("1.2"));
220+
}
221+
222+
@Test
223+
@DisplayName("정상 - Content-Type 없이도 동작")
224+
void t11() throws Exception {
225+
// when & then
226+
mockMvc.perform(get("/api/ws/info"))
227+
.andExpect(status().isOk())
228+
.andExpect(jsonPath("$.success").value(true));
229+
}
230+
}
231+
232+
@Nested
233+
@DisplayName("API 엔드포인트")
234+
class EndpointTest {
235+
236+
@Test
237+
@DisplayName("정상 - 모든 엔드포인트 JSON 응답")
238+
void t12() throws Exception {
239+
// given
240+
given(sessionManager.getTotalOnlineUserCount()).willReturn(0L);
241+
242+
// when & then
243+
mockMvc.perform(get("/api/ws/health"))
244+
.andExpect(status().isOk())
245+
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON));
246+
247+
mockMvc.perform(get("/api/ws/info"))
248+
.andExpect(status().isOk())
249+
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON));
250+
}
251+
252+
@Test
253+
@DisplayName("정상 - RsData 구조 일관성")
254+
void t13() throws Exception {
255+
// given
256+
given(sessionManager.getTotalOnlineUserCount()).willReturn(0L);
257+
258+
// when & then - health
259+
mockMvc.perform(get("/api/ws/health"))
260+
.andExpect(status().isOk())
261+
.andExpect(jsonPath("$.code").exists())
262+
.andExpect(jsonPath("$.message").exists())
263+
.andExpect(jsonPath("$.success").exists())
264+
.andExpect(jsonPath("$.data").exists());
265+
266+
// when & then - info
267+
mockMvc.perform(get("/api/ws/info"))
268+
.andExpect(status().isOk())
269+
.andExpect(jsonPath("$.code").exists())
270+
.andExpect(jsonPath("$.message").exists())
271+
.andExpect(jsonPath("$.success").exists())
272+
.andExpect(jsonPath("$.data").exists());
273+
}
274+
}
275+
}

0 commit comments

Comments
 (0)