Skip to content

Commit b157602

Browse files
committed
[Refactor] 마이너변경
1 parent b39a5eb commit b157602

File tree

6 files changed

+54
-81
lines changed

6 files changed

+54
-81
lines changed

back/src/main/java/com/back/domain/scenario/service/ScenarioTransactionService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class ScenarioTransactionService {
4545
private final BaseLineRepository baseLineRepository;
4646
private final AiService aiService;
4747
private final ObjectMapper objectMapper;
48+
private final com.back.global.ai.config.ImageAiConfig imageAiConfig;
4849

4950
// 상태 업데이트 전용 트랜잭션 메서드
5051
@Transactional(propagation = Propagation.REQUIRES_NEW)
@@ -139,7 +140,7 @@ private void handleImageGeneration(Scenario scenario, String imagePrompt) {
139140
try {
140141
if (imagePrompt != null && !imagePrompt.trim().isEmpty()) {
141142
String imageUrl = aiService.generateImage(imagePrompt)
142-
.orTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
143+
.orTimeout(imageAiConfig.getTimeoutSeconds(), java.util.concurrent.TimeUnit.SECONDS)
143144
.exceptionally(ex -> {
144145
log.warn("Image generation timeout or error for scenario {}: {}",
145146
scenario.getId(), ex.getMessage());

back/src/main/java/com/back/global/ai/config/BaseScenarioAiProperties.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
@ConfigurationProperties(prefix = "ai.base-scenario")
1010
public class BaseScenarioAiProperties {
1111
private int maxOutputTokens = 1000;
12+
private int timeoutSeconds = 60;
1213

1314
// getters/setters
1415
public int getMaxOutputTokens() { return maxOutputTokens; }
1516
public void setMaxOutputTokens(int maxOutputTokens) { this.maxOutputTokens = maxOutputTokens; }
17+
public int getTimeoutSeconds() { return timeoutSeconds; }
18+
public void setTimeoutSeconds(int timeoutSeconds) { this.timeoutSeconds = timeoutSeconds; }
1619
}

back/src/main/java/com/back/global/ai/config/DecisionScenarioAiProperties.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
@ConfigurationProperties(prefix = "ai.decision-scenario")
1010
public class DecisionScenarioAiProperties {
1111
private int maxOutputTokens = 1200;
12+
private int timeoutSeconds = 60;
1213

1314
// getters/setters
1415
public int getMaxOutputTokens() { return maxOutputTokens; }
1516
public void setMaxOutputTokens(int maxOutputTokens) { this.maxOutputTokens = maxOutputTokens; }
17+
public int getTimeoutSeconds() { return timeoutSeconds; }
18+
public void setTimeoutSeconds(int timeoutSeconds) { this.timeoutSeconds = timeoutSeconds; }
1619
}

back/src/main/resources/application.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ ai:
9090
api-key: ${GEMINI_API_KEY}
9191
model: gemini-2.5-flash
9292
base-url: https://generativelanguage.googleapis.com
93-
timeout-seconds: 180 # 테스트용 타임아웃 증가 (120 → 180초)
94-
max-retries: 0 # 재시도 비활성화 (테스트용)
93+
timeout-seconds: 70 # AI 응답 대기 시간 (시나리오 생성: 30-40초 + 여유 30초)
94+
max-retries: 0 # 재시도 비활성화 (타임아웃 방지)
9595
retry-delay-seconds: 2 # 재시도 간격 (초)
9696
image:
9797
enabled: true
@@ -112,10 +112,10 @@ ai:
112112
timeout-seconds: 30 # 상황 생성 타임아웃 (30초, 실시간 응답용)
113113
base-scenario:
114114
maxOutputTokens: 16384 # 8192 → 16384 (gemini-2.5-flash 최대 65536, 충분한 여유)
115-
timeout-seconds: 180 # 베이스 시나리오 생성 타임아웃 (60 → 180초, 테스트용)
115+
timeout-seconds: 60 # 베이스 시나리오 생성 타임아웃 (실제: 30-40초 + 여유)
116116
decision-scenario:
117117
maxOutputTokens: 16384 # 8192 -> 16384 (gemini-2.5-flash 최대 65536, 충분한 여유)
118-
timeout-seconds: 60 # 선택 시나리오 생성 타임아웃 (60초)
118+
timeout-seconds: 60 # 결정 시나리오 생성 타임아웃 (실제: 30-40초 + 여유)
119119

120120
server:
121121
servlet:

back/src/test/java/com/back/domain/scenario/service/ScenarioServiceTest.java

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class CreateScenarioTests {
8181
ReflectionTestUtils.setField(savedScenario, "id", 1001L);
8282

8383
// 실제 ScenarioService 구현에 맞춘 모킹
84-
given(decisionLineRepository.findById(decisionLineId))
84+
given(decisionLineRepository.findWithUserById(decisionLineId))
8585
.willReturn(Optional.of(mockDecisionLine));
8686
given(scenarioRepository.findByDecisionLineId(decisionLineId))
8787
.willReturn(Optional.empty()); // 기존 시나리오 없음
@@ -101,7 +101,7 @@ class CreateScenarioTests {
101101
assertThat(result.message()).isEqualTo("시나리오 생성이 시작되었습니다.");
102102

103103
// 동기 부분의 핵심 비즈니스 로직만 검증
104-
verify(decisionLineRepository).findById(decisionLineId);
104+
verify(decisionLineRepository).findWithUserById(decisionLineId);
105105
verify(scenarioRepository).findByDecisionLineId(decisionLineId);
106106
verify(scenarioRepository).save(any(Scenario.class));
107107

@@ -117,15 +117,15 @@ class CreateScenarioTests {
117117
Long decisionLineId = 999L;
118118
ScenarioCreateRequest request = new ScenarioCreateRequest(decisionLineId);
119119

120-
given(decisionLineRepository.findById(decisionLineId))
120+
given(decisionLineRepository.findWithUserById(decisionLineId))
121121
.willReturn(Optional.empty());
122122

123123
// When & Then
124124
assertThatThrownBy(() -> scenarioService.createScenario(userId, request, null))
125125
.isInstanceOf(ApiException.class)
126126
.hasFieldOrPropertyWithValue("errorCode", ErrorCode.DECISION_LINE_NOT_FOUND);
127127

128-
verify(decisionLineRepository).findById(decisionLineId);
128+
verify(decisionLineRepository).findWithUserById(decisionLineId);
129129
verify(scenarioRepository, never()).save(any());
130130
}
131131

@@ -146,15 +146,15 @@ class CreateScenarioTests {
146146
.build();
147147
ReflectionTestUtils.setField(mockDecisionLine, "id", decisionLineId);
148148

149-
given(decisionLineRepository.findById(decisionLineId))
149+
given(decisionLineRepository.findWithUserById(decisionLineId))
150150
.willReturn(Optional.of(mockDecisionLine));
151151

152152
// When & Then
153153
assertThatThrownBy(() -> scenarioService.createScenario(userId, request, null))
154154
.isInstanceOf(ApiException.class)
155155
.hasFieldOrPropertyWithValue("errorCode", ErrorCode.HANDLE_ACCESS_DENIED);
156156

157-
verify(decisionLineRepository).findById(decisionLineId);
157+
verify(decisionLineRepository).findWithUserById(decisionLineId);
158158
verify(scenarioRepository, never()).save(any());
159159
}
160160

@@ -182,7 +182,7 @@ class CreateScenarioTests {
182182
.build();
183183
ReflectionTestUtils.setField(existingScenario, "id", 999L);
184184

185-
given(decisionLineRepository.findById(decisionLineId))
185+
given(decisionLineRepository.findWithUserById(decisionLineId))
186186
.willReturn(Optional.of(mockDecisionLine));
187187
given(scenarioRepository.findByDecisionLineId(decisionLineId))
188188
.willReturn(Optional.of(existingScenario)); // 기존 PENDING 시나리오 존재
@@ -220,6 +220,7 @@ class ProcessScenarioGenerationAsyncTests {
220220
Scenario mockScenario = Scenario.builder()
221221
.user(mockUser)
222222
.decisionLine(mockDecisionLine)
223+
.baseLine(mockBaseLine)
223224
.status(ScenarioStatus.PENDING)
224225
.build();
225226
ReflectionTestUtils.setField(mockScenario, "id", scenarioId);
@@ -228,9 +229,9 @@ class ProcessScenarioGenerationAsyncTests {
228229
doNothing().when(scenarioTransactionService).updateScenarioStatus(anyLong(), any(), any());
229230
doNothing().when(scenarioTransactionService).saveAiResult(anyLong(), any());
230231

231-
// executeAiGeneration에서 사용되는 repository 조회 모킹
232-
given(scenarioRepository.findById(scenarioId))
233-
.willReturn(Optional.of(mockScenario));
232+
// prepareScenarioData 모킹 추가 (핵심!)
233+
given(scenarioTransactionService.prepareScenarioData(scenarioId))
234+
.willReturn(mockScenario);
234235

235236
// 베이스 시나리오가 이미 존재하도록 설정 (AI 호출 방지)
236237
Scenario mockBaseScenario = Scenario.builder()
@@ -253,6 +254,7 @@ class ProcessScenarioGenerationAsyncTests {
253254
// Then
254255
// 트랜잭션 서비스 호출 검증
255256
verify(scenarioTransactionService).updateScenarioStatus(scenarioId, ScenarioStatus.PROCESSING, null);
257+
verify(scenarioTransactionService).prepareScenarioData(scenarioId);
256258
verify(scenarioTransactionService).updateScenarioStatus(scenarioId, ScenarioStatus.COMPLETED, null);
257259
}
258260

@@ -265,16 +267,17 @@ class ProcessScenarioGenerationAsyncTests {
265267
// 트랜잭션 서비스는 정상 동작하도록 모킹
266268
doNothing().when(scenarioTransactionService).updateScenarioStatus(anyLong(), any(), any());
267269

268-
// executeAiGeneration에서 시나리오를 찾을 수 없음
269-
given(scenarioRepository.findById(scenarioId))
270-
.willReturn(Optional.empty());
270+
// prepareScenarioData에서 시나리오를 찾을 수 없음 (예외 발생)
271+
given(scenarioTransactionService.prepareScenarioData(scenarioId))
272+
.willThrow(new ApiException(ErrorCode.SCENARIO_NOT_FOUND));
271273

272274
// When
273275
scenarioService.processScenarioGenerationAsync(scenarioId);
274276

275277
// Then
276278
// 실패 시 FAILED 상태 업데이트가 호출되어야 함
277279
verify(scenarioTransactionService).updateScenarioStatus(scenarioId, ScenarioStatus.PROCESSING, null);
280+
verify(scenarioTransactionService).prepareScenarioData(scenarioId);
278281
verify(scenarioTransactionService).updateScenarioStatus(eq(scenarioId), eq(ScenarioStatus.FAILED), anyString());
279282
}
280283
}
@@ -295,7 +298,7 @@ class GetScenarioStatusTests {
295298
.build();
296299
ReflectionTestUtils.setField(mockScenario, "id", scenarioId);
297300

298-
given(scenarioRepository.findByIdAndUserId(scenarioId, userId))
301+
given(scenarioRepository.findByIdAndUserIdForStatusCheck(scenarioId, userId))
299302
.willReturn(Optional.of(mockScenario));
300303

301304
// When
@@ -307,7 +310,7 @@ class GetScenarioStatusTests {
307310
assertThat(result.status()).isEqualTo(ScenarioStatus.COMPLETED);
308311
assertThat(result.message()).isEqualTo("시나리오 생성이 완료되었습니다.");
309312

310-
verify(scenarioRepository).findByIdAndUserId(scenarioId, userId);
313+
verify(scenarioRepository).findByIdAndUserIdForStatusCheck(scenarioId, userId);
311314
}
312315

313316
@Test
@@ -317,15 +320,15 @@ class GetScenarioStatusTests {
317320
Long scenarioId = 999L;
318321
Long userId = 1L;
319322

320-
given(scenarioRepository.findByIdAndUserId(scenarioId, userId))
323+
given(scenarioRepository.findByIdAndUserIdForStatusCheck(scenarioId, userId))
321324
.willReturn(Optional.empty());
322325

323326
// When & Then
324327
assertThatThrownBy(() -> scenarioService.getScenarioStatus(scenarioId, userId))
325328
.isInstanceOf(ApiException.class)
326329
.hasFieldOrPropertyWithValue("errorCode", ErrorCode.SCENARIO_NOT_FOUND);
327330

328-
verify(scenarioRepository).findByIdAndUserId(scenarioId, userId);
331+
verify(scenarioRepository).findByIdAndUserIdForStatusCheck(scenarioId, userId);
329332
}
330333
}
331334

@@ -427,7 +430,7 @@ class GetStatusMessageTests {
427430
.status(ScenarioStatus.PENDING)
428431
.build();
429432
ReflectionTestUtils.setField(pendingScenario, "id", scenarioId);
430-
given(scenarioRepository.findByIdAndUserId(scenarioId, userId))
433+
given(scenarioRepository.findByIdAndUserIdForStatusCheck(scenarioId, userId))
431434
.willReturn(Optional.of(pendingScenario));
432435

433436
ScenarioStatusResponse pendingResult = scenarioService.getScenarioStatus(scenarioId, userId);
@@ -438,7 +441,7 @@ class GetStatusMessageTests {
438441
.status(ScenarioStatus.PROCESSING)
439442
.build();
440443
ReflectionTestUtils.setField(processingScenario, "id", scenarioId);
441-
given(scenarioRepository.findByIdAndUserId(scenarioId, userId))
444+
given(scenarioRepository.findByIdAndUserIdForStatusCheck(scenarioId, userId))
442445
.willReturn(Optional.of(processingScenario));
443446

444447
ScenarioStatusResponse processingResult = scenarioService.getScenarioStatus(scenarioId, userId);
@@ -449,7 +452,7 @@ class GetStatusMessageTests {
449452
.status(ScenarioStatus.COMPLETED)
450453
.build();
451454
ReflectionTestUtils.setField(completedScenario, "id", scenarioId);
452-
given(scenarioRepository.findByIdAndUserId(scenarioId, userId))
455+
given(scenarioRepository.findByIdAndUserIdForStatusCheck(scenarioId, userId))
453456
.willReturn(Optional.of(completedScenario));
454457

455458
ScenarioStatusResponse completedResult = scenarioService.getScenarioStatus(scenarioId, userId);
@@ -460,7 +463,7 @@ class GetStatusMessageTests {
460463
.status(ScenarioStatus.FAILED)
461464
.build();
462465
ReflectionTestUtils.setField(failedScenario, "id", scenarioId);
463-
given(scenarioRepository.findByIdAndUserId(scenarioId, userId))
466+
given(scenarioRepository.findByIdAndUserIdForStatusCheck(scenarioId, userId))
464467
.willReturn(Optional.of(failedScenario));
465468

466469
ScenarioStatusResponse failedResult = scenarioService.getScenarioStatus(scenarioId, userId);

0 commit comments

Comments
 (0)