1+ package com .back .web7_9_codecrete_be .domain .users .controller ;
2+
3+ import com .back .web7_9_codecrete_be .domain .auth .service .TokenService ;
4+ import com .back .web7_9_codecrete_be .domain .users .dto .response .UserResponse ;
5+ import com .back .web7_9_codecrete_be .domain .users .entity .User ;
6+ import com .back .web7_9_codecrete_be .domain .users .service .UserService ;
7+ import com .back .web7_9_codecrete_be .global .error .code .UserErrorCode ;
8+ import com .back .web7_9_codecrete_be .global .error .exception .BusinessException ;
9+ import com .back .web7_9_codecrete_be .global .rq .Rq ;
10+ import com .fasterxml .jackson .databind .ObjectMapper ;
11+ import org .junit .jupiter .api .BeforeEach ;
12+ import org .junit .jupiter .api .DisplayName ;
13+ import org .junit .jupiter .api .Test ;
14+ import org .springframework .beans .factory .annotation .Autowired ;
15+ import org .springframework .boot .test .autoconfigure .web .servlet .AutoConfigureMockMvc ;
16+ import org .springframework .boot .test .context .SpringBootTest ;
17+ import org .springframework .boot .test .mock .mockito .MockBean ;
18+ import org .springframework .http .MediaType ;
19+ import org .springframework .mock .web .MockMultipartFile ;
20+ import org .springframework .security .test .context .support .WithMockUser ;
21+ import org .springframework .test .context .ActiveProfiles ;
22+ import org .springframework .test .web .servlet .MockMvc ;
23+ import org .springframework .transaction .annotation .Transactional ;
24+
25+ import static org .mockito .ArgumentMatchers .any ;
26+ import static org .mockito .BDDMockito .given ;
27+ import static org .mockito .Mockito .*;
28+ import static org .springframework .security .test .web .servlet .request .SecurityMockMvcRequestPostProcessors .csrf ;
29+ import static org .springframework .test .web .servlet .request .MockMvcRequestBuilders .*;
30+ import static org .springframework .test .web .servlet .result .MockMvcResultHandlers .print ;
31+ import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .jsonPath ;
32+ import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .status ;
33+
34+ @ SpringBootTest
35+ @ AutoConfigureMockMvc
36+ @ ActiveProfiles ("test" )
37+ @ Transactional
38+ class UserControllerTest {
39+
40+ @ Autowired
41+ private MockMvc mockMvc ;
42+
43+ @ Autowired
44+ private ObjectMapper objectMapper ;
45+
46+ @ MockBean
47+ private UserService userService ;
48+
49+ @ MockBean
50+ private TokenService tokenService ;
51+
52+ @ MockBean
53+ private Rq rq ;
54+
55+ private User mockUser ;
56+ private UserResponse mockUserResponse ;
57+
58+ @ BeforeEach
59+ void setUp () {
60+ mockUser = User .builder ()
61+ .email ("test@example.com" )
62+ .nickname ("testUser" )
63+ .build ();
64+
65+ mockUserResponse = UserResponse .builder ()
66+ .email ("test@example.com" )
67+ .nickname ("testUser" )
68+ .profileImageUrl ("https://example.com/profile.jpg" )
69+ .build ();
70+
71+ // Rq가 사용될 때 가짜 유저 반환 설정
72+ lenient ().when (rq .getUser ()).thenReturn (mockUser );
73+ }
74+
75+ // 성공 테스트 시나리오
76+
77+ @ Test
78+ @ WithMockUser
79+ @ DisplayName ("내 정보 조회 성공" )
80+ void getMyInfo_Success () throws Exception {
81+ given (userService .getMyInfo (any ())).willReturn (mockUserResponse );
82+
83+ mockMvc .perform (get ("/api/v1/users/me" ))
84+ .andDo (print ())
85+ .andExpect (status ().isOk ())
86+ .andExpect (jsonPath ("$.resultCode" ).value ("OK" ))
87+ .andExpect (jsonPath ("$.msg" ).value ("사용자 정보 조회 성공" ))
88+ .andExpect (jsonPath ("$.data.email" ).value ("test@example.com" ));
89+ }
90+
91+ @ Test
92+ @ WithMockUser
93+ @ DisplayName ("닉네임 수정 성공" )
94+ void updateNickname_Success () throws Exception {
95+ String jsonRequest = "{\" nickname\" : \" newNickname\" }" ;
96+ UserResponse updatedResponse = UserResponse .builder ().nickname ("newNickname" ).build ();
97+
98+ given (userService .updateNickname (any (), any ())).willReturn (updatedResponse );
99+
100+ mockMvc .perform (patch ("/api/v1/users/nickname" )
101+ .with (csrf ())
102+ .contentType (MediaType .APPLICATION_JSON )
103+ .content (jsonRequest ))
104+ .andExpect (status ().isOk ())
105+ .andExpect (jsonPath ("$.resultCode" ).value ("OK" ))
106+ .andExpect (jsonPath ("$.data.nickname" ).value ("newNickname" ));
107+ }
108+
109+ @ Test
110+ @ WithMockUser
111+ @ DisplayName ("프로필 이미지 수정 성공" )
112+ void updateProfileImage_Success () throws Exception {
113+ MockMultipartFile file = new MockMultipartFile (
114+ "file" , "profile.jpg" , MediaType .IMAGE_JPEG_VALUE , "content" .getBytes ());
115+
116+ given (userService .updateProfileImage (any (), any ())).willReturn ("https://new-url.com" );
117+
118+ mockMvc .perform (multipart ("/api/v1/users/profile-image" )
119+ .file (file )
120+ .with (csrf ())
121+ .with (request -> {
122+ request .setMethod ("PATCH" );
123+ return request ;
124+ }))
125+ .andExpect (status ().isOk ())
126+ .andExpect (jsonPath ("$.msg" ).value ("프로필 이미지가 변경되었습니다." ));
127+ }
128+
129+ @ Test
130+ @ WithMockUser
131+ @ DisplayName ("비밀번호 변경 성공" )
132+ void updatePassword_Success () throws Exception {
133+
134+ String jsonRequest = """
135+
136+ {
137+
138+ "currentPassword": "oldPassword1!",
139+
140+ "newPassword": "newPassword1!"
141+
142+ }
143+
144+ """ ;
145+
146+ mockMvc .perform (patch ("/api/v1/users/password" )
147+
148+ .with (csrf ())
149+
150+ .contentType (MediaType .APPLICATION_JSON )
151+
152+ .content (jsonRequest ))
153+
154+ .andExpect (status ().isOk ());
155+
156+ verify (userService ).updatePassword (any (), any ());
157+
158+ }
159+
160+ @ Test
161+ @ WithMockUser
162+ @ DisplayName ("회원 탈퇴 성공" )
163+ void deleteMyAccount_Success () throws Exception {
164+ mockMvc .perform (delete ("/api/v1/users/me" )
165+ .with (csrf ()))
166+ .andDo (print ())
167+ .andExpect (status ().isOk ())
168+ .andExpect (jsonPath ("$.resultCode" ).value ("OK" ))
169+ .andExpect (jsonPath ("$.msg" ).value ("정상적으로 처리되었습니다." ))
170+ .andExpect (jsonPath ("$.data" ).value ("회원 탈퇴가 완료되었습니다." ));
171+ }
172+
173+ // 실패 테스트 시나리오
174+
175+ @ Test
176+ @ WithMockUser
177+ @ DisplayName ("닉네임 수정 실패 - 중복된 닉네임 (U-100)" )
178+ void updateNickname_Fail_Duplicated () throws Exception {
179+ doThrow (new BusinessException (UserErrorCode .NICKNAME_DUPLICATED ))
180+ .when (userService ).updateNickname (any (), any ());
181+
182+ String jsonRequest = "{\" nickname\" : \" existingNick\" }" ;
183+
184+ mockMvc .perform (patch ("/api/v1/users/nickname" )
185+ .with (csrf ())
186+ .contentType (MediaType .APPLICATION_JSON )
187+ .content (jsonRequest ))
188+ .andDo (print ())
189+ .andExpect (status ().isConflict ())
190+ .andExpect (jsonPath ("$.resultCode" ).value ("U-100" ))
191+ .andExpect (jsonPath ("$.msg" ).value ("이미 사용 중인 닉네임입니다." ));
192+ }
193+
194+ @ Test
195+ @ WithMockUser
196+ @ DisplayName ("비밀번호 변경 실패 - 현재 비밀번호 불일치 (U-110)" )
197+ void updatePassword_Fail_InvalidPassword () throws Exception {
198+ String jsonRequest = """
199+ {
200+ "currentPassword": "wrongPassword1!",
201+ "newPassword": "newPassword123!"
202+ }
203+ """ ;
204+
205+ doThrow (new BusinessException (UserErrorCode .INVALID_PASSWORD ))
206+ .when (userService ).updatePassword (any (), any ());
207+
208+ mockMvc .perform (patch ("/api/v1/users/password" )
209+ .with (csrf ())
210+ .contentType (MediaType .APPLICATION_JSON )
211+ .content (jsonRequest ))
212+ .andDo (print ())
213+ .andExpect (status ().isBadRequest ())
214+ .andExpect (jsonPath ("$.resultCode" ).value ("U-110" ));
215+ }
216+
217+ @ Test
218+ @ WithMockUser
219+ @ DisplayName ("비밀번호 변경 실패 - 유효성 검사 에러 (VALIDATION_ERROR)" )
220+ void updatePassword_Fail_ValidationError () throws Exception {
221+ String jsonRequest = "{\" currentPassword\" : \" short\" , \" newPassword\" : \" short\" }" ;
222+
223+ mockMvc .perform (patch ("/api/v1/users/password" )
224+ .with (csrf ())
225+ .contentType (MediaType .APPLICATION_JSON )
226+ .content (jsonRequest ))
227+ .andDo (print ())
228+ .andExpect (status ().isBadRequest ())
229+ .andExpect (jsonPath ("$.resultCode" ).value ("VALIDATION_ERROR" ));
230+ }
231+ }
0 commit comments