diff --git a/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/api/QnaSetController.java b/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/api/QnaSetController.java index cab52394e..cf22b3dbf 100644 --- a/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/api/QnaSetController.java +++ b/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/api/QnaSetController.java @@ -42,6 +42,7 @@ public class QnaSetController { private final QnaSetService qnaSetService; private final StarAnalysisAsyncService starAnalysisAsyncService; + // TODO 통합 테스트 작성 @Operation( summary = "지정한 산업군 / 직무의 빈출 질문 답변 세트를 조회합니다.", description = "지정한 산업군 / 직무의 빈출 질문 답변 세트를 조회합니다. 지정하지 않은 필드에 대해서는 전체를 대상으로 조회합니다.") @@ -71,11 +72,10 @@ public ResponseEntity> unmarkDifficultQuestion(@PathVariable L return ResponseEntity.ok(response); } - @Operation(summary = "지정한 질문 답변 세트의 질문 답변 내용을 수정합니다.", description = "질문 답변 내용 수정은 '기록중' 상태에서만 가능합니다.") + @Operation(summary = "지정한 질문 답변 세트의 질문 답변 내용을 수정합니다.", description = "질문 답변 내용 수정은 'QnaSetDraft' 상태에서만 가능합니다.") @PutMapping("/{qnaSetId}") public ResponseEntity> updateQnaSet( @PathVariable Long qnaSetId, @Valid @RequestBody QnaSetUpdateRequest request) { - // TODO : 질문 답변 내용 수정이 '기록중' 상태에서만 가능하도록 검증 추가 qnaSetService.updateQnaSet(qnaSetId, request); var response = ApiResponse.success(COMMON200); return ResponseEntity.ok(response); @@ -89,7 +89,9 @@ public ResponseEntity> deleteQnaSet(@PathVariable Long qnaSetI return ResponseEntity.ok(resposne); } - @Operation(summary = "지정한 질문 답변 세트에 대해 PDF 하이라이팅 정보를 등록/수정합니다.") + @Operation( + summary = "지정한 질문 답변 세트에 대해 PDF 하이라이팅 정보를 등록/수정합니다.", + description = "PDF 하이라이팅 정보 등록/수정은 'QnaSetDraft' 상태에서만 가능합니다.") @PutMapping("/{qnaSetId}/pdf-highlightings") public ResponseEntity> updatePdfHighlighting( @PathVariable Long qnaSetId, @Valid @RequestBody List request) { @@ -106,6 +108,7 @@ public ResponseEntity>> getPdfHighlightings return ResponseEntity.ok(response); } + // TODO 통합 테스트 작성 (E2E 테스트는 일단 보류) @Operation(summary = "지정한 질문 답변 세트에 대해 스타 분석 생성을 요청합니다.", description = "Gemini 요청을 수행하고 10~20초 뒤에 응답이 반환됩니다.") @PostMapping("/{qnaSetId}/star-analysis") public CompletableFuture>> createStarAnalysis( @@ -115,6 +118,7 @@ public CompletableFuture>> createSta .thenApply(rsp -> ResponseEntity.ok(ApiResponse.success(COMMON200, rsp))); } + // TODO 통합 테스트 작성 @Operation(summary = "지정한 질문 답변 세트가 스크랩 폴더에 포함되어 있는 지 여부가 포함된 스크랩 폴더 리스트를 조회합니다.") @GetMapping("/{qnaSetId}/scrap-folder") public ResponseEntity>> getScrapFoldersContainingQnaSet( diff --git a/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/model/QnaSet.java b/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/model/QnaSet.java index 02f76e1b3..f3519b60c 100644 --- a/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/model/QnaSet.java +++ b/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/model/QnaSet.java @@ -79,7 +79,7 @@ public void markDifficult() { } public void unmarkDifficult() { - this.isMarkedDifficult = true; + this.isMarkedDifficult = false; } @Builder(access = AccessLevel.PRIVATE) diff --git a/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/service/QnaSetService.java b/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/service/QnaSetService.java index 8f9ef44f6..2c3ca2916 100644 --- a/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/service/QnaSetService.java +++ b/backend/src/main/java/com/shyashyashya/refit/domain/qnaset/service/QnaSetService.java @@ -75,6 +75,7 @@ public void unmarkDifficultQuestion(Long qnaSetId) { @Transactional public void updateQnaSet(Long qnaSetId, QnaSetUpdateRequest request) { QnaSet qnaSet = getValidatedQnaSet(qnaSetId); + interviewValidator.validateInterviewReviewStatus(qnaSet.getInterview(), InterviewReviewStatus.QNA_SET_DRAFT); qnaSet.updateQuestionText(request.questionText()); qnaSet.updateAnswerText(request.answerText()); updateOrCreateSelfReview(qnaSet, request.selfReviewText()); @@ -90,6 +91,7 @@ public void deleteQnaSet(Long qnaSetId) { @Transactional public void updatePdfHighlighting(Long qnaSetId, List request) { QnaSet qnaSet = getValidatedQnaSet(qnaSetId); + interviewValidator.validateInterviewReviewStatus(qnaSet.getInterview(), InterviewReviewStatus.QNA_SET_DRAFT); deleteAllHighlightingsAndRects(qnaSet); saveAllHighlightings(qnaSet, request); } @@ -135,13 +137,6 @@ public Page getMyScrapFoldersWithQnaSetContainingInfo return qnaSetScrapFolderRepository.findAllScrapFoldersWithQnaSetContainingInfo(requestUser, qnaSet, pageable); } - private List removeDuplicatedIds(List list) { - if (list == null) { - return null; - } - return list.stream().distinct().toList(); - } - private QnaSet getValidatedQnaSet(Long qnaSetId) { QnaSet qnaSet = qnaSetRepository.findById(qnaSetId).orElseThrow(() -> new CustomException(QNA_SET_NOT_FOUND)); User requestUser = requestUserContext.getRequestUser(); diff --git a/backend/src/test/java/com/shyashyashya/refit/core/IntegrationTest.java b/backend/src/test/java/com/shyashyashya/refit/core/IntegrationTest.java index 49769b325..177dfaa2f 100644 --- a/backend/src/test/java/com/shyashyashya/refit/core/IntegrationTest.java +++ b/backend/src/test/java/com/shyashyashya/refit/core/IntegrationTest.java @@ -7,11 +7,19 @@ import com.shyashyashya.refit.domain.industry.model.Industry; import com.shyashyashya.refit.domain.industry.repository.IndustryRepository; import com.shyashyashya.refit.domain.interview.dto.request.InterviewCreateRequest; +import com.shyashyashya.refit.domain.interview.dto.request.QnaSetCreateRequest; import com.shyashyashya.refit.domain.interview.model.Interview; import com.shyashyashya.refit.domain.interview.model.InterviewReviewStatus; import com.shyashyashya.refit.domain.interview.repository.InterviewRepository; import com.shyashyashya.refit.domain.jobcategory.model.JobCategory; import com.shyashyashya.refit.domain.jobcategory.repository.JobCategoryRepository; +import com.shyashyashya.refit.domain.qnaset.dto.request.PdfHighlightingUpdateRequest; +import com.shyashyashya.refit.domain.qnaset.model.PdfHighlighting; +import com.shyashyashya.refit.domain.qnaset.model.PdfHighlightingRect; +import com.shyashyashya.refit.domain.qnaset.model.QnaSet; +import com.shyashyashya.refit.domain.qnaset.repository.PdfHighlightingRectRepository; +import com.shyashyashya.refit.domain.qnaset.repository.PdfHighlightingRepository; +import com.shyashyashya.refit.domain.qnaset.repository.QnaSetRepository; import com.shyashyashya.refit.domain.user.model.User; import com.shyashyashya.refit.domain.user.repository.UserRepository; import com.shyashyashya.refit.global.auth.service.JwtEncoder; @@ -80,6 +88,15 @@ public abstract class IntegrationTest { @Autowired private CompanyRepository companyRepository; + @Autowired + private QnaSetRepository qnaSetRepository; + + @Autowired + private PdfHighlightingRepository pdfHighlightingRepository; + + @Autowired + private PdfHighlightingRectRepository pdfHighlightingRectRepository; + @BeforeEach void restAssuredSetUp() { clearDatabase(); @@ -188,4 +205,41 @@ protected Company createAndSaveCompany(String companyName) { Company company = Company.create(companyName, "logo.url", true); return companyRepository.save(company); } + + protected QnaSet createQnaSet(QnaSetCreateRequest request, Interview interview, boolean isMarkedDifficult) { + QnaSet qnaSet = QnaSet.create( + request.questionText(), + request.answerText(), + isMarkedDifficult, + interview, + null + ); + + return qnaSetRepository.save(qnaSet); + } + + protected List createAndSavePdfHighlighting(List requests, QnaSet qnaSet) { + List result = new ArrayList<>(); + + requests.forEach(request -> { + PdfHighlighting pdfHighlighting = PdfHighlighting.create(request.highlightingText(), qnaSet); + result.add(pdfHighlightingRepository.save(pdfHighlighting)); + + request.rects().forEach( + rectDto -> { + PdfHighlightingRect rect = PdfHighlightingRect.create( + rectDto.x(), + rectDto.y(), + rectDto.width(), + rectDto.height(), + rectDto.pageNumber(), + pdfHighlighting); + + pdfHighlightingRectRepository.save(rect); + } + ); + }); + + return result; + } } diff --git a/backend/src/test/java/com/shyashyashya/refit/integration/qnaset/QnaSetIntegrationTest.java b/backend/src/test/java/com/shyashyashya/refit/integration/qnaset/QnaSetIntegrationTest.java new file mode 100644 index 000000000..56eb5c31c --- /dev/null +++ b/backend/src/test/java/com/shyashyashya/refit/integration/qnaset/QnaSetIntegrationTest.java @@ -0,0 +1,516 @@ +package com.shyashyashya.refit.integration.qnaset; + +import static com.shyashyashya.refit.global.exception.ErrorCode.INTERVIEW_NOT_ACCESSIBLE; +import static com.shyashyashya.refit.global.exception.ErrorCode.INTERVIEW_REVIEW_STATUS_VALIDATION_FAILED; +import static com.shyashyashya.refit.global.exception.ErrorCode.QNA_SET_NOT_FOUND; +import static com.shyashyashya.refit.global.model.ResponseCode.COMMON200; +import static com.shyashyashya.refit.global.model.ResponseCode.COMMON204; +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +import com.shyashyashya.refit.core.IntegrationTest; +import com.shyashyashya.refit.domain.interview.dto.request.InterviewCreateRequest; +import com.shyashyashya.refit.domain.interview.dto.request.QnaSetCreateRequest; +import com.shyashyashya.refit.domain.interview.model.Interview; +import com.shyashyashya.refit.domain.interview.model.InterviewReviewStatus; +import com.shyashyashya.refit.domain.interview.model.InterviewType; +import com.shyashyashya.refit.domain.interview.repository.InterviewRepository; +import com.shyashyashya.refit.domain.qnaset.dto.PdfHighlightingRectDto; +import com.shyashyashya.refit.domain.qnaset.dto.request.PdfHighlightingUpdateRequest; +import com.shyashyashya.refit.domain.qnaset.dto.request.QnaSetUpdateRequest; +import com.shyashyashya.refit.domain.qnaset.model.QnaSet; +import com.shyashyashya.refit.domain.qnaset.repository.QnaSetRepository; +import com.shyashyashya.refit.domain.user.model.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.util.List; + +public class QnaSetIntegrationTest extends IntegrationTest { + + @Autowired + private InterviewRepository interviewRepository; + @Autowired + private QnaSetRepository qnaSetRepository; + + @Nested + class 질답_세트_생성_시 { + + @Test + void 인터뷰가_QNA_SET_DRAFT_상태이면_질답_세트_생성에_성공한다() { + // given + InterviewCreateRequest interviewCreateRequest = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + Interview interview1 = createAndSaveInterview(interviewCreateRequest, InterviewReviewStatus.QNA_SET_DRAFT); + QnaSetCreateRequest request = new QnaSetCreateRequest("test question text", "test answer text"); + + // when & then + given(spec) + .body(request) + .when() + .post("/interview/" + interview1.getId() + "/qna-set") + .then() + .statusCode(200) + .body("code", equalTo(COMMON200.name())) + .body("message", equalTo(COMMON200.getMessage())) + .body("result", notNullValue()) + .body("result.qnaSetId", notNullValue()); + } + + @Test + void 인터뷰가_QNA_SET_DRAFT_상태가_아니라면_질답_세트_생성에_실패한다() { + // given + InterviewCreateRequest interviewCreateRequest = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + Interview interview1 = createAndSaveInterview(interviewCreateRequest); + QnaSetCreateRequest request = new QnaSetCreateRequest("test question text", "test answer text"); + + // when & then + given(spec) + .body(request) + .when() + .post("/interview/" + interview1.getId() + "/qna-set") + .then() + .statusCode(400) + .body("code", equalTo(INTERVIEW_REVIEW_STATUS_VALIDATION_FAILED.name())) + .body("message", equalTo(INTERVIEW_REVIEW_STATUS_VALIDATION_FAILED.getMessage())) + .body("result", nullValue()); + } + } + + @Nested + class 어려웠던_질문_마킹_시 { + + private Interview interview; + private Long qnaSetId; + + @BeforeEach + void setUp() { + interview = createAndSaveInterview( + new InterviewCreateRequest( + LocalDateTime.of(2023, 1, 10, 10, 0, 0), InterviewType.FIRST, company1.getName(), industry1.getId(), jobCategory1.getId(), "Developer" + )); + QnaSetCreateRequest qnaSetCreateRequest = new QnaSetCreateRequest ("test qqq text", "test aaa text"); + QnaSet qnaSet = createQnaSet(qnaSetCreateRequest, interview, false); + qnaSetId = qnaSet.getId(); + } + + @Test + void 어려웠던_질문_마킹에_성공한다() { + // given + + // when & then + given(spec). + when(). + patch("/qna-set/" + qnaSetId + "/difficult/mark"). + then(). + statusCode(200). + body("code", equalTo(COMMON200.name())). + body("message", equalTo(COMMON200.getMessage())). + body("result", nullValue()); + + QnaSet result = qnaSetRepository.findById(qnaSetId).get(); + assertThat(result.isMarkedDifficult()).isTrue(); + } + + @Test + void 어려웠던_질문_마킹_해제에_성공한다() { + // given + + // when & then + given(spec). + when(). + patch("/qna-set/" + qnaSetId + "/difficult/unmark"). + then(). + statusCode(200). + body("code", equalTo(COMMON200.name())). + body("message", equalTo(COMMON200.getMessage())). + body("result", nullValue()); + + QnaSet result = qnaSetRepository.findById(qnaSetId).get(); + assertThat(result.isMarkedDifficult()).isFalse(); + } + } + + @Nested + class 질답_세트_수정_시 { + + private Long qnaSetDraftQnaSetId; + private Long debriefCompletedQnaSetId; + + @BeforeEach + void setUp() { + InterviewCreateRequest interviewCreateRequest1 = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + Interview qnaSetDraftInterview = createAndSaveInterview(interviewCreateRequest1, InterviewReviewStatus.QNA_SET_DRAFT); + + InterviewCreateRequest interviewCreateRequest2 = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + Interview debriefCompletedInterview = createAndSaveInterview(interviewCreateRequest2, InterviewReviewStatus.DEBRIEF_COMPLETED); + + QnaSetCreateRequest qnaSetCreateRequest1 = new QnaSetCreateRequest("test question text", "test answer text"); + QnaSet qnaSetDraftQnaSet = createQnaSet(qnaSetCreateRequest1, qnaSetDraftInterview, true); + qnaSetDraftQnaSetId = qnaSetDraftQnaSet.getId(); + + QnaSetCreateRequest qnaSetCreateRequest2 = new QnaSetCreateRequest("test question text", "test answer text"); + QnaSet debriefCompletedQnaSet = createQnaSet(qnaSetCreateRequest2, debriefCompletedInterview, true); + debriefCompletedQnaSetId = debriefCompletedQnaSet.getId(); + } + + @Test + void 인터뷰가_QNA_SET_DRAFT_상태이면_질답_세트_수정에_성공한다() { + // given + QnaSetUpdateRequest qnaSetUpdateRequest = new QnaSetUpdateRequest("update question", "update answer", "self review self review"); + + // when & then + given(spec) + .body(qnaSetUpdateRequest) + .when() + .put("/qna-set/" + qnaSetDraftQnaSetId) + .then() + .statusCode(200) + .body("code", equalTo(COMMON200.name())) + .body("message", equalTo(COMMON200.getMessage())) + .body("result", nullValue()); + } + + @Test + void 수정_요청에_필드가_하나일_때_질답_세트_수정에_성공한다() { + // given + QnaSetUpdateRequest qnaSetUpdateRequest = new QnaSetUpdateRequest("only question text update", null, null); + + // when & then + given(spec) + .body(qnaSetUpdateRequest) + .when() + .put("/qna-set/" + qnaSetDraftQnaSetId) + .then() + .statusCode(200) + .body("code", equalTo(COMMON200.name())) + .body("message", equalTo(COMMON200.getMessage())) + .body("result", nullValue()); + } + + @Test + void 수정_요청이_빈_문자열일_때_질답_세트_수정에_성공한다() { + // given + QnaSetUpdateRequest qnaSetUpdateRequest = new QnaSetUpdateRequest("", null, ""); + + // when & then + given(spec) + .body(qnaSetUpdateRequest) + .when() + .put("/qna-set/" + qnaSetDraftQnaSetId) + .then() + .statusCode(200) + .body("code", equalTo(COMMON200.name())) + .body("message", equalTo(COMMON200.getMessage())) + .body("result", nullValue()); + } + + @Test + void 인터뷰가_QNA_SET_DRAFT_상태가_아니라면_질답_세트_수정에_실패한다() { + // given + QnaSetUpdateRequest qnaSetUpdateRequest = new QnaSetUpdateRequest("update question", "update answer", null); + + // when & then + given(spec) + .body(qnaSetUpdateRequest) + .when() + .put("/qna-set/" + debriefCompletedQnaSetId) + .then() + .statusCode(400) + .body("code", equalTo(INTERVIEW_REVIEW_STATUS_VALIDATION_FAILED.name())) + .body("message", equalTo(INTERVIEW_REVIEW_STATUS_VALIDATION_FAILED.getMessage())) + .body("result", nullValue()); + } + + @Test + void QNA_SET_이_존재하지_않으면_질답_세트_수정에_실패한다() { + // given + QnaSetUpdateRequest qnaSetUpdateRequest = new QnaSetUpdateRequest("update question", "update answer", null); + + // when & then + given(spec) + .body(qnaSetUpdateRequest) + .when() + .put("/qna-set/" + Long.MAX_VALUE) + .then() + .statusCode(404) + .body("code", equalTo(QNA_SET_NOT_FOUND.name())) + .body("message", equalTo(QNA_SET_NOT_FOUND.getMessage())) + .body("result", nullValue()); + } + } + + @Nested + class 질답_세트_삭제_시 { + + // TODO 반복 코드 + private Long qnaSetDraftQnaSetId; + private Long debriefCompletedQnaSetId; + + @BeforeEach + void setUp() { + InterviewCreateRequest interviewCreateRequest1 = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + Interview qnaSetDraftInterview = createAndSaveInterview(interviewCreateRequest1, InterviewReviewStatus.QNA_SET_DRAFT); + + InterviewCreateRequest interviewCreateRequest2 = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + Interview debriefCompletedInterview = createAndSaveInterview(interviewCreateRequest2, InterviewReviewStatus.DEBRIEF_COMPLETED); + + QnaSetCreateRequest qnaSetCreateRequest1 = new QnaSetCreateRequest("test question text", "test answer text"); + QnaSet qnaSetDraftQnaSet = createQnaSet(qnaSetCreateRequest1, qnaSetDraftInterview, true); + qnaSetDraftQnaSetId = qnaSetDraftQnaSet.getId(); + + QnaSetCreateRequest qnaSetCreateRequest2 = new QnaSetCreateRequest("test question text", "test answer text"); + QnaSet debriefCompletedQnaSet = createQnaSet(qnaSetCreateRequest2, debriefCompletedInterview, true); + debriefCompletedQnaSetId = debriefCompletedQnaSet.getId(); + } + + @Test + void 인터뷰가_QNA_SET_DRAFT_상태이면_질답_세트_삭제에_성공한다() { + // given + + // when & then + given(spec) + .when() + .delete("/qna-set/" + qnaSetDraftQnaSetId) + .then() + .statusCode(200) + .body("code", equalTo(COMMON204.name())) + .body("message", equalTo(COMMON204.getMessage())) + .body("result", nullValue()); + } + + @Test + void 인터뷰가_QNA_SET_DRAFT_상태가_아니면_질답_세트_삭제에_실패한다() { + // given + + // when & then + given(spec) + .when() + .delete("/qna-set/" + debriefCompletedQnaSetId) + .then() + .statusCode(400) + .body("code", equalTo(INTERVIEW_REVIEW_STATUS_VALIDATION_FAILED.name())) + .body("message", equalTo(INTERVIEW_REVIEW_STATUS_VALIDATION_FAILED.getMessage())) + .body("result", nullValue()); + } + } + + @Nested + class PDF_하이라이팅_등록_수정_시 { + + private Long qnaSetDraftQnaSetId; + private Long debriefCompletedQnaSetId; + + @BeforeEach + void setUp() { + InterviewCreateRequest interviewCreateRequest1 = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + Interview qnaSetDraftInterview = createAndSaveInterview(interviewCreateRequest1, InterviewReviewStatus.QNA_SET_DRAFT); + + InterviewCreateRequest interviewCreateRequest2 = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + Interview debriefCompletedInterview = createAndSaveInterview(interviewCreateRequest2, InterviewReviewStatus.DEBRIEF_COMPLETED); + + QnaSetCreateRequest qnaSetCreateRequest1 = new QnaSetCreateRequest("test question text", "test answer text"); + QnaSet qnaSetDraftQnaSet = createQnaSet(qnaSetCreateRequest1, qnaSetDraftInterview, true); + qnaSetDraftQnaSetId = qnaSetDraftQnaSet.getId(); + + QnaSetCreateRequest qnaSetCreateRequest2 = new QnaSetCreateRequest("test question text", "test answer text"); + QnaSet debriefCompletedQnaSet = createQnaSet(qnaSetCreateRequest2, debriefCompletedInterview, true); + debriefCompletedQnaSetId = debriefCompletedQnaSet.getId(); + } + + @Test + void 인터뷰가_QNA_SET_DRAFT_상태이면_PDF_하이라이팅_등록에_성공한다() { + // given + List request = createPdfHighlightUpdateRequest(); + + // when & then + given(spec) + .body(request) + .when() + .put("/qna-set/" + qnaSetDraftQnaSetId + "/pdf-highlightings") + .then() + .statusCode(200) + .body("code", equalTo(COMMON200.name())) + .body("message", equalTo(COMMON200.getMessage())) + .body("result", nullValue()); + } + + @Test + void 인터뷰가_QNA_SET_DRAFT_상태이면_비어있는_PDF_하이라이팅_등록에_성공한다() { + // given + List request = List.of(); + + // when & then + given(spec) + .body(request) + .when() + .put("/qna-set/" + qnaSetDraftQnaSetId + "/pdf-highlightings") + .then() + .statusCode(200) + .body("code", equalTo(COMMON200.name())) + .body("message", equalTo(COMMON200.getMessage())) + .body("result", nullValue()); + } + + @Test + void 인터뷰가_QNA_SET_DRAFT_상태가_아니면_PDF_하이라이팅_등록에_실패한다() { + // given + List request = createPdfHighlightUpdateRequest(); + + // when & then + given(spec) + .body(request) + .when() + .put("/qna-set/" + debriefCompletedQnaSetId + "/pdf-highlightings") + .then() + .statusCode(400) + .body("code", equalTo(INTERVIEW_REVIEW_STATUS_VALIDATION_FAILED.name())) + .body("message", equalTo(INTERVIEW_REVIEW_STATUS_VALIDATION_FAILED.getMessage())) + .body("result", nullValue()); + } + } + + @Nested + class PDF_하이라이팅_조회_시 { + + private Long qnaSetDraftQnaSetId; + private Long debriefCompletedQnaSetId; + private Long qnaSetWithPdfHighlightingId; + private Long otherUserQnaSetId; + + @BeforeEach + void setUp() { + InterviewCreateRequest interviewCreateRequest1 = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + Interview qnaSetDraftInterview = createAndSaveInterview(interviewCreateRequest1, InterviewReviewStatus.QNA_SET_DRAFT); + + InterviewCreateRequest interviewCreateRequest2 = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + Interview debriefCompletedInterview = createAndSaveInterview(interviewCreateRequest2, InterviewReviewStatus.DEBRIEF_COMPLETED); + + QnaSetCreateRequest qnaSetCreateRequest1 = new QnaSetCreateRequest("test question text", "test answer text"); + QnaSet qnaSetDraftQnaSet = createQnaSet(qnaSetCreateRequest1, qnaSetDraftInterview, true); + qnaSetDraftQnaSetId = qnaSetDraftQnaSet.getId(); + + QnaSetCreateRequest qnaSetCreateRequest2 = new QnaSetCreateRequest("test question text", "test answer text"); + QnaSet debriefCompletedQnaSet = createQnaSet(qnaSetCreateRequest2, debriefCompletedInterview, true); + debriefCompletedQnaSetId = debriefCompletedQnaSet.getId(); + + QnaSetCreateRequest qnaSetCreateRequest3 = new QnaSetCreateRequest("this qna has pdf highlighting", "hello PDF"); + QnaSet qnaSetWithPdfHighlighting = createQnaSet(qnaSetCreateRequest3,qnaSetDraftInterview, false); + qnaSetWithPdfHighlightingId = qnaSetWithPdfHighlighting.getId(); + + + InterviewCreateRequest request = new InterviewCreateRequest( + LocalDateTime.of(2025, 12, 29, 10, 0, 0), InterviewType.FIRST, "현대자동차", 1L, 1L, "BE Developer"); + User user = createAndSaveUser("other@example.com", "other", industry1, jobCategory1); + Interview otherUserInterview = createAndSaveInterview(request, InterviewReviewStatus.NOT_LOGGED, user); + + QnaSetCreateRequest qnaSetCreateRequest4 = new QnaSetCreateRequest("this qna is others", "hello stranger"); + QnaSet otherUserQnaSet = createQnaSet(qnaSetCreateRequest4, otherUserInterview, false); + otherUserQnaSetId = otherUserQnaSet.getId(); + + List pdfHighlightUpdateRequest = createPdfHighlightUpdateRequest(); + createAndSavePdfHighlighting(pdfHighlightUpdateRequest, qnaSetWithPdfHighlighting); + } + + @Test + void 나의_빈_PDF_하이라이팅_정보_조회를_성공한다() { + // given + + // when & then + given(spec) + .when() + .get("/qna-set/" + qnaSetDraftQnaSetId + "/pdf-highlightings") + .then() + .statusCode(200) + .body("code", equalTo(COMMON200.name())) + .body("message", equalTo(COMMON200.getMessage())) + .body("result", notNullValue()); + } + + @Test + void 나의_데이터가_있는_PDF_하이라이팅_정보_조회를_성공한다() { + // given + + // when & then + given(spec) + .when() + .get("/qna-set/" + qnaSetWithPdfHighlightingId + "/pdf-highlightings") + .then() + .statusCode(200) + .body("code", equalTo(COMMON200.name())) + .body("message", equalTo(COMMON200.getMessage())) + .body("result", notNullValue()); + } + + @Test + void 회고_완료_면접의_나의_PDF_하이라이팅_정보_조회를_성공한다() { + // given + + // when & then + given(spec) + .when() + .get("/qna-set/" + debriefCompletedQnaSetId + "/pdf-highlightings") + .then() + .statusCode(200) + .body("code", equalTo(COMMON200.name())) + .body("message", equalTo(COMMON200.getMessage())) + .body("result", notNullValue()); + } + + @Test + void 다른_사람의_PDF_하이라이팅_정보_조회를_실패한다() { + given(spec) + .when() + .get("/qna-set/" + otherUserQnaSetId + "/pdf-highlightings") + .then() + .statusCode(403) + .body("code", equalTo(INTERVIEW_NOT_ACCESSIBLE.name())) + .body("message", equalTo(INTERVIEW_NOT_ACCESSIBLE.getMessage())) + .body("result", nullValue()); + } + } + + private List createPdfHighlightUpdateRequest() { + return List.of( + new PdfHighlightingUpdateRequest( + "highlighting1 text", + List.of( + new PdfHighlightingRectDto(3.14, 1.592, 30.12, 4123.432, 10), + new PdfHighlightingRectDto(0.0, 1.592, 34.0, 4123.432, 10), + new PdfHighlightingRectDto(3.14, 1.592, 30.12, 4123.432, 10), + new PdfHighlightingRectDto(3.14, 123123.1, 30.12, 4123.432, 1) + )), + new PdfHighlightingUpdateRequest( + "highlighting2 text", + List.of( + new PdfHighlightingRectDto(3.14, 1.592, 30.12, 4123.432, 1), + new PdfHighlightingRectDto(0.0, 1.592, 34.0, 4123.432, 2), + new PdfHighlightingRectDto(3.14, 1.592, 30.12, 4123.432, 3), + new PdfHighlightingRectDto(3.14, 123123.1, 30.12, 4123.432, 4) + )), + new PdfHighlightingUpdateRequest( + "highlighting3 text", + List.of( + new PdfHighlightingRectDto(3.14, 1.592, 30.12, 20201483.2, 13), + new PdfHighlightingRectDto(0.0, 1.592, 34.0, 4123.432, 13), + new PdfHighlightingRectDto(0.04, 1.592, 30.12, 4123.432, 13), + new PdfHighlightingRectDto(452.1, 123123.1, 30.12, 4123.432, 13) + )) + ); + } +}