diff --git a/backend/src/main/java/reviewme/highlight/controller/HighlightController.java b/backend/src/main/java/reviewme/highlight/controller/HighlightController.java index a76c6c298..ed97f0b5e 100644 --- a/backend/src/main/java/reviewme/highlight/controller/HighlightController.java +++ b/backend/src/main/java/reviewme/highlight/controller/HighlightController.java @@ -22,7 +22,7 @@ public ResponseEntity highlight( @Valid @RequestBody HighlightsRequest request, @ReviewGroupSession ReviewGroup reviewGroup ) { - highlightService.highlight(request, reviewGroup); + highlightService.editHighlight(request, reviewGroup); return ResponseEntity.ok().build(); } } diff --git a/backend/src/main/java/reviewme/highlight/domain/Highlight.java b/backend/src/main/java/reviewme/highlight/domain/Highlight.java index de6714efa..4b30a79db 100644 --- a/backend/src/main/java/reviewme/highlight/domain/Highlight.java +++ b/backend/src/main/java/reviewme/highlight/domain/Highlight.java @@ -26,11 +26,15 @@ public class Highlight { @Column(name = "answer_id", nullable = false) private long answerId; + @Column(name = "line_index", nullable = false) + private int lineIndex; + @Embedded - private HighlightPosition highlightPosition; + private HighlightRange highlightRange; - public Highlight(long answerId, int lineIndex, int startIndex, int endIndex) { + public Highlight(long answerId, int lineIndex, HighlightRange range) { this.answerId = answerId; - this.highlightPosition = new HighlightPosition(lineIndex, startIndex, endIndex); + this.lineIndex = lineIndex; + this.highlightRange = range; } } diff --git a/backend/src/main/java/reviewme/highlight/domain/HighlightPosition.java b/backend/src/main/java/reviewme/highlight/domain/HighlightRange.java similarity index 52% rename from backend/src/main/java/reviewme/highlight/domain/HighlightPosition.java rename to backend/src/main/java/reviewme/highlight/domain/HighlightRange.java index 25463cd51..06fa9b5cd 100644 --- a/backend/src/main/java/reviewme/highlight/domain/HighlightPosition.java +++ b/backend/src/main/java/reviewme/highlight/domain/HighlightRange.java @@ -6,17 +6,13 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; -import reviewme.highlight.domain.exception.HighlightStartIndexExceedEndIndexException; -import reviewme.highlight.domain.exception.NegativeHighlightIndexException; +import reviewme.highlight.domain.exception.InvalidHighlightIndexRangeException; @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @EqualsAndHashCode -public class HighlightPosition { - - @Column(name = "line_index", nullable = false) - private int lineIndex; +public class HighlightRange { @Column(name = "start_index", nullable = false) private int startIndex; @@ -24,23 +20,22 @@ public class HighlightPosition { @Column(name = "end_index", nullable = false) private int endIndex; - public HighlightPosition(int lineIndex, int startIndex, int endIndex) { + public HighlightRange(int startIndex, int endIndex) { validateNonNegativeIndexNumber(startIndex, endIndex); validateEndIndexOverStartIndex(startIndex, endIndex); - this.lineIndex = lineIndex; this.startIndex = startIndex; this.endIndex = endIndex; } - private void validateNonNegativeIndexNumber(long startIndex, long endIndex) { + private void validateNonNegativeIndexNumber(int startIndex, int endIndex) { if (startIndex < 0 || endIndex < 0) { - throw new NegativeHighlightIndexException(startIndex, endIndex); + throw new InvalidHighlightIndexRangeException(startIndex, endIndex); } } - private void validateEndIndexOverStartIndex(long startIndex, long endIndex) { + private void validateEndIndexOverStartIndex(int startIndex, int endIndex) { if (startIndex > endIndex) { - throw new HighlightStartIndexExceedEndIndexException(startIndex, endIndex); + throw new InvalidHighlightIndexRangeException(startIndex, endIndex); } } } diff --git a/backend/src/main/java/reviewme/highlight/domain/HighlightedLine.java b/backend/src/main/java/reviewme/highlight/domain/HighlightedLine.java new file mode 100644 index 000000000..06bca5576 --- /dev/null +++ b/backend/src/main/java/reviewme/highlight/domain/HighlightedLine.java @@ -0,0 +1,32 @@ +package reviewme.highlight.domain; + +import java.util.HashSet; +import java.util.Set; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import reviewme.highlight.domain.exception.HighlightIndexExceedLineLengthException; + +@Getter +@EqualsAndHashCode +public class HighlightedLine { + + private final String content; + private final Set ranges; + + public HighlightedLine(String content) { + this.content = content; + this.ranges = new HashSet<>(); + } + + public void addRange(int startIndex, int endIndex) { + validateRangeByContentLength(startIndex, endIndex); + ranges.add(new HighlightRange(startIndex, endIndex)); + } + + private void validateRangeByContentLength(int startIndex, int endIndex) { + int contentLength = content.length(); + if (startIndex >= contentLength || endIndex >= contentLength) { + throw new HighlightIndexExceedLineLengthException(content.length(), startIndex, endIndex); + } + } +} diff --git a/backend/src/main/java/reviewme/highlight/domain/HighlightedLines.java b/backend/src/main/java/reviewme/highlight/domain/HighlightedLines.java new file mode 100644 index 000000000..f7000ecb2 --- /dev/null +++ b/backend/src/main/java/reviewme/highlight/domain/HighlightedLines.java @@ -0,0 +1,40 @@ +package reviewme.highlight.domain; + +import java.util.Arrays; +import java.util.List; +import lombok.Getter; +import reviewme.highlight.domain.exception.InvalidHighlightLineIndexException; +import reviewme.highlight.domain.exception.NegativeHighlightLineIndexException; + +@Getter +public class HighlightedLines { + + public static final String LINE_SEPARATOR = "\n"; + + private final List lines; + + public HighlightedLines(String content) { + this.lines = Arrays.stream(content.split(LINE_SEPARATOR)) + .map(HighlightedLine::new) + .toList(); + } + + public void addRange(int lineIndex, int startIndex, int endIndex) { + validateNonNegativeLineIndexNumber(lineIndex); + validateLineIndexRange(lineIndex); + HighlightedLine line = lines.get(lineIndex); + line.addRange(startIndex, endIndex); + } + + private void validateNonNegativeLineIndexNumber(int lineIndex) { + if (lineIndex < 0) { + throw new NegativeHighlightLineIndexException(lineIndex); + } + } + + private void validateLineIndexRange(int lineIndex) { + if (lineIndex >= lines.size()) { + throw new InvalidHighlightLineIndexException(lineIndex, lines.size()); + } + } +} diff --git a/backend/src/main/java/reviewme/highlight/domain/exception/HighlightIndexExceedLineLengthException.java b/backend/src/main/java/reviewme/highlight/domain/exception/HighlightIndexExceedLineLengthException.java new file mode 100644 index 000000000..1c631fd83 --- /dev/null +++ b/backend/src/main/java/reviewme/highlight/domain/exception/HighlightIndexExceedLineLengthException.java @@ -0,0 +1,14 @@ +package reviewme.highlight.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class HighlightIndexExceedLineLengthException extends BadRequestException { + + public HighlightIndexExceedLineLengthException(int lineLength, int startIndex, int endIndex) { + super("하이라이트 위치가 텍스트의 범위를 벗어났어요."); + log.info("Highlight index exceed line length - lineLength: {}, startIndex: {}, endIndex: {}", + lineLength, startIndex, endIndex); + } +} diff --git a/backend/src/main/java/reviewme/highlight/domain/exception/HighlightStartIndexExceedEndIndexException.java b/backend/src/main/java/reviewme/highlight/domain/exception/HighlightStartIndexExceedEndIndexException.java index 735ba06b6..38c99ac9a 100644 --- a/backend/src/main/java/reviewme/highlight/domain/exception/HighlightStartIndexExceedEndIndexException.java +++ b/backend/src/main/java/reviewme/highlight/domain/exception/HighlightStartIndexExceedEndIndexException.java @@ -6,7 +6,7 @@ @Slf4j public class HighlightStartIndexExceedEndIndexException extends BadRequestException { - public HighlightStartIndexExceedEndIndexException(long startIndex, long endIndex) { + public HighlightStartIndexExceedEndIndexException(int startIndex, int endIndex) { super("하이라이트 끝 위치는 시작 위치보다 같거나 커야 해요."); log.info("Highlight start index exceed end index - startIndex: {}, endIndex: {}", startIndex, endIndex); } diff --git a/backend/src/main/java/reviewme/highlight/domain/exception/InvalidHighlightIndexRangeException.java b/backend/src/main/java/reviewme/highlight/domain/exception/InvalidHighlightIndexRangeException.java new file mode 100644 index 000000000..889bcd4fb --- /dev/null +++ b/backend/src/main/java/reviewme/highlight/domain/exception/InvalidHighlightIndexRangeException.java @@ -0,0 +1,13 @@ +package reviewme.highlight.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class InvalidHighlightIndexRangeException extends BadRequestException { + + public InvalidHighlightIndexRangeException(int startIndex, int endIndex) { + super("유효하지 않은 하이라이트 위치에요. 하이라이트 시작 위치: %d, 종료 위치: %d".formatted(startIndex, endIndex)); + log.info("Highlight index is a negative number - startIndex: {}, endIndex: {}", startIndex, endIndex); + } +} diff --git a/backend/src/main/java/reviewme/highlight/service/exception/InvalidHighlightLineIndexException.java b/backend/src/main/java/reviewme/highlight/domain/exception/InvalidHighlightLineIndexException.java similarity index 58% rename from backend/src/main/java/reviewme/highlight/service/exception/InvalidHighlightLineIndexException.java rename to backend/src/main/java/reviewme/highlight/domain/exception/InvalidHighlightLineIndexException.java index 7271c08a3..8f06ce5b4 100644 --- a/backend/src/main/java/reviewme/highlight/service/exception/InvalidHighlightLineIndexException.java +++ b/backend/src/main/java/reviewme/highlight/domain/exception/InvalidHighlightLineIndexException.java @@ -1,4 +1,4 @@ -package reviewme.highlight.service.exception; +package reviewme.highlight.domain.exception; import lombok.extern.slf4j.Slf4j; import reviewme.global.exception.BadRequestException; @@ -6,8 +6,8 @@ @Slf4j public class InvalidHighlightLineIndexException extends BadRequestException { - public InvalidHighlightLineIndexException(long submittedLineIndex, long providedMaxLineIndex) { - super("줄 번호는 %d 이하여야해요.".formatted(providedMaxLineIndex)); + public InvalidHighlightLineIndexException(int submittedLineIndex, int providedMaxLineIndex) { + super("하이라이트 위치가 답변의 라인을 벗어났어요."); log.info("Line index is out of bound - maxIndex: {}, submittedLineIndex: {}", providedMaxLineIndex, submittedLineIndex); } diff --git a/backend/src/main/java/reviewme/highlight/domain/exception/NegativeHighlightIndexException.java b/backend/src/main/java/reviewme/highlight/domain/exception/NegativeHighlightIndexException.java deleted file mode 100644 index 9d2a32f00..000000000 --- a/backend/src/main/java/reviewme/highlight/domain/exception/NegativeHighlightIndexException.java +++ /dev/null @@ -1,13 +0,0 @@ -package reviewme.highlight.domain.exception; - -import lombok.extern.slf4j.Slf4j; -import reviewme.global.exception.BadRequestException; - -@Slf4j -public class NegativeHighlightIndexException extends BadRequestException { - - public NegativeHighlightIndexException(long startIndex, long endIndex) { - super("하이라이트 위치는 1 이상의 수이어야 해요."); - log.info("Highlight index is a negative number - startIndex: {}, endIndex: {}", startIndex, endIndex); - } -} diff --git a/backend/src/main/java/reviewme/highlight/domain/exception/NegativeHighlightLineIndexException.java b/backend/src/main/java/reviewme/highlight/domain/exception/NegativeHighlightLineIndexException.java new file mode 100644 index 000000000..1fbd8f6c3 --- /dev/null +++ b/backend/src/main/java/reviewme/highlight/domain/exception/NegativeHighlightLineIndexException.java @@ -0,0 +1,13 @@ +package reviewme.highlight.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class NegativeHighlightLineIndexException extends BadRequestException { + + public NegativeHighlightLineIndexException(int lineIndex) { + super("하이라이트 할 라인의 위치는 0 이상의 수이어야 해요."); + log.info("Highlight index is a negative number - lineIndex: {}", lineIndex); + } +} diff --git a/backend/src/main/java/reviewme/highlight/repository/HighlightRepository.java b/backend/src/main/java/reviewme/highlight/repository/HighlightRepository.java index c914d9750..c7e3b5adf 100644 --- a/backend/src/main/java/reviewme/highlight/repository/HighlightRepository.java +++ b/backend/src/main/java/reviewme/highlight/repository/HighlightRepository.java @@ -1,7 +1,17 @@ package reviewme.highlight.repository; +import java.util.Collection; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import reviewme.highlight.domain.Highlight; public interface HighlightRepository extends JpaRepository { + + @Modifying + @Query(""" + DELETE FROM Highlight h + WHERE h.answerId IN :answerIds + """) + void deleteAllByAnswerIds(Collection answerIds); } diff --git a/backend/src/main/java/reviewme/highlight/service/HighlightService.java b/backend/src/main/java/reviewme/highlight/service/HighlightService.java index 8f05d156b..7cb9f9c70 100644 --- a/backend/src/main/java/reviewme/highlight/service/HighlightService.java +++ b/backend/src/main/java/reviewme/highlight/service/HighlightService.java @@ -1,6 +1,5 @@ package reviewme.highlight.service; -import java.util.ArrayList; import java.util.List; import java.util.Set; import lombok.RequiredArgsConstructor; @@ -8,12 +7,9 @@ import org.springframework.transaction.annotation.Transactional; import reviewme.highlight.domain.Highlight; import reviewme.highlight.repository.HighlightRepository; -import reviewme.highlight.service.dto.HighlightIndexRangeRequest; -import reviewme.highlight.service.dto.HighlightRequest; -import reviewme.highlight.service.dto.HighlightedLineRequest; import reviewme.highlight.service.dto.HighlightsRequest; +import reviewme.highlight.service.mapper.HighlightMapper; import reviewme.highlight.service.validator.HighlightValidator; -import reviewme.review.domain.Answer; import reviewme.review.repository.AnswerRepository; import reviewme.reviewgroup.domain.ReviewGroup; @@ -25,36 +21,16 @@ public class HighlightService { private final AnswerRepository answerRepository; private final HighlightValidator highlightValidator; + private final HighlightMapper highlightMapper; @Transactional - public void highlight(HighlightsRequest request, ReviewGroup reviewGroup) { - long reviewGroupId = reviewGroup.getId(); - highlightValidator.validate(request, reviewGroupId); - deleteOldHighlight(request.questionId(), reviewGroupId); - saveNewHighlight(request); - } - - private void deleteOldHighlight(long questionId, long reviewGroupId) { - Set answersByReviewGroup = answerRepository.findAllByReviewGroupId(reviewGroupId); - List answersByReviewQuestion = answersByReviewGroup.stream() - .filter(answer -> answer.getQuestionId() == questionId) - .map(Answer::getId) - .toList(); + public void editHighlight(HighlightsRequest highlightsRequest, ReviewGroup reviewGroup) { + highlightValidator.validate(highlightsRequest, reviewGroup); + List highlights = highlightMapper.mapToHighlights(highlightsRequest); - highlightRepository.deleteAllById(answersByReviewQuestion); - } + Set answerIds = answerRepository.findIdsByQuestionId(highlightsRequest.questionId()); + highlightRepository.deleteAllByAnswerIds(answerIds); - private void saveNewHighlight(HighlightsRequest highlightsRequest) { - List highlights = new ArrayList<>(); - for (HighlightRequest highlight : highlightsRequest.highlights()) { - for (HighlightedLineRequest line : highlight.lines()) { - for (HighlightIndexRangeRequest range : line.ranges()) { - Highlight highLight = new Highlight(highlight.answerId(), - line.index(), range.startIndex(), range.endIndex()); - highlights.add(highLight); - } - } - } highlightRepository.saveAll(highlights); } } diff --git a/backend/src/main/java/reviewme/highlight/service/dto/HighlightsRequest.java b/backend/src/main/java/reviewme/highlight/service/dto/HighlightsRequest.java index 7b41526d9..b8f26cba6 100644 --- a/backend/src/main/java/reviewme/highlight/service/dto/HighlightsRequest.java +++ b/backend/src/main/java/reviewme/highlight/service/dto/HighlightsRequest.java @@ -12,4 +12,12 @@ public record HighlightsRequest( @Valid @NotNull(message = "하이라이트할 부분을 입력해주세요.") List highlights ) { + + public List getUniqueAnswerIds() { + return highlights() + .stream() + .map(HighlightRequest::answerId) + .distinct() + .toList(); + } } diff --git a/backend/src/main/java/reviewme/highlight/service/exception/HighlightDuplicatedException.java b/backend/src/main/java/reviewme/highlight/service/exception/HighlightDuplicatedException.java deleted file mode 100644 index de999bf84..000000000 --- a/backend/src/main/java/reviewme/highlight/service/exception/HighlightDuplicatedException.java +++ /dev/null @@ -1,14 +0,0 @@ -package reviewme.highlight.service.exception; - -import lombok.extern.slf4j.Slf4j; -import reviewme.global.exception.BadRequestException; - -@Slf4j -public class HighlightDuplicatedException extends BadRequestException { - - public HighlightDuplicatedException(long answerId, long lineIndex, long startIndex, long endIndex) { - super("중복된 하이라이트는 생성할 수 없어요."); - log.info("Highlight is duplicated - answerId: {}, lineIndex: {}, startIndex: {}, endIndex: {}", - answerId, lineIndex, startIndex, endIndex); - } -} diff --git a/backend/src/main/java/reviewme/highlight/service/mapper/HighlightMapper.java b/backend/src/main/java/reviewme/highlight/service/mapper/HighlightMapper.java new file mode 100644 index 000000000..edbec9013 --- /dev/null +++ b/backend/src/main/java/reviewme/highlight/service/mapper/HighlightMapper.java @@ -0,0 +1,77 @@ +package reviewme.highlight.service.mapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import reviewme.highlight.domain.HighlightedLines; +import reviewme.highlight.domain.HighlightedLine; +import reviewme.highlight.domain.Highlight; +import reviewme.highlight.domain.HighlightRange; +import reviewme.highlight.service.dto.HighlightIndexRangeRequest; +import reviewme.highlight.service.dto.HighlightRequest; +import reviewme.highlight.service.dto.HighlightedLineRequest; +import reviewme.highlight.service.dto.HighlightsRequest; +import reviewme.review.domain.Answer; +import reviewme.review.repository.TextAnswerRepository; + +@Component +@RequiredArgsConstructor +public class HighlightMapper { + + private final TextAnswerRepository textAnswerRepository; + + public List mapToHighlights(HighlightsRequest highlightsRequest) { + Map answerHighlightLines = textAnswerRepository + .findAllById(highlightsRequest.getUniqueAnswerIds()) + .stream() + .collect(Collectors.toMap(Answer::getId, answer -> new HighlightedLines(answer.getContent()))); + addIndexRanges(highlightsRequest, answerHighlightLines); + return mapLinesToHighlights(answerHighlightLines); + } + + private void addIndexRanges(HighlightsRequest highlightsRequest, Map answerHighlightLines) { + for (HighlightRequest highlightRequest : highlightsRequest.highlights()) { + HighlightedLines highlightedLines = answerHighlightLines.get(highlightRequest.answerId()); + addIndexRangesForAnswer(highlightRequest, highlightedLines); + } + } + + private void addIndexRangesForAnswer(HighlightRequest highlightRequest, HighlightedLines highlightedLines) { + for (HighlightedLineRequest lineRequest : highlightRequest.lines()) { + int lineIndex = lineRequest.index(); + for (HighlightIndexRangeRequest rangeRequest : lineRequest.ranges()) { + highlightedLines.addRange(lineIndex, rangeRequest.startIndex(), rangeRequest.endIndex()); + } + } + } + + private List mapLinesToHighlights(Map answerHighlightLines) { + List highlights = new ArrayList<>(); + for (Entry answerHighlightLine : answerHighlightLines.entrySet()) { + createHighlightsForAnswer(answerHighlightLine, highlights); + } + return highlights; + } + + private void createHighlightsForAnswer(Entry answerHighlightLine, + List highlights) { + long answerId = answerHighlightLine.getKey(); + List highlightedLines = answerHighlightLine.getValue().getLines(); + + for (int lineIndex = 0; lineIndex < highlightedLines.size(); lineIndex++) { + createHighlightForLine(highlightedLines, lineIndex, answerId, highlights); + } + } + + private void createHighlightForLine(List highlightedLines, int lineIndex, long answerId, + List highlights) { + for (HighlightRange range : highlightedLines.get(lineIndex).getRanges()) { + Highlight highlight = new Highlight(answerId, lineIndex, range); + highlights.add(highlight); + } + } +} diff --git a/backend/src/main/java/reviewme/highlight/service/validator/HighlightValidator.java b/backend/src/main/java/reviewme/highlight/service/validator/HighlightValidator.java index ad2e323b3..e05f0f9df 100644 --- a/backend/src/main/java/reviewme/highlight/service/validator/HighlightValidator.java +++ b/backend/src/main/java/reviewme/highlight/service/validator/HighlightValidator.java @@ -1,87 +1,41 @@ + package reviewme.highlight.service.validator; import java.util.List; import java.util.Set; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import reviewme.highlight.service.dto.HighlightRequest; -import reviewme.highlight.service.dto.HighlightedLineRequest; import reviewme.highlight.service.dto.HighlightsRequest; -import reviewme.highlight.service.exception.InvalidHighlightLineIndexException; import reviewme.highlight.service.exception.SubmittedAnswerAndProvidedAnswerMismatchException; -import reviewme.question.repository.QuestionRepository; -import reviewme.review.domain.TextAnswer; import reviewme.review.repository.AnswerRepository; -import reviewme.review.repository.TextAnswerRepository; -import reviewme.review.service.exception.AnswerNotFoundByIdException; -import reviewme.review.service.exception.SubmittedQuestionAndProvidedQuestionMismatchException; -import reviewme.reviewgroup.repository.ReviewGroupRepository; +import reviewme.reviewgroup.domain.ReviewGroup; @Component @RequiredArgsConstructor public class HighlightValidator { private final AnswerRepository answerRepository; - private final TextAnswerRepository textAnswerRepository; - private final QuestionRepository questionRepository; - private final ReviewGroupRepository reviewGroupRepository; - public void validate(HighlightsRequest request, long reviewGroupId) { - validateReviewGroupContainsQuestion(request, reviewGroupId); - validateReviewGroupContainsAnswer(request, reviewGroupId); + public void validate(HighlightsRequest request, ReviewGroup reviewGroup) { validateQuestionContainsAnswer(request); - validateLineIndex(request); - // TODO: 중복 요청 검증 추가 예정 - } - - private void validateReviewGroupContainsQuestion(HighlightsRequest request, long reviewGroupId) { - long templateId = reviewGroupRepository.findById(reviewGroupId) - .orElseThrow() - .getTemplateId(); - Set providedQuestionIds = questionRepository.findAllQuestionIdByTemplateId(templateId); - long submittedQuestionId = request.questionId(); - - if (!providedQuestionIds.contains(submittedQuestionId)) { - throw new SubmittedQuestionAndProvidedQuestionMismatchException(submittedQuestionId, providedQuestionIds); - } - } - - private void validateReviewGroupContainsAnswer(HighlightsRequest request, long reviewGroupId) { - Set providedAnswerIds = answerRepository.findIdsByReviewGroupId(reviewGroupId); - List submittedAnswerIds = request.highlights() - .stream() - .map(HighlightRequest::answerId) - .toList(); - - if (!providedAnswerIds.containsAll(submittedAnswerIds)) { - throw new SubmittedAnswerAndProvidedAnswerMismatchException(providedAnswerIds, submittedAnswerIds); - } + validateReviewGroupContainsAnswer(request, reviewGroup); } private void validateQuestionContainsAnswer(HighlightsRequest request) { Set providedAnswerIds = answerRepository.findIdsByQuestionId(request.questionId()); - List submittedAnswerIds = request.highlights() - .stream() - .map(HighlightRequest::answerId) - .toList(); + List submittedAnswerIds = request.getUniqueAnswerIds(); if (!providedAnswerIds.containsAll(submittedAnswerIds)) { throw new SubmittedAnswerAndProvidedAnswerMismatchException(providedAnswerIds, submittedAnswerIds); } } - private void validateLineIndex(HighlightsRequest request) { - for (HighlightRequest highlight : request.highlights()) { - TextAnswer textAnswer = textAnswerRepository.findById(highlight.answerId()) - .orElseThrow(() -> new AnswerNotFoundByIdException(highlight.answerId())); - long providedMaxLineIndex = textAnswer.getContent().lines().count() - 1; + private void validateReviewGroupContainsAnswer(HighlightsRequest request, ReviewGroup reviewGroup) { + Set providedAnswerIds = answerRepository.findIdsByReviewGroupId(reviewGroup.getId()); + List submittedAnswerIds = request.getUniqueAnswerIds(); - for (HighlightedLineRequest line : highlight.lines()) { - long submittedLineIndex = line.index(); - if (providedMaxLineIndex < submittedLineIndex) { - throw new InvalidHighlightLineIndexException(submittedLineIndex, providedMaxLineIndex); - } - } + if (!providedAnswerIds.containsAll(submittedAnswerIds)) { + throw new SubmittedAnswerAndProvidedAnswerMismatchException(providedAnswerIds, submittedAnswerIds); } } } diff --git a/backend/src/main/java/reviewme/review/domain/TextAnswers.java b/backend/src/main/java/reviewme/review/domain/TextAnswers.java deleted file mode 100644 index 4ce230eb0..000000000 --- a/backend/src/main/java/reviewme/review/domain/TextAnswers.java +++ /dev/null @@ -1,30 +0,0 @@ -package reviewme.review.domain; - -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; -import reviewme.review.domain.exception.MissingTextAnswerForQuestionException; - -@Slf4j -public class TextAnswers { - - private final Map textAnswers; - - public TextAnswers(List textAnswers) { - this.textAnswers = textAnswers.stream() - .collect(Collectors.toMap(TextAnswer::getQuestionId, Function.identity())); - } - - public TextAnswer getAnswerByQuestionId(long questionId) { - if (!textAnswers.containsKey(questionId)) { - throw new MissingTextAnswerForQuestionException(questionId); - } - return textAnswers.get(questionId); - } - - public boolean hasAnswerByQuestionId(long questionId) { - return textAnswers.containsKey(questionId); - } -} diff --git a/backend/src/main/java/reviewme/review/repository/AnswerRepository.java b/backend/src/main/java/reviewme/review/repository/AnswerRepository.java index d296f89fe..ea793623a 100644 --- a/backend/src/main/java/reviewme/review/repository/AnswerRepository.java +++ b/backend/src/main/java/reviewme/review/repository/AnswerRepository.java @@ -28,14 +28,6 @@ public interface AnswerRepository extends JpaRepository { """) Set findIdsByReviewGroupId(long reviewGroupId); - @Query(""" - SELECT a FROM Answer a - JOIN Review r - ON a.reviewId = r.id - WHERE r.reviewGroupId = :reviewGroupId - """) - Set findAllByReviewGroupId(long reviewGroupId); - @Query(""" SELECT a.id FROM Answer a WHERE a.questionId = :questionId diff --git a/backend/src/test/java/reviewme/highlight/domain/HighlightPositionTest.java b/backend/src/test/java/reviewme/highlight/domain/HighlightPositionTest.java deleted file mode 100644 index a858f1d2a..000000000 --- a/backend/src/test/java/reviewme/highlight/domain/HighlightPositionTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package reviewme.highlight.domain; - -import static org.assertj.core.api.Assertions.assertThatCode; - -import org.junit.jupiter.api.Test; -import reviewme.highlight.domain.exception.HighlightStartIndexExceedEndIndexException; -import reviewme.highlight.domain.exception.NegativeHighlightIndexException; - -class HighlightPositionTest { - - @Test - void 하이라이트의_시작_인덱스가_종료_인덱스보다_큰_경우_예외를_발생한다() { - assertThatCode(() -> new HighlightPosition(1, 2, 1)) - .isInstanceOf(HighlightStartIndexExceedEndIndexException.class); - } - - @Test - void 하이라이트의_인덱스들이_0보다_작은_경우_예외를_발생한다() { - assertThatCode(() -> new HighlightPosition(1, -2, -1)) - .isInstanceOf(NegativeHighlightIndexException.class); - - } -} diff --git a/backend/src/test/java/reviewme/highlight/domain/HighlightedLineTest.java b/backend/src/test/java/reviewme/highlight/domain/HighlightedLineTest.java new file mode 100644 index 000000000..200300bbf --- /dev/null +++ b/backend/src/test/java/reviewme/highlight/domain/HighlightedLineTest.java @@ -0,0 +1,36 @@ +package reviewme.highlight.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; + +import java.util.Set; +import org.junit.jupiter.api.Test; +import reviewme.highlight.domain.exception.HighlightIndexExceedLineLengthException; + +class HighlightedLineTest { + + @Test + void 하이라이트_대상_라인의_글자수보다_큰_시작_종료_인덱스_범위를_추가하려고_하면_예외를_발생한다() { + // given + String content = "12345"; + HighlightedLine highlightedLine = new HighlightedLine(content); + + // when && then + assertThatCode(() -> highlightedLine.addRange(content.length() - 1, content.length())) + .isInstanceOf(HighlightIndexExceedLineLengthException.class); + } + + @Test + void 하이라이트_할_라인의_시작_종료_인덱스_범위를_추가한다() { + // given + HighlightedLine highlightedLine = new HighlightedLine("12345"); + + // when + highlightedLine.addRange(2, 4); + highlightedLine.addRange(0, 1); + + // then + Set ranges = highlightedLine.getRanges(); + assertThat(ranges).containsExactly(new HighlightRange(2, 4), new HighlightRange(0, 1)); + } +} diff --git a/backend/src/test/java/reviewme/highlight/domain/HighlightedLinesTest.java b/backend/src/test/java/reviewme/highlight/domain/HighlightedLinesTest.java new file mode 100644 index 000000000..53d81c209 --- /dev/null +++ b/backend/src/test/java/reviewme/highlight/domain/HighlightedLinesTest.java @@ -0,0 +1,87 @@ +package reviewme.highlight.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import reviewme.highlight.domain.exception.InvalidHighlightLineIndexException; +import reviewme.highlight.domain.exception.NegativeHighlightLineIndexException; +import reviewme.review.domain.Review; +import reviewme.review.domain.TextAnswer; +import reviewme.review.repository.ReviewRepository; + +@DataJpaTest +class HighlightedLinesTest { + + @Autowired + private ReviewRepository reviewRepository; + + @Test + void 답변_내용으로_하이라이트에_사용될_라인을_생성한다() { + // given + TextAnswer answer = new TextAnswer(1L, "123\n456\n789"); + reviewRepository.save(new Review(1L, 1L, List.of(answer))); + + // when + HighlightedLines highlightedLines = new HighlightedLines(answer.getContent()); + + // then + assertThat(highlightedLines.getLines()).containsExactly( + new HighlightedLine("123"), + new HighlightedLine("456"), + new HighlightedLine("789") + ); + } + + @Test + void 특정_라인에_하이라이트_시작_종료_범위를_추가한다() { + // given + TextAnswer answer = new TextAnswer(1L, "123\n456\n78910"); + reviewRepository.save(new Review(1L, 1L, List.of(answer))); + HighlightedLines highlightedLines = new HighlightedLines(answer.getContent()); + + // when + highlightedLines.addRange(0, 1, 1); + highlightedLines.addRange(2, 0, 1); + highlightedLines.addRange(2, 3, 4); + + // then + List lines = highlightedLines.getLines(); + assertAll( + () -> assertThat(lines.get(0).getRanges()) + .containsExactly(new HighlightRange(1, 1)), + () -> assertThat(lines.get(2).getRanges()) + .containsExactly(new HighlightRange(0, 1), new HighlightRange(3, 4)) + ); + } + + @Test + void 하이라이트에_추가할_라인의_인덱스가_0보다_작을_경우_예외를_발생한다() { + // given + HighlightedLines highlightedLines = new HighlightedLines("123\n456"); + int negativeLineIndex = -1; + + // when && then + assertThatCode(() -> highlightedLines.addRange(negativeLineIndex, 0, 1)) + .isInstanceOf(NegativeHighlightLineIndexException.class); + } + + @Test + void 하이라이트에_추가할_라인의_인덱스가_대상_답변의_라인_수를_넘으면_예외를_발생한다() { + // given + String content = "123\n456"; + TextAnswer answer = new TextAnswer(1L, content); + reviewRepository.save(new Review(1L, 1L, List.of(answer))); + HighlightedLines highlightedLines = new HighlightedLines(answer.getContent()); + int invalidLineIndex = (int) content.lines().count(); + System.out.println(invalidLineIndex); + + // when && then + assertThatCode(() -> highlightedLines.addRange(invalidLineIndex, 0, 1)) + .isInstanceOf(InvalidHighlightLineIndexException.class); + } +} diff --git a/backend/src/test/java/reviewme/highlight/entity/HighlightRangeTest.java b/backend/src/test/java/reviewme/highlight/entity/HighlightRangeTest.java new file mode 100644 index 000000000..84f164490 --- /dev/null +++ b/backend/src/test/java/reviewme/highlight/entity/HighlightRangeTest.java @@ -0,0 +1,22 @@ +package reviewme.highlight.entity; + +import static org.assertj.core.api.Assertions.assertThatCode; + +import org.junit.jupiter.api.Test; +import reviewme.highlight.domain.HighlightRange; +import reviewme.highlight.domain.exception.InvalidHighlightIndexRangeException; + +class HighlightRangeTest { + + @Test + void 하이라이트의_시작_인덱스가_종료_인덱스보다_큰_경우_예외를_발생한다() { + assertThatCode(() -> new HighlightRange(2, 1)) + .isInstanceOf(InvalidHighlightIndexRangeException.class); + } + + @Test + void 하이라이트의_인덱스들이_0보다_작은_경우_예외를_발생한다() { + assertThatCode(() -> new HighlightRange(-2, -1)) + .isInstanceOf(InvalidHighlightIndexRangeException.class); + } +} diff --git a/backend/src/test/java/reviewme/highlight/service/HighlightServiceTest.java b/backend/src/test/java/reviewme/highlight/service/HighlightServiceTest.java index a31403588..32eed36b8 100644 --- a/backend/src/test/java/reviewme/highlight/service/HighlightServiceTest.java +++ b/backend/src/test/java/reviewme/highlight/service/HighlightServiceTest.java @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import reviewme.highlight.domain.Highlight; -import reviewme.highlight.domain.HighlightPosition; +import reviewme.highlight.domain.HighlightRange; import reviewme.highlight.repository.HighlightRepository; import reviewme.highlight.service.dto.HighlightIndexRangeRequest; import reviewme.highlight.service.dto.HighlightRequest; @@ -59,30 +59,22 @@ class HighlightServiceTest { long templateId = templateRepository.save(템플릿(List.of(sectionId))).getId(); String reviewRequestCode = "reviewRequestCode"; ReviewGroup reviewGroup = reviewGroupRepository.save(리뷰_그룹(reviewRequestCode, "groupAccessCode")); - Highlight highlight1 = highlightRepository.save(new Highlight(1, 1, 1, 1)); - Highlight highlight2 = highlightRepository.save(new Highlight(2, 1, 1, 1)); TextAnswer textAnswer1 = new TextAnswer(questionId, "text answer1"); TextAnswer textAnswer2 = new TextAnswer(questionId, "text answer2"); - Review review = reviewRepository.save( - new Review(templateId, reviewGroup.getId(), List.of(textAnswer1, textAnswer2))); + Review review = reviewRepository.save(new Review(templateId, reviewGroup.getId(), List.of(textAnswer1, textAnswer2))); + Highlight highlight = highlightRepository.save(new Highlight(textAnswer1.getId(), 1, new HighlightRange(1, 1))); HighlightIndexRangeRequest indexRangeRequest = new HighlightIndexRangeRequest(1, 1); HighlightedLineRequest lineRequest = new HighlightedLineRequest(0, List.of(indexRangeRequest)); - HighlightRequest highlightRequest1 = new HighlightRequest(textAnswer1.getId(), List.of(lineRequest)); - HighlightRequest highlightRequest2 = new HighlightRequest(textAnswer2.getId(), List.of(lineRequest)); - HighlightsRequest highlightsRequest = new HighlightsRequest( - questionId, List.of(highlightRequest1, highlightRequest2) - ); + HighlightRequest highlightRequest1 = new HighlightRequest(textAnswer2.getId(), List.of(lineRequest)); + HighlightsRequest highlightsRequest = new HighlightsRequest(questionId, List.of(highlightRequest1)); // when - highlightService.highlight(highlightsRequest, reviewGroup); + highlightService.editHighlight(highlightsRequest, reviewGroup); // then - assertAll( - () -> assertThat(highlightRepository.existsById(highlight1.getId())).isFalse(), - () -> assertThat(highlightRepository.existsById(highlight2.getId())).isFalse() - ); + assertAll(() -> assertThat(highlightRepository.existsById(highlight.getId())).isFalse()); } @Test @@ -93,34 +85,50 @@ class HighlightServiceTest { long templateId = templateRepository.save(템플릿(List.of(sectionId))).getId(); String reviewRequestCode = "reviewRequestCode"; ReviewGroup reviewGroup = reviewGroupRepository.save(리뷰_그룹(reviewRequestCode, "groupAccessCode")); - highlightRepository.save(new Highlight(1, 1, 1, 1)); - TextAnswer textAnswer1 = new TextAnswer(questionId, "text answer1"); - TextAnswer textAnswer2 = new TextAnswer(questionId, "text answer2"); - Review review = reviewRepository.save(new Review(templateId, reviewGroup.getId(), List.of(textAnswer1, textAnswer2))); + + TextAnswer textAnswer = new TextAnswer(questionId, "text answer1"); + Review review = reviewRepository.save(new Review(templateId, reviewGroup.getId(), List.of(textAnswer))); + highlightRepository.save(new Highlight(1, 1, new HighlightRange(1, 1))); int startIndex = 2; int endIndex = 2; - int lineIndex = 0; HighlightIndexRangeRequest indexRangeRequest = new HighlightIndexRangeRequest(startIndex, endIndex); - HighlightedLineRequest lineRequest1 = new HighlightedLineRequest(lineIndex, List.of(indexRangeRequest)); - HighlightedLineRequest lineRequest2 = new HighlightedLineRequest(lineIndex, List.of(indexRangeRequest)); - HighlightRequest highlightRequest1 = new HighlightRequest(textAnswer1.getId(), List.of(lineRequest1)); - HighlightRequest highlightRequest2 = new HighlightRequest(textAnswer2.getId(), List.of(lineRequest2)); - HighlightsRequest highlightsRequest = new HighlightsRequest(questionId, - List.of(highlightRequest1, highlightRequest2)); + HighlightedLineRequest lineRequest = new HighlightedLineRequest(0, List.of(indexRangeRequest)); + HighlightRequest highlightRequest = new HighlightRequest(textAnswer.getId(), List.of(lineRequest)); + HighlightsRequest highlightsRequest = new HighlightsRequest(questionId, List.of(highlightRequest)); // when - highlightService.highlight(highlightsRequest, reviewGroup); + highlightService.editHighlight(highlightsRequest, reviewGroup); // then List highlights = highlightRepository.findAll(); - HighlightPosition position = new HighlightPosition(lineIndex, startIndex, endIndex); assertAll( - () -> assertThat(highlights.get(0).getAnswerId()).isEqualTo(textAnswer1.getId()), - () -> assertThat(highlights.get(1).getAnswerId()).isEqualTo(textAnswer2.getId()), - () -> assertThat(highlights.get(0).getHighlightPosition()).isEqualTo(position), - () -> assertThat(highlights.get(0).getHighlightPosition()).isEqualTo(position) + () -> assertThat(highlights.get(0).getAnswerId()).isEqualTo(textAnswer.getId()), + () -> assertThat(highlights.get(0).getHighlightRange()).isEqualTo( + new HighlightRange(startIndex, endIndex)) ); } + + @Test + void 하이라이트_할_내용이_없는_요청이_오면_기존에_있던_내용을_삭제하고_아무것도_저장하지_않는다() { + // given + long questionId = questionRepository.save(서술형_필수_질문()).getId(); + long sectionId = sectionRepository.save(항상_보이는_섹션(List.of(questionId))).getId(); + long templateId = templateRepository.save(템플릿(List.of(sectionId))).getId(); + String reviewRequestCode = "reviewRequestCode"; + ReviewGroup reviewGroup = reviewGroupRepository.save(리뷰_그룹(reviewRequestCode, "groupAccessCode")); + + TextAnswer textAnswer = new TextAnswer(questionId, "text answer1"); + Review review = reviewRepository.save(new Review(templateId, reviewGroup.getId(), List.of(textAnswer))); + Highlight highlight = highlightRepository.save(new Highlight(textAnswer.getId(), 1, new HighlightRange(1, 1))); + + HighlightsRequest highlightsRequest = new HighlightsRequest(questionId, List.of()); + + // when + highlightService.editHighlight(highlightsRequest, reviewGroup); + + // then + assertAll(() -> assertThat(highlightRepository.existsById(highlight.getId())).isFalse()); + } } diff --git a/backend/src/test/java/reviewme/highlight/service/mapper/HighlightMapperTest.java b/backend/src/test/java/reviewme/highlight/service/mapper/HighlightMapperTest.java new file mode 100644 index 000000000..14a6639f9 --- /dev/null +++ b/backend/src/test/java/reviewme/highlight/service/mapper/HighlightMapperTest.java @@ -0,0 +1,104 @@ +package reviewme.highlight.service.mapper; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static reviewme.fixture.QuestionFixture.서술형_필수_질문; +import static reviewme.fixture.ReviewGroupFixture.리뷰_그룹; +import static reviewme.fixture.SectionFixture.항상_보이는_섹션; +import static reviewme.fixture.TemplateFixture.템플릿; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import reviewme.highlight.domain.Highlight; +import reviewme.highlight.domain.HighlightRange; +import reviewme.highlight.repository.HighlightRepository; +import reviewme.highlight.service.dto.HighlightIndexRangeRequest; +import reviewme.highlight.service.dto.HighlightRequest; +import reviewme.highlight.service.dto.HighlightedLineRequest; +import reviewme.highlight.service.dto.HighlightsRequest; +import reviewme.question.repository.QuestionRepository; +import reviewme.review.domain.Review; +import reviewme.review.domain.TextAnswer; +import reviewme.review.repository.ReviewRepository; +import reviewme.reviewgroup.repository.ReviewGroupRepository; +import reviewme.support.ServiceTest; +import reviewme.template.repository.SectionRepository; +import reviewme.template.repository.TemplateRepository; + +@ServiceTest +class HighlightMapperTest { + + @Autowired + private HighlightMapper highlightMapper; + + @Autowired + private HighlightRepository highlightRepository; + + @Autowired + private ReviewGroupRepository reviewGroupRepository; + + @Autowired + private ReviewRepository reviewRepository; + + @Autowired + private QuestionRepository questionRepository; + + @Autowired + private SectionRepository sectionRepository; + + @Autowired + private TemplateRepository templateRepository; + + @Test + void 하이라이트_요청과_기존_서술형_답변으로_하이라이트를_매핑한다() { + // given + long questionId = questionRepository.save(서술형_필수_질문()).getId(); + long sectionId = sectionRepository.save(항상_보이는_섹션(List.of(questionId))).getId(); + long templateId = templateRepository.save(템플릿(List.of(sectionId))).getId(); + String reviewRequestCode = "reviewRequestCode"; + long reviewGroupId = reviewGroupRepository.save(리뷰_그룹(reviewRequestCode, "groupAccessCode")) + .getId(); + + TextAnswer textAnswer1 = new TextAnswer(questionId, "text answer1"); + TextAnswer textAnswer2 = new TextAnswer(questionId, "text answer2"); + Review review = reviewRepository.save(new Review(templateId, reviewGroupId, List.of(textAnswer1, textAnswer2))); + + highlightRepository.save(new Highlight(1, 1, new HighlightRange(1, 1))); + + int startIndex = 2; + int endIndex = 2; + int lineIndex = 0; + HighlightIndexRangeRequest rangeRequest = new HighlightIndexRangeRequest(startIndex, endIndex); + HighlightedLineRequest lineRequest1 = new HighlightedLineRequest(lineIndex, List.of(rangeRequest)); + HighlightedLineRequest lineRequest2 = new HighlightedLineRequest(lineIndex, List.of(rangeRequest)); + HighlightRequest highlightRequest1 = new HighlightRequest(textAnswer1.getId(), List.of(lineRequest1)); + HighlightRequest highlightRequest2 = new HighlightRequest(textAnswer2.getId(), List.of(lineRequest2)); + HighlightsRequest highlightsRequest = new HighlightsRequest(questionId, + List.of(highlightRequest1, highlightRequest2)); + + // when + List highlights = highlightMapper.mapToHighlights(highlightsRequest); + + // then + HighlightRange range = new HighlightRange(startIndex, endIndex); + assertAll( + () -> assertThat(highlights.get(0).getAnswerId()).isEqualTo(textAnswer1.getId()), + () -> assertThat(highlights.get(1).getAnswerId()).isEqualTo(textAnswer2.getId()), + () -> assertThat(highlights.get(0).getHighlightRange()).isEqualTo(range), + () -> assertThat(highlights.get(1).getHighlightRange()).isEqualTo(range) + ); + } + + @Test + void 하이라이트_할_내용이_없는_요청이_오면_매핑_결과_빈_리스트를_반환한다() { + // given + HighlightsRequest highlightsRequest = new HighlightsRequest(1L, List.of()); + + // when + List highlights = highlightMapper.mapToHighlights(highlightsRequest); + + // then + assertThat(highlights).isEmpty(); + } +} diff --git a/backend/src/test/java/reviewme/highlight/service/validator/HighlightValidatorTest.java b/backend/src/test/java/reviewme/highlight/service/validator/HighlightValidatorTest.java index 21f1f8638..84bf793d2 100644 --- a/backend/src/test/java/reviewme/highlight/service/validator/HighlightValidatorTest.java +++ b/backend/src/test/java/reviewme/highlight/service/validator/HighlightValidatorTest.java @@ -1,25 +1,21 @@ package reviewme.highlight.service.validator; import static org.assertj.core.api.Assertions.assertThatCode; +import static reviewme.fixture.QuestionFixture.서술형_필수_질문; +import static reviewme.fixture.ReviewGroupFixture.리뷰_그룹; import static reviewme.fixture.SectionFixture.항상_보이는_섹션; import static reviewme.fixture.TemplateFixture.템플릿; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import reviewme.fixture.QuestionFixture; -import reviewme.fixture.ReviewGroupFixture; import reviewme.highlight.service.dto.HighlightRequest; -import reviewme.highlight.service.dto.HighlightedLineRequest; import reviewme.highlight.service.dto.HighlightsRequest; -import reviewme.highlight.service.exception.InvalidHighlightLineIndexException; import reviewme.highlight.service.exception.SubmittedAnswerAndProvidedAnswerMismatchException; -import reviewme.question.domain.Question; import reviewme.question.repository.QuestionRepository; import reviewme.review.domain.Review; import reviewme.review.domain.TextAnswer; import reviewme.review.repository.ReviewRepository; -import reviewme.review.service.exception.SubmittedQuestionAndProvidedQuestionMismatchException; import reviewme.reviewgroup.domain.ReviewGroup; import reviewme.reviewgroup.repository.ReviewGroupRepository; import reviewme.support.ServiceTest; @@ -50,26 +46,33 @@ class HighlightValidatorTest { private TemplateRepository templateRepository; @Test - void 하이라이트의_질문_id가_리뷰_그룹의_템플릿에_속한_질문이_아니면_예외를_발생한다() { + void 하이라이트의_답변_id가_하이라이트의_질문_id에_해당하는_답변이_아니면_예외를_발생한다() { // given - ReviewGroup reviewGroup = reviewGroupRepository.save(ReviewGroupFixture.리뷰_그룹()); - Question question = questionRepository.save(QuestionFixture.서술형_필수_질문()); - HighlightsRequest highlightsRequest = new HighlightsRequest(question.getId(), List.of()); + long questionId1 = questionRepository.save(서술형_필수_질문()).getId(); + long questionId2 = questionRepository.save(서술형_필수_질문()).getId(); + Section section = sectionRepository.save(항상_보이는_섹션(List.of(questionId1, questionId2))); + Template template = templateRepository.save(템플릿(List.of(section.getId()))); + + ReviewGroup reviewGroup = reviewGroupRepository.save(리뷰_그룹()); + TextAnswer textAnswer_Q1 = new TextAnswer(questionId1, "text answer 1"); + + HighlightRequest highlightRequest = new HighlightRequest(textAnswer_Q1.getId(), List.of()); + HighlightsRequest highlightsRequest = new HighlightsRequest(questionId2, List.of(highlightRequest)); // when && then - assertThatCode(() -> highlightValidator.validate(highlightsRequest, reviewGroup.getId())) - .isInstanceOf(SubmittedQuestionAndProvidedQuestionMismatchException.class); + assertThatCode(() -> highlightValidator.validate(highlightsRequest, reviewGroup)) + .isInstanceOf(SubmittedAnswerAndProvidedAnswerMismatchException.class); } @Test void 하이라이트의_답변_id가_리뷰_그룹에_달린_답변이_아니면_예외를_발생한다() { // given - long questionId = questionRepository.save(QuestionFixture.서술형_필수_질문()).getId(); + long questionId = questionRepository.save(서술형_필수_질문()).getId(); Section section = sectionRepository.save(항상_보이는_섹션(List.of(questionId))); Template template = templateRepository.save(템플릿(List.of(section.getId()))); - ReviewGroup reviewGroup1 = reviewGroupRepository.save(ReviewGroupFixture.리뷰_그룹()); - ReviewGroup reviewGroup2 = reviewGroupRepository.save(ReviewGroupFixture.리뷰_그룹()); + ReviewGroup reviewGroup1 = reviewGroupRepository.save(리뷰_그룹()); + ReviewGroup reviewGroup2 = reviewGroupRepository.save(리뷰_그룹()); TextAnswer textAnswer1 = new TextAnswer(questionId, "text answer1"); TextAnswer textAnswer2 = new TextAnswer(questionId, "text answer2"); reviewRepository.saveAll(List.of( @@ -81,47 +84,26 @@ class HighlightValidatorTest { HighlightsRequest highlightsRequest = new HighlightsRequest(1L, List.of(highlightRequest)); // when && then - assertThatCode(() -> highlightValidator.validate(highlightsRequest, reviewGroup1.getId())) + assertThatCode(() -> highlightValidator.validate(highlightsRequest, reviewGroup1)) .isInstanceOf(SubmittedAnswerAndProvidedAnswerMismatchException.class); } @Test - void 하이라이트의_답변_id가_하이라이트의_질문_id에_해당하는_답변이_아니면_예외를_발생한다() { + void 하이라이트의_질문_id가_리뷰_그룹의_템플릿에_속한_질문이_아니면_예외를_발생한다() { // given - long questionId1 = questionRepository.save(QuestionFixture.서술형_필수_질문()).getId(); - long questionId2 = questionRepository.save(QuestionFixture.서술형_필수_질문()).getId(); - Section section = sectionRepository.save(항상_보이는_섹션(List.of(questionId1, questionId2))); + long questionId1 = questionRepository.save(서술형_필수_질문()).getId(); + long questionId2 = questionRepository.save(서술형_필수_질문()).getId(); + Section section = sectionRepository.save(항상_보이는_섹션(List.of(questionId1))); Template template = templateRepository.save(템플릿(List.of(section.getId()))); - long reviewGroupId = reviewGroupRepository.save(ReviewGroupFixture.리뷰_그룹()).getId(); + ReviewGroup reviewGroup = reviewGroupRepository.save(리뷰_그룹()); TextAnswer textAnswer_Q1 = new TextAnswer(questionId1, "text answer 1"); HighlightRequest highlightRequest = new HighlightRequest(textAnswer_Q1.getId(), List.of()); HighlightsRequest highlightsRequest = new HighlightsRequest(questionId2, List.of(highlightRequest)); // when && then - assertThatCode(() -> highlightValidator.validate(highlightsRequest, reviewGroupId)) + assertThatCode(() -> highlightValidator.validate(highlightsRequest, reviewGroup)) .isInstanceOf(SubmittedAnswerAndProvidedAnswerMismatchException.class); } - - @Test - void 답변의_줄_수보다_하이라이트의_줄_번호가_더_크면_예외를_발생한다() { - // given - long questionId = questionRepository.save(QuestionFixture.서술형_필수_질문()).getId(); - long sectionId = sectionRepository.save(항상_보이는_섹션(List.of(questionId))).getId(); - long templateId = templateRepository.save(템플릿(List.of(sectionId))).getId(); - - TextAnswer textAnswer = new TextAnswer(questionId, "line 1\n line 2"); - long reviewGroupId = reviewGroupRepository.save(ReviewGroupFixture.리뷰_그룹()).getId(); - Review review = reviewRepository.save(new Review(templateId, reviewGroupId, List.of(textAnswer))); - - long answerLineCount = textAnswer.getContent().lines().count(); - HighlightedLineRequest highlightedLineRequest = new HighlightedLineRequest((int) answerLineCount, List.of()); - HighlightRequest highlightRequest = new HighlightRequest(textAnswer.getId(), List.of(highlightedLineRequest)); - HighlightsRequest highlightsRequest = new HighlightsRequest(questionId, List.of(highlightRequest)); - - // when & then - assertThatCode(() -> highlightValidator.validate(highlightsRequest, reviewGroupId)) - .isInstanceOf(InvalidHighlightLineIndexException.class); - } } diff --git a/backend/src/test/java/reviewme/review/domain/TextAnswersTest.java b/backend/src/test/java/reviewme/review/domain/TextAnswersTest.java deleted file mode 100644 index 82eeb7e0b..000000000 --- a/backend/src/test/java/reviewme/review/domain/TextAnswersTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package reviewme.review.domain; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -import java.util.List; -import org.junit.jupiter.api.Test; -import reviewme.review.domain.exception.MissingTextAnswerForQuestionException; - -class TextAnswersTest { - - @Test - void 질문에_해당하는_답변이_없으면_예외를_발생한다() { - // given - TextAnswers textAnswers = new TextAnswers(List.of(new TextAnswer(1, "답".repeat(20)))); - - // when, then - assertThatThrownBy(() -> textAnswers.getAnswerByQuestionId(2)) - .isInstanceOf(MissingTextAnswerForQuestionException.class); - } - - @Test - void 질문_ID로_서술형_답변을_반환한다() { - // given - TextAnswers textAnswers = new TextAnswers(List.of(new TextAnswer(1, "답".repeat(20)))); - - // when - TextAnswer actual = textAnswers.getAnswerByQuestionId(1); - - // then - assertThat(actual.getContent()).isEqualTo("답".repeat(20)); - } - - @Test - void 질문_ID에_해당하는_답변이_있는지_확인한다() { - // given - TextAnswers textAnswers = new TextAnswers(List.of(new TextAnswer(1, "답변"))); - - // when - boolean actual1 = textAnswers.hasAnswerByQuestionId(1); - boolean actual2 = textAnswers.hasAnswerByQuestionId(2); - - // then - assertAll( - () -> assertThat(actual1).isTrue(), - () -> assertThat(actual2).isFalse() - ); - } -}