11package com .backend .api .review .controller ;
22
3- import com .backend .api .question .service .AiQuestionService ;
43import com .backend .api .global .JwtTest ;
5- import com .backend .domain . review . entity . Review ;
4+ import com .backend .api . question . service . AiQuestionService ;
65import com .backend .domain .resume .entity .Resume ;
76import com .backend .domain .resume .repository .ResumeRepository ;
7+ import com .backend .domain .review .entity .Review ;
8+ import com .backend .domain .review .repository .ReviewRepository ;
89import com .backend .domain .subscription .entity .Subscription ;
910import com .backend .domain .subscription .entity .SubscriptionType ;
1011import com .backend .domain .subscription .repository .SubscriptionRepository ;
11- import com .backend .domain .review .repository .ReviewRepository ;
12- import com .fasterxml .jackson .databind .ObjectMapper ;
12+ import com .backend .domain .user .entity .Role ;
13+ import com .backend .domain .user .entity .User ;
14+ import com .backend .global .Rq .Rq ;
1315import org .junit .jupiter .api .BeforeEach ;
1416import org .junit .jupiter .api .DisplayName ;
1517import org .junit .jupiter .api .Nested ;
1618import org .junit .jupiter .api .Test ;
19+ import org .mockito .Mockito ;
1720import org .springframework .beans .factory .annotation .Autowired ;
1821import org .springframework .boot .test .autoconfigure .web .servlet .AutoConfigureMockMvc ;
19- import org .springframework .boot .test .mock .mockito .MockBean ;
2022import org .springframework .boot .test .context .SpringBootTest ;
23+ import org .springframework .boot .test .mock .mockito .MockBean ;
2124import org .springframework .http .MediaType ;
2225import org .springframework .security .core .context .SecurityContextHolder ;
23- import org .springframework .test .annotation .Rollback ;
2426import org .springframework .test .context .ActiveProfiles ;
2527import org .springframework .test .web .servlet .MockMvc ;
2628import org .springframework .test .web .servlet .ResultActions ;
3638import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .jsonPath ;
3739import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .status ;
3840
39-
4041@ SpringBootTest
4142@ AutoConfigureMockMvc (addFilters = false )
4243@ Transactional
@@ -46,9 +47,6 @@ class AiReviewControllerTest extends JwtTest {
4647 @ Autowired
4748 private MockMvc mockMvc ;
4849
49- @ Autowired
50- private ObjectMapper objectMapper ;
51-
5250 @ Autowired
5351 private ReviewRepository reviewRepository ;
5452
@@ -61,20 +59,59 @@ class AiReviewControllerTest extends JwtTest {
6159 @ MockBean
6260 private AiQuestionService aiQuestionService ;
6361
62+ @ MockBean
63+ private Rq rq ;
64+
65+ private User testUser ;
66+
6467 @ BeforeEach
6568 void setUp () {
66- reviewRepository .deleteAll ();
67- resumeRepository .deleteAll ();
68- subscriptionRepository .deleteAll ();
69- userRepository .deleteAll ();
69+ // 1️⃣ 기본 유저 저장
70+ testUser = userRepository .save (
71+ User .builder ()
72+ 73+ .name ("테스트 유저" )
74+ .nickname ("tester" )
75+ .password ("1234" )
76+ .image ("default.png" )
77+ .github ("https://github.com/tester" )
78+ .role (Role .USER )
79+ .build ()
80+ );
81+
82+ // 2️⃣ rq.getUser() mock 설정
83+ Mockito .when (rq .getUser ()).thenReturn (testUser );
7084 }
7185
72- private Review createAndSaveReview (String content ) {
73- Review review = Review .builder ()
74- .AiReviewContent (content )
75- .user (mockUser )
86+ private Subscription createPremiumSubscription (User user ) {
87+ Subscription subscription = Subscription .builder ()
88+ .user (user )
89+ .subscriptionType (SubscriptionType .BASIC )
90+ .isActive (false )
91+ .subscriptionName ("BASIC" )
92+ .price (0L )
93+ .questionLimit (5 )
94+ .startDate (LocalDateTime .now ())
7695 .build ();
77- return reviewRepository .save (review );
96+
97+ // 프리미엄 전환
98+ subscription .activatePremium ("test-billing-key-123" );
99+
100+ // 관계 설정 및 저장
101+ subscription = subscriptionRepository .save (subscription );
102+ user .setSubscription (subscription );
103+ userRepository .saveAndFlush (user );
104+
105+ return subscription ;
106+ }
107+
108+ private Review createAndSaveReview (String content ) {
109+ return reviewRepository .save (
110+ Review .builder ()
111+ .AiReviewContent (content )
112+ .user (testUser )
113+ .build ()
114+ );
78115 }
79116
80117 @ Nested
@@ -85,19 +122,17 @@ class CreateAiReviewTest {
85122 @ DisplayName ("성공 - 프리미엄 등급의 사용자가 AI 첨삭을 생성합니다." )
86123 void createAiReview_Success () throws Exception {
87124 // given
88- Resume resume = Resume .builder ()
89- .user (mockUser )
90- .content ("테스트 이력서 내용입니다." )
91- .build ();
92- resumeRepository .save (resume );
125+ createPremiumSubscription (testUser );
93126
94- given (aiQuestionService .getAiReviewContent (any ())).willReturn ("AI가 생성한 첨삭 내용입니다." );
95-
96- Subscription subscription = new Subscription ();
97- subscription .activatePremium ("dummy-billing-key" );
127+ Resume resume = resumeRepository .save (
128+ Resume .builder ()
129+ .user (testUser )
130+ .content ("테스트 이력서 내용입니다." )
131+ .build ()
132+ );
98133
99- mockUser . setSubscription ( subscription );
100- subscriptionRepository . save ( subscription );
134+ given ( aiQuestionService . getAiReviewContent ( any ()))
135+ . willReturn ( "AI가 생성한 첨삭 내용입니다." );
101136
102137 // when
103138 ResultActions resultActions = mockMvc .perform (
@@ -121,6 +156,7 @@ void createAiReview_Success() throws Exception {
121156 void createAiReview_Fail_Unauthorized () throws Exception {
122157 // given
123158 SecurityContextHolder .clearContext ();
159+ Mockito .when (rq .getUser ()).thenReturn (null );
124160
125161 // when
126162 ResultActions resultActions = mockMvc .perform (
@@ -141,19 +177,19 @@ void createAiReview_Fail_Unauthorized() throws Exception {
141177 @ DisplayName ("실패 - 일반 등급의 사용자는 AI 첨삭을 생성할 수 없습니다." )
142178 void createAiReview_Fail_NotPremium () throws Exception {
143179 // given
144- Subscription basicSubscription = Subscription . builder ()
145- . subscriptionType ( SubscriptionType . BASIC )
146- . isActive ( false )
147- . subscriptionName ( " BASIC" )
148- . price ( 0L )
149- . questionLimit ( 5 )
150- . startDate ( LocalDateTime . now () )
151- . build ();
152-
153- basicSubscription . setUser ( mockUser );
154- mockUser . setSubscription ( basicSubscription );
155-
156- subscriptionRepository . save ( basicSubscription );
180+ Subscription basic = subscriptionRepository . save (
181+ Subscription . builder ( )
182+ . user ( testUser )
183+ . subscriptionType ( SubscriptionType . BASIC )
184+ . isActive ( false )
185+ . subscriptionName ( "BASIC" )
186+ . price ( 0L )
187+ . questionLimit ( 5 )
188+ . startDate ( LocalDateTime . now ())
189+ . build ()
190+ );
191+ testUser . setSubscription ( basic );
192+ userRepository . saveAndFlush ( testUser );
157193
158194 // when
159195 ResultActions resultActions = mockMvc .perform (
@@ -171,7 +207,6 @@ void createAiReview_Fail_NotPremium() throws Exception {
171207 }
172208 }
173209
174-
175210 @ Nested
176211 @ DisplayName ("AI 첨삭 단건 조회 API" )
177212 class GetReviewByIdTest {
@@ -180,6 +215,7 @@ class GetReviewByIdTest {
180215 @ DisplayName ("성공 - 자신의 AI 첨삭을 조회합니다." )
181216 void getReviewById_Success () throws Exception {
182217 // given
218+ createPremiumSubscription (testUser );
183219 Review savedReview = createAndSaveReview ("AI 첨삭 내용입니다." );
184220 Long reviewId = savedReview .getId ();
185221
@@ -197,23 +233,6 @@ void getReviewById_Success() throws Exception {
197233 .andExpect (jsonPath ("$.data.reviewId" ).value (reviewId ))
198234 .andDo (print ());
199235 }
200-
201- @ Test
202- @ DisplayName ("실패 - reviewId가 'undefined' 또는 null일 경우 예외가 발생합니다." )
203- void getReviewById_Fail_InvalidParameter () throws Exception {
204- // when
205- ResultActions resultActions = mockMvc .perform (
206- get ("/api/v1/portfolio-review/{reviewId}" , "undefined" )
207- .accept (MediaType .APPLICATION_JSON )
208- );
209-
210- // then
211- resultActions
212- .andExpect (status ().isBadRequest ())
213- .andExpect (jsonPath ("$.status" ).value ("BAD_REQUEST" ))
214- .andExpect (jsonPath ("$.message" ).value ("잘못된 파라미터입니다." ))
215- .andDo (print ());
216- }
217236 }
218237
219238 @ Nested
@@ -224,12 +243,12 @@ class GetMyReviewsTest {
224243 @ DisplayName ("성공 - 로그인한 사용자의 모든 AI 첨삭 목록을 조회합니다." )
225244 void getMyReviews_Success () throws Exception {
226245 // given
227- // 최신순으로 조회되므로, 시간 순서를 다르게 해서 저장
246+ createPremiumSubscription (testUser );
247+
228248 createAndSaveReview ("첫 번째 첨삭" );
229- Thread .sleep (10 ); // 생성 시간 차이를 두기 위함
249+ Thread .sleep (10 );
230250 Review latestReview = createAndSaveReview ("두 번째 첨삭" );
231251
232-
233252 // when
234253 ResultActions resultActions = mockMvc .perform (
235254 get ("/api/v1/portfolio-review/reviews" )
@@ -242,26 +261,8 @@ void getMyReviews_Success() throws Exception {
242261 .andExpect (jsonPath ("$.status" ).value ("OK" ))
243262 .andExpect (jsonPath ("$.message" ).value ("내 AI 첨삭 목록 조회가 완료되었습니다." ))
244263 .andExpect (jsonPath ("$.data.length()" ).value (2 ))
245- .andExpect (jsonPath ("$.data[0].reviewId" ).value (latestReview .getId ())) // 최신순 정렬 확인
246- .andDo (print ());
247- }
248-
249- @ Test
250- @ DisplayName ("성공 - AI 첨삭 내역이 없을 경우 빈 리스트를 반환합니다." )
251- void getMyReviews_Success_Empty () throws Exception {
252- // when
253- ResultActions resultActions = mockMvc .perform (
254- get ("/api/v1/portfolio-review/reviews" )
255- .accept (MediaType .APPLICATION_JSON )
256- );
257-
258- // then
259- resultActions
260- .andExpect (status ().isOk ())
261- .andExpect (jsonPath ("$.status" ).value ("OK" ))
262- .andExpect (jsonPath ("$.message" ).value ("내 AI 첨삭 목록 조회가 완료되었습니다." ))
263- .andExpect (jsonPath ("$.data.length()" ).value (0 ))
264+ .andExpect (jsonPath ("$.data[0].reviewId" ).value (latestReview .getId ()))
264265 .andDo (print ());
265266 }
266267 }
267- }
268+ }
0 commit comments