Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions modules/quiz-make/impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,14 @@ dependencies {
// 예: 서버 기동 후 /swagger-ui/index.html 접속하면 API 목록·테스트 UI가 제공됨
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8"

// ────────────────────────────────
// 테스트
// ────────────────────────────────
testImplementation "org.springframework.boot:spring-boot-starter-test"
testImplementation platform("org.junit:junit-bom:5.12.2")
testRuntimeOnly "org.junit.platform:junit-platform-launcher"
}

test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private void processGenerationAsync(
GenerationRequestToAI requestToAI =
GenerationRequestToAI.builder()
.fileUrl(request.uploadedUrl())
.strategyValue(request.quizType().name())
.strategyValue(request.quizType().toAiStrategyName())
.language(request.language().name())
.quizCount(request.quizCount())
.referencePages(request.pageNumbers())
Expand All @@ -113,7 +113,9 @@ private void processGenerationAsync(

// 2. 선택지 셔플
QuizType quizType = request.quizType();
if (quizType == QuizType.MULTIPLE || quizType == QuizType.BLANK) {
if (quizType == QuizType.MULTIPLE
|| quizType == QuizType.BLANK
|| quizType == QuizType.REAL_BLANK) {
for (var quiz : problemSet.getQuiz()) {
if (!CollectionUtils.isEmpty(quiz.getSelections())) {
List<SelectionsOfAI> shuffled = new ArrayList<>(quiz.getSelections());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.icc.qasker.quizmake.service.generation;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.icc.qasker.ai.dto.GenerationRequestToAI;
import com.icc.qasker.global.component.HashUtil;
import com.icc.qasker.quizmake.SseNotificationService;
import com.icc.qasker.quizmake.adapter.AIServerAdapter;
import com.icc.qasker.quizmake.dto.ferequest.GenerationRequest;
import com.icc.qasker.quizmake.dto.ferequest.enums.Language;
import com.icc.qasker.quizset.QuizCommandService;
import com.icc.qasker.quizset.QuizQueryService;
import com.icc.qasker.quizset.dto.ferequest.enums.QuizType;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;

class GenerationCommandServiceImplTest {

private AIServerAdapter aiServerAdapter;
private SseNotificationService notificationService;
private QuizCommandService quizCommandService;
private QuizQueryService quizQueryService;
private HashUtil hashUtil;
private GenerationResultRecorder resultRecorder;
private GenerationCommandServiceImpl service;

@BeforeEach
void setUp() {
aiServerAdapter = mock(AIServerAdapter.class);
notificationService = mock(SseNotificationService.class);
quizCommandService = mock(QuizCommandService.class);
quizQueryService = mock(QuizQueryService.class);
hashUtil = mock(HashUtil.class);
resultRecorder = mock(GenerationResultRecorder.class);

when(quizCommandService.initProblemSet(any(), any(), any(), anyInt(), any(), any(), any()))
.thenReturn(1L);

service =
new GenerationCommandServiceImpl(
aiServerAdapter,
notificationService,
quizCommandService,
quizQueryService,
hashUtil,
resultRecorder);
}

@Test
@DisplayName("REAL_BLANK 요청은 DB에는 REAL_BLANK 그대로 저장한다")
void real_blank_request_persists_real_blank_to_db() {
GenerationRequest request = request(QuizType.REAL_BLANK);

service.triggerGeneration("user-1", request);

verify(quizCommandService, timeout(2000))
.initProblemSet(
eq("user-1"), any(), any(), anyInt(), eq(QuizType.REAL_BLANK), any(), any());
}

@Test
@DisplayName("REAL_BLANK 요청은 AI 서버에 strategyValue=BLANK로 전달한다")
void real_blank_request_calls_ai_with_blank_strategy() {
GenerationRequest request = request(QuizType.REAL_BLANK);

service.triggerGeneration("user-1", request);

ArgumentCaptor<GenerationRequestToAI> captor =
ArgumentCaptor.forClass(GenerationRequestToAI.class);
verify(aiServerAdapter, timeout(2000)).streamRequest(captor.capture());
assertThat(captor.getValue().strategyValue()).isEqualTo("BLANK");
}

@Test
@DisplayName("BLANK 요청은 AI 서버에 strategyValue=BLANK로 전달한다 (회귀 방지)")
void blank_request_calls_ai_with_blank_strategy() {
GenerationRequest request = request(QuizType.BLANK);

service.triggerGeneration("user-1", request);

ArgumentCaptor<GenerationRequestToAI> captor =
ArgumentCaptor.forClass(GenerationRequestToAI.class);
verify(aiServerAdapter, timeout(2000)).streamRequest(captor.capture());
assertThat(captor.getValue().strategyValue()).isEqualTo("BLANK");
}

@Test
@DisplayName("MULTIPLE 요청은 AI 서버에 strategyValue=MULTIPLE로 전달한다 (회귀 방지)")
void multiple_request_calls_ai_with_multiple_strategy() {
GenerationRequest request = request(QuizType.MULTIPLE);

service.triggerGeneration("user-1", request);

ArgumentCaptor<GenerationRequestToAI> captor =
ArgumentCaptor.forClass(GenerationRequestToAI.class);
verify(aiServerAdapter, timeout(2000)).streamRequest(captor.capture());
assertThat(captor.getValue().strategyValue()).isEqualTo("MULTIPLE");
}

private GenerationRequest request(QuizType quizType) {
return new GenerationRequest(
null,
UUID.randomUUID().toString(),
"https://example.com/file.pdf",
"title",
5,
quizType,
List.of(1, 2),
Language.KO);
}
}
11 changes: 11 additions & 0 deletions modules/quiz-set/api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,15 @@ dependencies {
// Spring WebMVC: SseEmitter 등 웹 관련 타입 제공
// 예: GenerationService 인터페이스에서 SseEmitter 반환 타입 사용
implementation "org.springframework:spring-webmvc"

// ────────────────────────────────
// 테스트
// ────────────────────────────────
testImplementation "org.springframework.boot:spring-boot-starter-test"
testImplementation platform("org.junit:junit-bom:5.12.2")
testRuntimeOnly "org.junit.platform:junit-platform-launcher"
}

test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
public enum QuizType {
MULTIPLE,
BLANK,
REAL_BLANK,
OX,
ESSAY
ESSAY;

public String toAiStrategyName() {
return this == REAL_BLANK ? BLANK.name() : name();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.icc.qasker.quizset.dto.ferequest.enums;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

class QuizTypeTest {

@Test
@DisplayName("REAL_BLANK는 AI 전략 이름을 BLANK로 매핑한다")
void real_blank_maps_to_blank() {
assertThat(QuizType.REAL_BLANK.toAiStrategyName()).isEqualTo("BLANK");
}

@Test
@DisplayName("BLANK는 자기 자신의 이름을 그대로 반환한다")
void blank_keeps_own_name() {
assertThat(QuizType.BLANK.toAiStrategyName()).isEqualTo("BLANK");
}

@Test
@DisplayName("MULTIPLE은 자기 자신의 이름을 그대로 반환한다")
void multiple_keeps_own_name() {
assertThat(QuizType.MULTIPLE.toAiStrategyName()).isEqualTo("MULTIPLE");
}

@Test
@DisplayName("OX는 자기 자신의 이름을 그대로 반환한다")
void ox_keeps_own_name() {
assertThat(QuizType.OX.toAiStrategyName()).isEqualTo("OX");
}

@Test
@DisplayName("ESSAY는 자기 자신의 이름을 그대로 반환한다")
void essay_keeps_own_name() {
assertThat(QuizType.ESSAY.toAiStrategyName()).isEqualTo("ESSAY");
}
}
Loading