Skip to content

Commit 289e692

Browse files
committed
Test: 컨트롤러 테스트 작성
1 parent e31d99f commit 289e692

File tree

2 files changed

+354
-3
lines changed

2 files changed

+354
-3
lines changed
Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
package com.back.domain.user.controller;
2+
3+
import com.back.domain.user.dto.UpdateUserProfileRequest;
4+
import com.back.domain.user.entity.User;
5+
import com.back.domain.user.entity.UserProfile;
6+
import com.back.domain.user.entity.UserStatus;
7+
import com.back.domain.user.repository.UserRepository;
8+
import com.back.fixture.TestJwtTokenProvider;
9+
import com.fasterxml.jackson.databind.ObjectMapper;
10+
import org.junit.jupiter.api.DisplayName;
11+
import org.junit.jupiter.api.Test;
12+
import org.springframework.beans.factory.annotation.Autowired;
13+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
14+
import org.springframework.boot.test.context.SpringBootTest;
15+
import org.springframework.http.MediaType;
16+
import org.springframework.security.crypto.password.PasswordEncoder;
17+
import org.springframework.test.context.ActiveProfiles;
18+
import org.springframework.test.web.servlet.MockMvc;
19+
import org.springframework.test.web.servlet.ResultActions;
20+
import org.springframework.transaction.annotation.Transactional;
21+
22+
import java.time.LocalDate;
23+
24+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
25+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
26+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
27+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
28+
29+
@SpringBootTest
30+
@AutoConfigureMockMvc
31+
@ActiveProfiles("test")
32+
@Transactional
33+
class UserControllerTest {
34+
35+
@Autowired
36+
private MockMvc mvc;
37+
38+
@Autowired
39+
private UserRepository userRepository;
40+
41+
@Autowired
42+
private TestJwtTokenProvider testJwtTokenProvider;
43+
44+
@Autowired
45+
private PasswordEncoder passwordEncoder;
46+
47+
@Autowired
48+
private ObjectMapper objectMapper;
49+
50+
private String generateAccessToken(User user) {
51+
return testJwtTokenProvider.createAccessToken(user.getId(), user.getUsername(), user.getRole().name());
52+
}
53+
54+
// ---------------------- getMyInfo ----------------------
55+
56+
@Test
57+
@DisplayName("내 정보 조회 성공 → 200 OK")
58+
void getMyInfo_success() throws Exception {
59+
// given: 정상 유저 생성
60+
User user = User.createUser("myinfo", "[email protected]", passwordEncoder.encode("P@ssw0rd!"));
61+
user.setUserProfile(new UserProfile(user, "홍길동", "https://cdn.example.com/1.png", "안녕하세요", LocalDate.of(2000, 1, 1), 1000));
62+
user.setUserStatus(UserStatus.ACTIVE);
63+
userRepository.save(user);
64+
65+
String accessToken = generateAccessToken(user);
66+
67+
// when: 내 정보 조회 요청
68+
ResultActions resultActions = mvc.perform(
69+
get("/api/users/me")
70+
.header("Authorization", "Bearer " + accessToken)
71+
).andDo(print());
72+
73+
// then: 200 OK + 반환 값 검증
74+
resultActions
75+
.andExpect(status().isOk())
76+
.andExpect(jsonPath("$.success").value(true))
77+
.andExpect(jsonPath("$.data.username").value("myinfo"))
78+
.andExpect(jsonPath("$.data.profile.nickname").value("홍길동"));
79+
}
80+
81+
@Test
82+
@DisplayName("탈퇴한 계정 조회 → 410 Gone")
83+
void getMyInfo_deletedUser() throws Exception {
84+
// given: 상태 DELETED 유저 저장
85+
User user = User.createUser("deleted", "[email protected]", passwordEncoder.encode("P@ssw0rd!"));
86+
user.setUserProfile(new UserProfile(user, "닉네임", null, null, null, 0));
87+
user.setUserStatus(UserStatus.DELETED);
88+
userRepository.save(user);
89+
90+
String accessToken = generateAccessToken(user);
91+
92+
// when & then
93+
mvc.perform(get("/api/users/me").header("Authorization", "Bearer " + accessToken))
94+
.andDo(print())
95+
.andExpect(status().isGone())
96+
.andExpect(jsonPath("$.code").value("USER_009"));
97+
}
98+
99+
@Test
100+
@DisplayName("정지된 계정 조회 → 403 Forbidden")
101+
void getMyInfo_suspendedUser() throws Exception {
102+
// given: 상태 SUSPENDED 유저 저장
103+
User user = User.createUser("suspended", "[email protected]", passwordEncoder.encode("P@ssw0rd!"));
104+
user.setUserProfile(new UserProfile(user, "닉네임", null, null, null, 0));
105+
user.setUserStatus(UserStatus.SUSPENDED);
106+
userRepository.save(user);
107+
108+
String accessToken = generateAccessToken(user);
109+
110+
// when & then
111+
mvc.perform(get("/api/users/me").header("Authorization", "Bearer " + accessToken))
112+
.andDo(print())
113+
.andExpect(status().isForbidden())
114+
.andExpect(jsonPath("$.code").value("USER_008"));
115+
}
116+
117+
@Test
118+
@DisplayName("AccessToken 없음 → 401 Unauthorized")
119+
void getMyInfo_noAccessToken() throws Exception {
120+
// when & then
121+
mvc.perform(get("/api/users/me"))
122+
.andDo(print())
123+
.andExpect(status().isUnauthorized())
124+
.andExpect(jsonPath("$.code").value("AUTH_401"));
125+
}
126+
127+
// TODO: 인증 에러 처리 로직 수정 후 테스트 케이스 활성화
128+
// @Test
129+
// @DisplayName("잘못된 AccessToken → 401 Unauthorized")
130+
// void getMyInfo_invalidAccessToken() throws Exception {
131+
// // when & then
132+
// mvc.perform(get("/api/users/me").header("Authorization", "Bearer invalidToken"))
133+
// .andDo(print())
134+
// .andExpect(status().isUnauthorized())
135+
// .andExpect(jsonPath("$.code").value("AUTH_401"));
136+
// }
137+
//
138+
// @Test
139+
// @DisplayName("만료된 AccessToken → 401 Unauthorized")
140+
// void getMyInfo_expiredAccessToken() throws Exception {
141+
// // given: 만료된 토큰 발급
142+
// User user = User.createUser("expired", "[email protected]", passwordEncoder.encode("P@ssw0rd!"));
143+
// user.setUserProfile(new UserProfile(user, "닉네임", null, null, null, 0));
144+
// user.setUserStatus(UserStatus.ACTIVE);
145+
// userRepository.save(user);
146+
//
147+
// String expiredToken = testJwtTokenProvider.createExpiredAccessToken(user.getId(), user.getUsername(), user.getRole().name());
148+
//
149+
// // when & then
150+
// mvc.perform(get("/api/users/me").header("Authorization", "Bearer " + expiredToken))
151+
// .andDo(print())
152+
// .andExpect(status().isUnauthorized())
153+
// .andExpect(jsonPath("$.code").value("AUTH_401"));
154+
// }
155+
156+
// ---------------------- updateMyProfile ----------------------
157+
158+
@Test
159+
@DisplayName("내 프로필 수정 성공 → 200 OK")
160+
void updateMyProfile_success() throws Exception {
161+
// given: 정상 유저 저장
162+
User user = User.createUser("updateuser", "[email protected]", passwordEncoder.encode("P@ssw0rd!"));
163+
user.setUserProfile(new UserProfile(user, "기존닉", null, null, null, 0));
164+
user.setUserStatus(UserStatus.ACTIVE);
165+
userRepository.save(user);
166+
167+
String accessToken = generateAccessToken(user);
168+
169+
UpdateUserProfileRequest request = new UpdateUserProfileRequest(
170+
"새닉네임",
171+
"https://cdn.example.com/profile/new.png",
172+
"저는 개발자입니다!",
173+
LocalDate.of(2000, 5, 10)
174+
);
175+
176+
// when
177+
ResultActions resultActions = mvc.perform(
178+
patch("/api/users/me")
179+
.header("Authorization", "Bearer " + accessToken)
180+
.contentType(MediaType.APPLICATION_JSON)
181+
.content(objectMapper.writeValueAsString(request))
182+
).andDo(print());
183+
184+
// then
185+
resultActions
186+
.andExpect(status().isOk())
187+
.andExpect(jsonPath("$.data.profile.nickname").value("새닉네임"))
188+
.andExpect(jsonPath("$.data.profile.bio").value("저는 개발자입니다!"))
189+
.andExpect(jsonPath("$.data.profile.birthDate").value("2000-05-10"));
190+
}
191+
192+
@Test
193+
@DisplayName("중복 닉네임 수정 → 409 Conflict")
194+
void updateMyProfile_duplicateNickname() throws Exception {
195+
// given: user1, user2 저장
196+
User user1 = User.createUser("user1", "[email protected]", passwordEncoder.encode("P@ssw0rd!"));
197+
user1.setUserProfile(new UserProfile(user1, "닉1", null, null, null, 0));
198+
user1.setUserStatus(UserStatus.ACTIVE);
199+
userRepository.save(user1);
200+
201+
User user2 = User.createUser("user2", "[email protected]", passwordEncoder.encode("P@ssw0rd!"));
202+
user2.setUserProfile(new UserProfile(user2, "닉2", null, null, null, 0));
203+
user2.setUserStatus(UserStatus.ACTIVE);
204+
userRepository.save(user2);
205+
206+
String accessToken = generateAccessToken(user2);
207+
208+
UpdateUserProfileRequest request = new UpdateUserProfileRequest("닉1", null, null, null);
209+
210+
// when & then
211+
mvc.perform(patch("/api/users/me")
212+
.header("Authorization", "Bearer " + accessToken)
213+
.contentType(MediaType.APPLICATION_JSON)
214+
.content(objectMapper.writeValueAsString(request)))
215+
.andDo(print())
216+
.andExpect(status().isConflict())
217+
.andExpect(jsonPath("$.code").value("USER_004"));
218+
}
219+
220+
@Test
221+
@DisplayName("탈퇴 계정 프로필 수정 → 410 Gone")
222+
void updateMyProfile_deletedUser() throws Exception {
223+
// given: 상태 DELETED 유저 저장
224+
User user = User.createUser("deleted2", "[email protected]", passwordEncoder.encode("P@ssw0rd!"));
225+
user.setUserProfile(new UserProfile(user, "닉네임", null, null, null, 0));
226+
user.setUserStatus(UserStatus.DELETED);
227+
userRepository.save(user);
228+
229+
String accessToken = generateAccessToken(user);
230+
231+
UpdateUserProfileRequest request = new UpdateUserProfileRequest("새닉", null, null, null);
232+
233+
// when & then
234+
mvc.perform(patch("/api/users/me")
235+
.header("Authorization", "Bearer " + accessToken)
236+
.contentType(MediaType.APPLICATION_JSON)
237+
.content(objectMapper.writeValueAsString(request)))
238+
.andDo(print())
239+
.andExpect(status().isGone())
240+
.andExpect(jsonPath("$.code").value("USER_009"));
241+
}
242+
243+
@Test
244+
@DisplayName("정지 계정 프로필 수정 → 403 Forbidden")
245+
void updateMyProfile_suspendedUser() throws Exception {
246+
// given: 상태 SUSPENDED 유저 저장
247+
User user = User.createUser("suspended2", "[email protected]", passwordEncoder.encode("P@ssw0rd!"));
248+
user.setUserProfile(new UserProfile(user, "닉네임", null, null, null, 0));
249+
user.setUserStatus(UserStatus.SUSPENDED);
250+
userRepository.save(user);
251+
252+
String accessToken = generateAccessToken(user);
253+
254+
UpdateUserProfileRequest request = new UpdateUserProfileRequest("새닉", null, null, null);
255+
256+
// when & then
257+
mvc.perform(patch("/api/users/me")
258+
.header("Authorization", "Bearer " + accessToken)
259+
.contentType(MediaType.APPLICATION_JSON)
260+
.content(objectMapper.writeValueAsString(request)))
261+
.andDo(print())
262+
.andExpect(status().isForbidden())
263+
.andExpect(jsonPath("$.code").value("USER_008"));
264+
}
265+
266+
@Test
267+
@DisplayName("AccessToken 없음으로 프로필 수정 → 401 Unauthorized")
268+
void updateMyProfile_noAccessToken() throws Exception {
269+
// given
270+
UpdateUserProfileRequest request = new UpdateUserProfileRequest("새닉", null, null, null);
271+
272+
// when & then
273+
mvc.perform(patch("/api/users/me")
274+
.contentType(MediaType.APPLICATION_JSON)
275+
.content(objectMapper.writeValueAsString(request)))
276+
.andDo(print())
277+
.andExpect(status().isUnauthorized())
278+
.andExpect(jsonPath("$.code").value("AUTH_401"));
279+
}
280+
281+
// TODO: 인증 에러 처리 로직 수정 후 테스트 케이스 활성화
282+
// @Test
283+
// @DisplayName("잘못된 AccessToken으로 프로필 수정 → 401 Unauthorized")
284+
// void updateMyProfile_invalidAccessToken() throws Exception {
285+
// // given
286+
// UpdateUserProfileRequest request = new UpdateUserProfileRequest("새닉", null, null, null);
287+
//
288+
// // when & then
289+
// mvc.perform(patch("/api/users/me")
290+
// .header("Authorization", "Bearer invalidToken")
291+
// .contentType(MediaType.APPLICATION_JSON)
292+
// .content(objectMapper.writeValueAsString(request)))
293+
// .andDo(print())
294+
// .andExpect(status().isUnauthorized())
295+
// .andExpect(jsonPath("$.code").value("AUTH_401"));
296+
// }
297+
//
298+
// @Test
299+
// @DisplayName("만료된 AccessToken으로 프로필 수정 → 401 Unauthorized")
300+
// void updateMyProfile_expiredAccessToken() throws Exception {
301+
// // given: 만료된 토큰 발급
302+
// User user = User.createUser("expired2", "[email protected]", passwordEncoder.encode("P@ssw0rd!"));
303+
// user.setUserProfile(new UserProfile(user, "닉네임", null, null, null, 0));
304+
// user.setUserStatus(UserStatus.ACTIVE);
305+
// userRepository.save(user);
306+
//
307+
// String expiredToken = testJwtTokenProvider.createExpiredAccessToken(user.getId(), user.getUsername(), user.getRole().name());
308+
//
309+
// UpdateUserProfileRequest request = new UpdateUserProfileRequest("새닉", null, null, null);
310+
//
311+
// // when & then
312+
// mvc.perform(patch("/api/users/me")
313+
// .header("Authorization", "Bearer " + expiredToken)
314+
// .contentType(MediaType.APPLICATION_JSON)
315+
// .content(objectMapper.writeValueAsString(request)))
316+
// .andDo(print())
317+
// .andExpect(status().isUnauthorized())
318+
// .andExpect(jsonPath("$.code").value("AUTH_401"));
319+
// }
320+
}

src/test/java/com/back/fixture/TestJwtTokenProvider.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ public void init() {
2727

2828
/**
2929
* 만료된 리프레시 토큰 생성
30-
*
31-
* @param userId 사용자 ID
32-
* @return 만료된 JWT 리프레시 토큰 문자열
3330
*/
3431
public String createExpiredRefreshToken(Long userId) {
3532
Date issuedAt = new Date(System.currentTimeMillis() - 2000L); // 2초 전 발급
@@ -42,4 +39,38 @@ public String createExpiredRefreshToken(Long userId) {
4239
.signWith(key)
4340
.compact();
4441
}
42+
43+
/**
44+
* 만료된 액세스 토큰 생성
45+
*/
46+
public String createExpiredAccessToken(Long userId, String username, String role) {
47+
Date issuedAt = new Date(System.currentTimeMillis() - 2000L); // 2초 전 발급
48+
Date expiredAt = new Date(System.currentTimeMillis() - 1000L); // 1초 전 만료
49+
50+
return Jwts.builder()
51+
.subject(username)
52+
.claim("userId", userId)
53+
.claim("role", role)
54+
.issuedAt(issuedAt)
55+
.expiration(expiredAt)
56+
.signWith(key)
57+
.compact();
58+
}
59+
60+
/**
61+
* 유효한 액세스 토큰 생성
62+
*/
63+
public String createAccessToken(Long userId, String username, String role) {
64+
Date issuedAt = new Date();
65+
Date expiredAt = new Date(System.currentTimeMillis() + 1000L * 60 * 10); // 10분 유효
66+
67+
return Jwts.builder()
68+
.subject(username)
69+
.claim("userId", userId)
70+
.claim("role", role)
71+
.issuedAt(issuedAt)
72+
.expiration(expiredAt)
73+
.signWith(key)
74+
.compact();
75+
}
4576
}

0 commit comments

Comments
 (0)