Skip to content

Commit b32356f

Browse files
committed
2 parents a542bc7 + 637b772 commit b32356f

File tree

4 files changed

+961
-19
lines changed

4 files changed

+961
-19
lines changed

back/src/main/java/com/back/domain/scenario/controller/ScenarioController.java

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
@RequiredArgsConstructor
2727
public class ScenarioController {
2828

29+
// TODO: ApiResponse를 ResponseEntity로 변경 예정
2930
private final ScenarioService scenarioService;
3031

3132
@PostMapping
3233
@Operation(summary = "시나리오 생성", description = "DecisionLine을 기반으로 AI 시나리오를 생성합니다.")
3334
public ApiResponse<ScenarioStatusResponse> createScenario(
34-
@Valid @RequestBody ScenarioCreateRequest request,
35-
Principal principal
35+
@Valid @RequestBody ScenarioCreateRequest request
3636
) {
37-
Long userId = getUserIdFromPrincipal(principal);
37+
Long userId = 1L; // TODO: Principal에서 추출 예정
3838

3939
ScenarioStatusResponse scenarioCreateResponse = scenarioService.createScenario(userId, request);
4040

@@ -44,10 +44,9 @@ public ApiResponse<ScenarioStatusResponse> createScenario(
4444
@GetMapping("/{scenarioId}/status")
4545
@Operation(summary = "시나리오 상태 조회", description = "시나리오 생성 진행 상태를 조회합니다.")
4646
public ApiResponse<ScenarioStatusResponse> getScenarioStatus(
47-
@Parameter(description = "시나리오 ID") @PathVariable Long scenarioId,
48-
Principal principal
47+
@Parameter(description = "시나리오 ID") @PathVariable Long scenarioId
4948
) {
50-
Long userId = getUserIdFromPrincipal(principal);
49+
Long userId = 1L; // TODO: Principal에서 추출 예정
5150

5251
ScenarioStatusResponse scenarioStatusResponse = scenarioService.getScenarioStatus(scenarioId, userId);
5352

@@ -57,10 +56,9 @@ public ApiResponse<ScenarioStatusResponse> getScenarioStatus(
5756
@GetMapping("/info/{scenarioId}")
5857
@Operation(summary = "시나리오 상세 조회", description = "완성된 시나리오의 상세 정보를 조회합니다.")
5958
public ApiResponse<ScenarioDetailResponse> getScenarioDetail(
60-
@Parameter(description = "시나리오 ID") @PathVariable Long scenarioId,
61-
Principal principal
59+
@Parameter(description = "시나리오 ID") @PathVariable Long scenarioId
6260
) {
63-
Long userId = getUserIdFromPrincipal(principal);
61+
Long userId = 1L; // TODO: Principal에서 추출 예정
6462

6563
ScenarioDetailResponse scenarioDetailResponse = scenarioService.getScenarioDetail(scenarioId, userId);
6664

@@ -70,10 +68,9 @@ public ApiResponse<ScenarioDetailResponse> getScenarioDetail(
7068
@GetMapping("/{scenarioId}/timeline")
7169
@Operation(summary = "시나리오 타임라인 조회", description = "시나리오의 선택 경로를 시간순으로 조회합니다.")
7270
public ApiResponse<TimelineResponse> getScenarioTimeline(
73-
@Parameter(description = "시나리오 ID") @PathVariable Long scenarioId,
74-
Principal principal
71+
@Parameter(description = "시나리오 ID") @PathVariable Long scenarioId
7572
) {
76-
Long userId = getUserIdFromPrincipal(principal);
73+
Long userId = 1L; // TODO: Principal에서 추출 예정
7774

7875
TimelineResponse timelineResponse = scenarioService.getScenarioTimeline(scenarioId, userId);
7976

@@ -82,10 +79,8 @@ public ApiResponse<TimelineResponse> getScenarioTimeline(
8279

8380
@GetMapping("/baselines")
8481
@Operation(summary = "베이스라인 목록 조회", description = "사용자의 베이스라인 목록을 조회합니다.")
85-
public ApiResponse<List<BaselineListResponse>> getBaselines(
86-
Principal principal
87-
) {
88-
Long userId = getUserIdFromPrincipal(principal);
82+
public ApiResponse<List<BaselineListResponse>> getBaselines() {
83+
Long userId = 1L; // TODO: Principal에서 추출 예정
8984

9085
List<BaselineListResponse> baselines = scenarioService.getBaselines(userId);
9186

@@ -96,10 +91,9 @@ public ApiResponse<List<BaselineListResponse>> getBaselines(
9691
@Operation(summary = "시나리오 비교 분석 결과 조회", description = "두 시나리오를 비교 분석 결과를 조회합니다.")
9792
public ApiResponse<ScenarioCompareResponse> compareScenarios(
9893
@Parameter(description = "기준 시나리오 ID") @PathVariable Long baseId,
99-
@Parameter(description = "비교 시나리오 ID") @PathVariable Long compareId,
100-
Principal principal
94+
@Parameter(description = "비교 시나리오 ID") @PathVariable Long compareId
10195
) {
102-
Long userId = getUserIdFromPrincipal(principal);
96+
Long userId = 1L; // TODO: Principal에서 추출 예정
10397

10498
ScenarioCompareResponse scenarioCompareResponse = scenarioService.compareScenarios(baseId, compareId, userId);
10599

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
package com.back.domain.scenario.controller;
2+
3+
import com.back.domain.scenario.dto.*;
4+
import com.back.domain.scenario.entity.ScenarioStatus;
5+
import com.back.domain.scenario.entity.Type;
6+
import com.back.domain.scenario.service.ScenarioService;
7+
import com.back.global.exception.ApiException;
8+
import com.back.global.exception.ErrorCode;
9+
import com.fasterxml.jackson.databind.ObjectMapper;
10+
import org.junit.jupiter.api.DisplayName;
11+
import org.junit.jupiter.api.Nested;
12+
import org.junit.jupiter.api.Test;
13+
import org.junit.jupiter.api.TestInstance;
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.test.context.ActiveProfiles;
20+
import org.springframework.test.web.servlet.MockMvc;
21+
import org.springframework.transaction.annotation.Transactional;
22+
23+
import java.time.LocalDateTime;
24+
import java.util.List;
25+
26+
import static org.mockito.ArgumentMatchers.*;
27+
import static org.mockito.BDDMockito.given;
28+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
29+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
30+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
31+
32+
/**
33+
* ScenarioController 통합 테스트.
34+
* 인증/인가가 구현되지 않은 상태에서 Service를 모킹하여 테스트합니다.
35+
*/
36+
@SpringBootTest
37+
@AutoConfigureMockMvc(addFilters = false)
38+
@ActiveProfiles("test")
39+
@Transactional
40+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
41+
@DisplayName("ScenarioController 통합 테스트")
42+
class ScenarioControllerTest {
43+
44+
@Autowired
45+
private MockMvc mockMvc;
46+
47+
@Autowired
48+
private ObjectMapper objectMapper;
49+
50+
@MockBean
51+
private ScenarioService scenarioService;
52+
53+
@Nested
54+
@DisplayName("시나리오 생성")
55+
class CreateScenario {
56+
57+
@Test
58+
@DisplayName("성공 - 정상적인 시나리오 생성 요청")
59+
void createScenario_성공() throws Exception {
60+
// Given
61+
Long decisionLineId = 100L;
62+
ScenarioCreateRequest request = new ScenarioCreateRequest(decisionLineId);
63+
64+
ScenarioStatusResponse mockResponse = new ScenarioStatusResponse(
65+
1001L,
66+
ScenarioStatus.PENDING,
67+
"시나리오 생성이 시작되었습니다."
68+
);
69+
70+
given(scenarioService.createScenario(eq(1L), any(ScenarioCreateRequest.class)))
71+
.willReturn(mockResponse);
72+
73+
// When & Then
74+
mockMvc.perform(post("/api/v1/scenarios")
75+
.contentType(MediaType.APPLICATION_JSON)
76+
.content(objectMapper.writeValueAsString(request)))
77+
.andDo(print())
78+
.andExpect(status().isOk()) // ApiResponse는 실제 HTTP 상태를 설정하지 않음
79+
.andExpect(jsonPath("$.data.scenarioId").value(1001))
80+
.andExpect(jsonPath("$.data.status").value("PENDING"))
81+
.andExpect(jsonPath("$.message").value("시나리오 생성 요청이 접수되었습니다."))
82+
.andExpect(jsonPath("$.status").value(201)); // 응답 본문의 status 필드 검증
83+
}
84+
85+
@Test
86+
@DisplayName("실패 - 잘못된 요청 데이터 (null decisionLineId)")
87+
void createScenario_실패_잘못된요청() throws Exception {
88+
// Given
89+
String invalidRequest = "{\"decisionLineId\":null}";
90+
91+
// When & Then
92+
mockMvc.perform(post("/api/v1/scenarios")
93+
.contentType(MediaType.APPLICATION_JSON)
94+
.content(invalidRequest))
95+
.andDo(print())
96+
.andExpect(status().isBadRequest());
97+
}
98+
99+
@Test
100+
@DisplayName("실패 - Service 예외 발생")
101+
void createScenario_실패_Service예외() throws Exception {
102+
// Given
103+
ScenarioCreateRequest request = new ScenarioCreateRequest(999L);
104+
105+
given(scenarioService.createScenario(eq(1L), any(ScenarioCreateRequest.class)))
106+
.willThrow(new ApiException(ErrorCode.DECISION_LINE_NOT_FOUND));
107+
108+
// When & Then
109+
mockMvc.perform(post("/api/v1/scenarios")
110+
.contentType(MediaType.APPLICATION_JSON)
111+
.content(objectMapper.writeValueAsString(request)))
112+
.andDo(print())
113+
.andExpect(status().isNotFound());
114+
}
115+
}
116+
117+
@Nested
118+
@DisplayName("시나리오 상태 조회")
119+
class GetScenarioStatus {
120+
121+
@Test
122+
@DisplayName("성공 - 유효한 시나리오 상태 조회")
123+
void getScenarioStatus_성공() throws Exception {
124+
// Given
125+
Long scenarioId = 1001L;
126+
ScenarioStatusResponse mockResponse = new ScenarioStatusResponse(
127+
scenarioId,
128+
ScenarioStatus.COMPLETED,
129+
"시나리오 생성이 완료되었습니다."
130+
);
131+
132+
given(scenarioService.getScenarioStatus(scenarioId, 1L))
133+
.willReturn(mockResponse);
134+
135+
// When & Then
136+
mockMvc.perform(get("/api/v1/scenarios/{scenarioId}/status", scenarioId))
137+
.andDo(print())
138+
.andExpect(status().isOk())
139+
.andExpect(jsonPath("$.data.scenarioId").value(scenarioId))
140+
.andExpect(jsonPath("$.data.status").value("COMPLETED"))
141+
.andExpect(jsonPath("$.message").value("상태를 성공적으로 조회했습니다."));
142+
}
143+
144+
@Test
145+
@DisplayName("실패 - 존재하지 않는 시나리오")
146+
void getScenarioStatus_실패_없는시나리오() throws Exception {
147+
// Given
148+
Long scenarioId = 999L;
149+
150+
given(scenarioService.getScenarioStatus(scenarioId, 1L))
151+
.willThrow(new ApiException(ErrorCode.SCENARIO_NOT_FOUND));
152+
153+
// When & Then
154+
mockMvc.perform(get("/api/v1/scenarios/{scenarioId}/status", scenarioId))
155+
.andDo(print())
156+
.andExpect(status().isNotFound());
157+
}
158+
}
159+
160+
@Nested
161+
@DisplayName("시나리오 상세 조회")
162+
class GetScenarioDetail {
163+
164+
@Test
165+
@DisplayName("성공 - 완료된 시나리오 상세 조회")
166+
void getScenarioDetail_성공() throws Exception {
167+
// Given
168+
Long scenarioId = 1001L;
169+
170+
List<ScenarioTypeDto> indicators = List.of(
171+
new ScenarioTypeDto(Type.경제, 90, "창업 성공으로 높은 경제적 성취"),
172+
new ScenarioTypeDto(Type.행복, 85, "자신이 원하는 일을 하며 성취감 높음"),
173+
new ScenarioTypeDto(Type.관계, 75, "업무로 인해 개인 관계에 다소 소홀"),
174+
new ScenarioTypeDto(Type.직업, 95, "창업가로서 최고 수준의 직업 만족도"),
175+
new ScenarioTypeDto(Type.건강, 70, "스트레스로 인한 건강 관리 필요")
176+
);
177+
178+
ScenarioDetailResponse mockResponse = new ScenarioDetailResponse(
179+
scenarioId,
180+
ScenarioStatus.COMPLETED,
181+
"스타트업 CEO",
182+
85,
183+
"성공적인 창업으로 안정적인 수익 창출",
184+
"창업 초기 어려움을 극복하고 지속가능한 비즈니스 모델을 구축했습니다.",
185+
"https://example.com/scenario-image.jpg",
186+
LocalDateTime.now().minusDays(1),
187+
indicators
188+
);
189+
190+
given(scenarioService.getScenarioDetail(scenarioId, 1L))
191+
.willReturn(mockResponse);
192+
193+
// When & Then
194+
mockMvc.perform(get("/api/v1/scenarios/info/{scenarioId}", scenarioId))
195+
.andDo(print())
196+
.andExpect(status().isOk())
197+
.andExpect(jsonPath("$.data.scenarioId").value(scenarioId))
198+
.andExpect(jsonPath("$.data.job").value("스타트업 CEO"))
199+
.andExpect(jsonPath("$.data.total").value(85))
200+
.andExpect(jsonPath("$.data.indicators").isArray())
201+
.andExpect(jsonPath("$.data.indicators.length()").value(5));
202+
}
203+
}
204+
205+
@Nested
206+
@DisplayName("시나리오 타임라인 조회")
207+
class GetScenarioTimeline {
208+
209+
@Test
210+
@DisplayName("성공 - 시나리오 타임라인 조회")
211+
void getScenarioTimeline_성공() throws Exception {
212+
// Given
213+
Long scenarioId = 1001L;
214+
215+
List<TimelineResponse.TimelineEvent> events = List.of(
216+
new TimelineResponse.TimelineEvent(2025, "창업 시작"),
217+
new TimelineResponse.TimelineEvent(2027, "첫 투자 유치"),
218+
new TimelineResponse.TimelineEvent(2030, "IPO 성공")
219+
);
220+
221+
TimelineResponse mockResponse = new TimelineResponse(scenarioId, events);
222+
223+
given(scenarioService.getScenarioTimeline(scenarioId, 1L))
224+
.willReturn(mockResponse);
225+
226+
// When & Then
227+
mockMvc.perform(get("/api/v1/scenarios/{scenarioId}/timeline", scenarioId))
228+
.andDo(print())
229+
.andExpect(status().isOk())
230+
.andExpect(jsonPath("$.data.scenarioId").value(scenarioId))
231+
.andExpect(jsonPath("$.data.events").isArray())
232+
.andExpect(jsonPath("$.data.events[0].year").value(2025))
233+
.andExpect(jsonPath("$.data.events[0].title").value("창업 시작"));
234+
}
235+
}
236+
237+
@Nested
238+
@DisplayName("베이스라인 목록 조회")
239+
class GetBaselines {
240+
241+
@Test
242+
@DisplayName("성공 - 사용자 베이스라인 목록 조회")
243+
void getBaselines_성공() throws Exception {
244+
// Given
245+
List<BaselineListResponse> mockResponse = List.of(
246+
new BaselineListResponse(
247+
200L,
248+
"대학 졸업 이후",
249+
List.of("진로", "교육"),
250+
LocalDateTime.now().minusMonths(6)
251+
),
252+
new BaselineListResponse(
253+
201L,
254+
"첫 직장 입사",
255+
List.of("직업", "성장"),
256+
LocalDateTime.now().minusMonths(3)
257+
)
258+
);
259+
260+
given(scenarioService.getBaselines(1L))
261+
.willReturn(mockResponse);
262+
263+
// When & Then
264+
mockMvc.perform(get("/api/v1/scenarios/baselines"))
265+
.andDo(print())
266+
.andExpect(status().isOk())
267+
.andExpect(jsonPath("$.data").isArray())
268+
.andExpect(jsonPath("$.data[0].baselineId").value(200))
269+
.andExpect(jsonPath("$.data[0].title").value("대학 졸업 이후"));
270+
}
271+
}
272+
273+
@Nested
274+
@DisplayName("시나리오 비교")
275+
class CompareScenarios {
276+
277+
@Test
278+
@DisplayName("성공 - 시나리오 비교 분석")
279+
void compareScenarios_성공() throws Exception {
280+
// Given
281+
Long baseId = 1001L;
282+
Long compareId = 1002L;
283+
284+
List<ScenarioCompareResponse.IndicatorComparison> indicators = List.of(
285+
new ScenarioCompareResponse.IndicatorComparison(Type.경제, 90, 80, "창업이 대기업보다 경제적으로 유리"),
286+
new ScenarioCompareResponse.IndicatorComparison(Type.행복, 85, 70, "창업을 통한 더 높은 성취감"),
287+
new ScenarioCompareResponse.IndicatorComparison(Type.관계, 75, 85, "대기업에서 더 안정적인 인간관계"),
288+
new ScenarioCompareResponse.IndicatorComparison(Type.직업, 95, 75, "창업가로서 더 높은 직업 만족도"),
289+
new ScenarioCompareResponse.IndicatorComparison(Type.건강, 70, 80, "대기업에서 더 나은 워라밸")
290+
);
291+
292+
ScenarioCompareResponse mockResponse = new ScenarioCompareResponse(
293+
baseId,
294+
compareId,
295+
"창업 경로가 전반적으로 더 도전적이지만 성취감과 경제적 보상이 큽니다.",
296+
indicators
297+
);
298+
299+
given(scenarioService.compareScenarios(baseId, compareId, 1L))
300+
.willReturn(mockResponse);
301+
302+
// When & Then
303+
mockMvc.perform(get("/api/v1/scenarios/compare/{baseId}/{compareId}", baseId, compareId))
304+
.andDo(print())
305+
.andExpect(status().isOk())
306+
.andExpect(jsonPath("$.data.baseScenarioId").value(baseId))
307+
.andExpect(jsonPath("$.data.compareScenarioId").value(compareId))
308+
.andExpect(jsonPath("$.data.overallAnalysis").exists())
309+
.andExpect(jsonPath("$.data.indicators").isArray());
310+
}
311+
}
312+
}

0 commit comments

Comments
 (0)