diff --git a/backend/src/main/java/reviewme/question/domain/Question2.java b/backend/src/main/java/reviewme/question/domain/Question2.java index 6639346ac..acf475798 100644 --- a/backend/src/main/java/reviewme/question/domain/Question2.java +++ b/backend/src/main/java/reviewme/question/domain/Question2.java @@ -53,6 +53,6 @@ public boolean isSelectable() { } public boolean hasGuideline() { - return guideline != null; + return guideline != null && !guideline.isEmpty(); } } diff --git a/backend/src/main/java/reviewme/question/domain/exception/MissingOptionItemsInOptionGroupException.java b/backend/src/main/java/reviewme/question/domain/exception/MissingOptionItemsInOptionGroupException.java new file mode 100644 index 000000000..5a6365773 --- /dev/null +++ b/backend/src/main/java/reviewme/question/domain/exception/MissingOptionItemsInOptionGroupException.java @@ -0,0 +1,13 @@ +package reviewme.question.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.NotFoundException; + +@Slf4j +public class MissingOptionItemsInOptionGroupException extends NotFoundException { + + public MissingOptionItemsInOptionGroupException(long optionGroupId) { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.warn("OptionGroup has no OptionItems - optionGroupId: {}", optionGroupId); + } +} diff --git a/backend/src/main/java/reviewme/question/domain/exception/OptionGroupNotFoundByQuestionException.java b/backend/src/main/java/reviewme/question/domain/exception/OptionGroupNotFoundByQuestionException.java new file mode 100644 index 000000000..8a1898824 --- /dev/null +++ b/backend/src/main/java/reviewme/question/domain/exception/OptionGroupNotFoundByQuestionException.java @@ -0,0 +1,13 @@ +package reviewme.question.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.NotFoundException; + +@Slf4j +public class OptionGroupNotFoundByQuestionException extends NotFoundException { + + public OptionGroupNotFoundByQuestionException(long questionId) { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.warn("OptionGroup not found by question - questionId: {}", questionId); + } +} diff --git a/backend/src/main/java/reviewme/question/repository/OptionItemRepository.java b/backend/src/main/java/reviewme/question/repository/OptionItemRepository.java index 0c87c92dd..15e626597 100644 --- a/backend/src/main/java/reviewme/question/repository/OptionItemRepository.java +++ b/backend/src/main/java/reviewme/question/repository/OptionItemRepository.java @@ -16,6 +16,8 @@ public interface OptionItemRepository extends JpaRepository { List findAllByOptionGroupId(long optionGroupId); + boolean existsByOptionTypeAndId(OptionType optionType, long id); + @Query(value = """ SELECT o.id FROM option_item o LEFT JOIN checkbox_answer ca @@ -38,8 +40,6 @@ public interface OptionItemRepository extends JpaRepository { """, nativeQuery = true) List findSelectedOptionItemsByReviewIdAndQuestionId(long reviewId, long questionId); - boolean existsByOptionTypeAndId(OptionType optionType, long id); - default OptionItem getOptionItemById(long id) { return findById(id).orElseThrow(() -> new OptionItemNotFoundException(id)); } diff --git a/backend/src/main/java/reviewme/question/repository/Question2Repository.java b/backend/src/main/java/reviewme/question/repository/Question2Repository.java index 2be2ce042..0f48aea5e 100644 --- a/backend/src/main/java/reviewme/question/repository/Question2Repository.java +++ b/backend/src/main/java/reviewme/question/repository/Question2Repository.java @@ -1,9 +1,11 @@ package reviewme.question.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import reviewme.question.domain.Question2; import reviewme.question.domain.exception.QuestionNotFoundException; +@Repository public interface Question2Repository extends JpaRepository { default Question2 getQuestionById(long id) { diff --git a/backend/src/main/java/reviewme/review/domain/exception/InvalidAnswerLengthException.java b/backend/src/main/java/reviewme/review/domain/exception/InvalidAnswerLengthException.java index abf6ffe60..1318fa89a 100644 --- a/backend/src/main/java/reviewme/review/domain/exception/InvalidAnswerLengthException.java +++ b/backend/src/main/java/reviewme/review/domain/exception/InvalidAnswerLengthException.java @@ -7,8 +7,8 @@ public class InvalidAnswerLengthException extends BadRequestException { public InvalidAnswerLengthException(int answerLength, int minLength, int maxLength) { - super("답변의 길이는 %d자 이상 %d자 이하여야 합니다.".formatted(minLength, maxLength)); - log.info("AnswerLength is out of bound - answerLength:{}, minLength: {}, maxLength: {}", + super("답변의 길이는 %d자 이상 %d자 이하여야 해요.".formatted(minLength, maxLength)); + log.info("AnswerLength is out of bound - answerLength: {}, minLength: {}, maxLength: {}", answerLength, minLength, maxLength); } } diff --git a/backend/src/main/java/reviewme/template/controller/TemplateController.java b/backend/src/main/java/reviewme/template/controller/TemplateController.java new file mode 100644 index 000000000..dad6acd04 --- /dev/null +++ b/backend/src/main/java/reviewme/template/controller/TemplateController.java @@ -0,0 +1,22 @@ +package reviewme.template.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import reviewme.template.dto.response.TemplateResponse; +import reviewme.template.service.TemplateService; + +@RestController +@RequiredArgsConstructor +public class TemplateController { + + private final TemplateService templateService; + + @GetMapping("/v2/reviews/write") + public ResponseEntity getReviewForm(@RequestParam String reviewRequestCode) { + TemplateResponse response = templateService.generateReviewForm(reviewRequestCode); + return ResponseEntity.ok(response); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/Section.java b/backend/src/main/java/reviewme/template/domain/Section.java index 86e40c752..29182a86c 100644 --- a/backend/src/main/java/reviewme/template/domain/Section.java +++ b/backend/src/main/java/reviewme/template/domain/Section.java @@ -1,7 +1,7 @@ package reviewme.template.domain; -import jakarta.persistence.CascadeType; import jakarta.persistence.Column; +import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; diff --git a/backend/src/main/java/reviewme/template/domain/exception/DefaultTemplateNotFoundException.java b/backend/src/main/java/reviewme/template/domain/exception/DefaultTemplateNotFoundException.java new file mode 100644 index 000000000..09cd0fe39 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/DefaultTemplateNotFoundException.java @@ -0,0 +1,13 @@ +package reviewme.template.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.NotFoundException; + +@Slf4j +public class DefaultTemplateNotFoundException extends NotFoundException { + + public DefaultTemplateNotFoundException(long id) { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.warn("DefaultTemplateNotFoundException is occurred - id: {}", id); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/SectionNotFoundException.java b/backend/src/main/java/reviewme/template/domain/exception/SectionNotFoundException.java index 87031665c..961090730 100644 --- a/backend/src/main/java/reviewme/template/domain/exception/SectionNotFoundException.java +++ b/backend/src/main/java/reviewme/template/domain/exception/SectionNotFoundException.java @@ -7,7 +7,7 @@ public class SectionNotFoundException extends NotFoundException { public SectionNotFoundException(long id) { - super("섹션이 존재하지 않아요."); - log.info("SectionNotFoundException is occurred - id: {}", id); + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.warn("SectionNotFoundException is occurred - id: {}", id); } } diff --git a/backend/src/main/java/reviewme/template/dto/response/OptionGroupResponse.java b/backend/src/main/java/reviewme/template/dto/response/OptionGroupResponse.java new file mode 100644 index 000000000..7fe5732ed --- /dev/null +++ b/backend/src/main/java/reviewme/template/dto/response/OptionGroupResponse.java @@ -0,0 +1,12 @@ +package reviewme.template.dto.response; + +import java.util.List; + +public record OptionGroupResponse( + + long optionGroupId, + int minCount, + int maxCount, + List options +) { +} diff --git a/backend/src/main/java/reviewme/template/dto/response/OptionItemResponse.java b/backend/src/main/java/reviewme/template/dto/response/OptionItemResponse.java new file mode 100644 index 000000000..a96160598 --- /dev/null +++ b/backend/src/main/java/reviewme/template/dto/response/OptionItemResponse.java @@ -0,0 +1,8 @@ +package reviewme.template.dto.response; + +public record OptionItemResponse( + + long optionId, + String content +) { +} diff --git a/backend/src/main/java/reviewme/template/dto/response/QuestionResponse.java b/backend/src/main/java/reviewme/template/dto/response/QuestionResponse.java new file mode 100644 index 000000000..8c8a9582d --- /dev/null +++ b/backend/src/main/java/reviewme/template/dto/response/QuestionResponse.java @@ -0,0 +1,15 @@ +package reviewme.template.dto.response; + +import jakarta.annotation.Nullable; + +public record QuestionResponse( + + long questionId, + boolean required, + String content, + String questionType, + @Nullable OptionGroupResponse optionGroup, + boolean hasGuideline, + @Nullable String guideline +) { +} diff --git a/backend/src/main/java/reviewme/template/dto/response/SectionResponse.java b/backend/src/main/java/reviewme/template/dto/response/SectionResponse.java new file mode 100644 index 000000000..5cdc9a3a5 --- /dev/null +++ b/backend/src/main/java/reviewme/template/dto/response/SectionResponse.java @@ -0,0 +1,14 @@ +package reviewme.template.dto.response; + +import jakarta.annotation.Nullable; +import java.util.List; + +public record SectionResponse( + + long sectionId, + String visible, + @Nullable Long onSelectedOptionId, + String header, + List questions +) { +} diff --git a/backend/src/main/java/reviewme/template/dto/response/TemplateResponse.java b/backend/src/main/java/reviewme/template/dto/response/TemplateResponse.java new file mode 100644 index 000000000..8df16a786 --- /dev/null +++ b/backend/src/main/java/reviewme/template/dto/response/TemplateResponse.java @@ -0,0 +1,12 @@ +package reviewme.template.dto.response; + +import java.util.List; + +public record TemplateResponse( + + long formId, + String revieweeName, + String projectName, + List sections +) { +} diff --git a/backend/src/main/java/reviewme/template/service/TemplateMapper.java b/backend/src/main/java/reviewme/template/service/TemplateMapper.java new file mode 100644 index 000000000..cf721ad04 --- /dev/null +++ b/backend/src/main/java/reviewme/template/service/TemplateMapper.java @@ -0,0 +1,102 @@ +package reviewme.template.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import reviewme.question.domain.OptionGroup; +import reviewme.question.domain.OptionItem; +import reviewme.question.domain.Question2; +import reviewme.question.domain.exception.MissingOptionItemsInOptionGroupException; +import reviewme.question.repository.OptionGroupRepository; +import reviewme.question.repository.OptionItemRepository; +import reviewme.question.repository.Question2Repository; +import reviewme.reviewgroup.domain.ReviewGroup; +import reviewme.template.domain.Section; +import reviewme.template.domain.SectionQuestion; +import reviewme.template.domain.Template; +import reviewme.template.domain.TemplateSection; +import reviewme.template.dto.response.OptionGroupResponse; +import reviewme.template.dto.response.OptionItemResponse; +import reviewme.template.dto.response.QuestionResponse; +import reviewme.template.dto.response.SectionResponse; +import reviewme.template.dto.response.TemplateResponse; +import reviewme.template.repository.SectionRepository; + +@Component +@RequiredArgsConstructor +public class TemplateMapper { + + private final SectionRepository sectionRepository; + private final Question2Repository questionRepository; + private final OptionGroupRepository optionGroupRepository; + private final OptionItemRepository optionItemRepository; + + public TemplateResponse mapToTemplateResponse(ReviewGroup reviewGroup, Template template) { + List sectionResponses = template.getSectionIds() + .stream() + .map(this::mapToSectionResponse) + .toList(); + + return new TemplateResponse( + template.getId(), + reviewGroup.getReviewee(), + reviewGroup.getProjectName(), + sectionResponses + ); + } + + private SectionResponse mapToSectionResponse(TemplateSection templateSection) { + Section section = sectionRepository.getSectionById(templateSection.getSectionId()); + List questionResponses = section.getQuestionIds() + .stream() + .map(this::mapToQuestionResponse) + .toList(); + + return new SectionResponse( + section.getId(), + section.getVisibleType().name(), + section.getOnSelectedOptionId(), + section.getHeader(), + questionResponses + ); + } + + private QuestionResponse mapToQuestionResponse(SectionQuestion sectionQuestion) { + Question2 question = questionRepository.getQuestionById(sectionQuestion.getQuestionId()); + OptionGroupResponse optionGroupResponse = optionGroupRepository.findByQuestionId(question.getId()) + .map(this::mapToOptionGroupResponse) + .orElse(null); + + return new QuestionResponse( + question.getId(), + question.isRequired(), + question.getContent(), + question.getContent(), + optionGroupResponse, + question.hasGuideline(), + question.getGuideline() + ); + } + + private OptionGroupResponse mapToOptionGroupResponse(OptionGroup optionGroup) { + List optionItems = optionItemRepository.findAllByOptionGroupId(optionGroup.getId()); + if (optionItems.isEmpty()) { + throw new MissingOptionItemsInOptionGroupException(optionGroup.getId()); + } + + List optionItemResponses = optionItems.stream() + .map(this::mapToOptionItemResponse) + .toList(); + + return new OptionGroupResponse( + optionGroup.getId(), + optionGroup.getMinSelectionCount(), + optionGroup.getMaxSelectionCount(), + optionItemResponses + ); + } + + private OptionItemResponse mapToOptionItemResponse(OptionItem optionItem) { + return new OptionItemResponse(optionItem.getId(), optionItem.getContent()); + } +} diff --git a/backend/src/main/java/reviewme/template/service/TemplateService.java b/backend/src/main/java/reviewme/template/service/TemplateService.java new file mode 100644 index 000000000..a840673a5 --- /dev/null +++ b/backend/src/main/java/reviewme/template/service/TemplateService.java @@ -0,0 +1,34 @@ +package reviewme.template.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import reviewme.review.domain.exception.ReviewGroupNotFoundByRequestReviewCodeException; +import reviewme.reviewgroup.domain.ReviewGroup; +import reviewme.reviewgroup.repository.ReviewGroupRepository; +import reviewme.template.domain.Template; +import reviewme.template.domain.exception.DefaultTemplateNotFoundException; +import reviewme.template.dto.response.TemplateResponse; +import reviewme.template.repository.TemplateRepository; + +@Service +@RequiredArgsConstructor +public class TemplateService { + + private static final long USE_TEMPLATE_ID = 1L; + + private final ReviewGroupRepository reviewGroupRepository; + private final TemplateRepository templateRepository; + private final TemplateMapper templateMapper; + + @Transactional(readOnly = true) + public TemplateResponse generateReviewForm(String reviewRequestCode) { + ReviewGroup reviewGroup = reviewGroupRepository.findByReviewRequestCode(reviewRequestCode) + .orElseThrow(() -> new ReviewGroupNotFoundByRequestReviewCodeException(reviewRequestCode)); + + Template defaultTemplate = templateRepository.findById(USE_TEMPLATE_ID) + .orElseThrow(() -> new DefaultTemplateNotFoundException(USE_TEMPLATE_ID)); + + return templateMapper.mapToTemplateResponse(reviewGroup, defaultTemplate); + } +} diff --git a/backend/src/test/java/reviewme/support/DatabaseCleaner.java b/backend/src/test/java/reviewme/support/DatabaseCleaner.java index 779e04103..b90427980 100644 --- a/backend/src/test/java/reviewme/support/DatabaseCleaner.java +++ b/backend/src/test/java/reviewme/support/DatabaseCleaner.java @@ -6,6 +6,7 @@ import jakarta.persistence.Table; import jakarta.persistence.metamodel.EntityType; import jakarta.transaction.Transactional; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -22,10 +23,10 @@ public class DatabaseCleaner { @PostConstruct public void afterPropertiesSet() { Set> entities = entityManager.getMetamodel().getEntities(); - tableNames = entities.stream() + tableNames = new ArrayList<>(entities.stream() .filter(entity -> entity.getJavaType().isAnnotationPresent(Table.class)) .map(entity -> entity.getJavaType().getAnnotation(Table.class).name()) - .toList(); + .toList()); } @Transactional diff --git a/backend/src/test/java/reviewme/template/service/TemplateMapperTest.java b/backend/src/test/java/reviewme/template/service/TemplateMapperTest.java new file mode 100644 index 000000000..af5bb28d7 --- /dev/null +++ b/backend/src/test/java/reviewme/template/service/TemplateMapperTest.java @@ -0,0 +1,210 @@ +package reviewme.template.service; + +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 org.springframework.beans.factory.annotation.Autowired; +import reviewme.question.domain.OptionGroup; +import reviewme.question.domain.OptionItem; +import reviewme.question.domain.OptionType; +import reviewme.question.domain.Question2; +import reviewme.question.domain.QuestionType; +import reviewme.question.domain.exception.MissingOptionItemsInOptionGroupException; +import reviewme.question.repository.OptionGroupRepository; +import reviewme.question.repository.OptionItemRepository; +import reviewme.question.repository.Question2Repository; +import reviewme.reviewgroup.domain.ReviewGroup; +import reviewme.reviewgroup.repository.ReviewGroupRepository; +import reviewme.support.ServiceTest; +import reviewme.template.domain.Section; +import reviewme.template.domain.Template; +import reviewme.template.domain.VisibleType; +import reviewme.template.domain.exception.SectionNotFoundException; +import reviewme.template.dto.response.QuestionResponse; +import reviewme.template.dto.response.SectionResponse; +import reviewme.template.dto.response.TemplateResponse; +import reviewme.template.repository.SectionRepository; +import reviewme.template.repository.TemplateRepository; + +@ServiceTest +class TemplateMapperTest { + + @Autowired + TemplateMapper templateMapper; + + @Autowired + TemplateRepository templateRepository; + + @Autowired + SectionRepository sectionRepository; + + @Autowired + Question2Repository questionRepository; + + @Autowired + OptionGroupRepository optionGroupRepository; + + @Autowired + OptionItemRepository optionItemRepository; + + @Autowired + ReviewGroupRepository reviewGroupRepository; + + @Test + void 리뷰_그룹과_템플릿으로_템플릿_응답을_매핑한다() { + // given + Question2 question1 = new Question2(true, QuestionType.TEXT, "질문", "가이드라인", 1); + Question2 question2 = new Question2(true, QuestionType.CHECKBOX, "질문", "가이드라인", 1); + questionRepository.saveAll(List.of(question1, question2)); + + OptionGroup optionGroup = new OptionGroup(question2.getId(), 1, 2); + optionGroupRepository.save(optionGroup); + + OptionItem optionItem = new OptionItem("선택지", optionGroup.getId(), 1, OptionType.CATEGORY); + optionItemRepository.save(optionItem); + + Section section1 = new Section(VisibleType.ALWAYS, List.of(question1.getId()), null, "말머리1", 1); + Section section2 = new Section(VisibleType.ALWAYS, List.of(question2.getId()), null, "말머리2", 2); + sectionRepository.saveAll(List.of(section1, section2)); + + Template template = new Template(List.of(section1.getId(), section2.getId())); + templateRepository.save(template); + + ReviewGroup reviewGroup = new ReviewGroup("리뷰이명", "프로젝트명", "reviewRequestCode", "groupAccessCode"); + reviewGroupRepository.save(reviewGroup); + + // when + TemplateResponse templateResponse = templateMapper.mapToTemplateResponse(reviewGroup, template); + + // then + assertAll( + () -> assertThat(templateResponse.revieweeName()).isEqualTo(reviewGroup.getReviewee()), + () -> assertThat(templateResponse.projectName()).isEqualTo(reviewGroup.getProjectName()), + () -> assertThat(templateResponse.sections()).hasSize(2), + () -> assertThat(templateResponse.sections().get(0).header()).isEqualTo(section1.getHeader()), + () -> assertThat(templateResponse.sections().get(0).questions()).hasSize(1), + () -> assertThat(templateResponse.sections().get(1).header()).isEqualTo(section2.getHeader()), + () -> assertThat(templateResponse.sections().get(1).questions()).hasSize(1) + ); + } + + @Test + void 섹션의_선택된_옵션이_필요없는_경우_제공하지_않는다() { + // given + Question2 question = new Question2(true, QuestionType.TEXT, "질문", "가이드라인", 1); + questionRepository.save(question); + + Section section = new Section(VisibleType.ALWAYS, List.of(question.getId()), null, "말머리1", 1); + sectionRepository.save(section); + + Template template = new Template(List.of(section.getId())); + templateRepository.save(template); + + ReviewGroup reviewGroup = new ReviewGroup("리뷰이명", "프로젝트명", "reviewRequestCode", "groupAccessCode"); + reviewGroupRepository.save(reviewGroup); + + // when + TemplateResponse templateResponse = templateMapper.mapToTemplateResponse(reviewGroup, template); + + // then + SectionResponse sectionResponse = templateResponse.sections().get(0); + assertThat(sectionResponse.onSelectedOptionId()).isNull(); + } + + @Test + void 가이드라인이_없는_경우_가이드_라인을_제공하지_않는다() { + // given + Question2 question = new Question2(true, QuestionType.TEXT, "질문", null, 1); + questionRepository.save(question); + + OptionGroup optionGroup = new OptionGroup(question.getId(), 1, 2); + optionGroupRepository.save(optionGroup); + + OptionItem optionItem = new OptionItem("선택지", optionGroup.getId(), 1, OptionType.CATEGORY); + optionItemRepository.save(optionItem); + + Section section = new Section(VisibleType.ALWAYS, List.of(question.getId()), null, "말머리1", 1); + sectionRepository.save(section); + + Template template = new Template(List.of(section.getId())); + templateRepository.save(template); + + ReviewGroup reviewGroup = new ReviewGroup("리뷰이명", "프로젝트명", "reviewRequestCode", "groupAccessCode"); + reviewGroupRepository.save(reviewGroup); + + // when + TemplateResponse templateResponse = templateMapper.mapToTemplateResponse(reviewGroup, template); + + // then + QuestionResponse questionResponse = templateResponse.sections().get(0).questions().get(0); + assertAll( + () -> assertThat(questionResponse.hasGuideline()).isFalse(), + () -> assertThat(questionResponse.guideline()).isNull() + ); + } + + @Test + void 옵션_그룹이_없는_질문의_경우_옵션_그룹을_제공하지_않는다() { + // given + Question2 question = new Question2(true, QuestionType.TEXT, "질문", "가이드라인", 1); + questionRepository.save(question); + + Section section = new Section(VisibleType.ALWAYS, List.of(question.getId()), null, "말머리1", 1); + sectionRepository.save(section); + + Template template = new Template(List.of(section.getId())); + templateRepository.save(template); + + ReviewGroup reviewGroup = new ReviewGroup("리뷰이명", "프로젝트명", "reviewRequestCode", "groupAccessCode"); + reviewGroupRepository.save(reviewGroup); + + // when + TemplateResponse templateResponse = templateMapper.mapToTemplateResponse(reviewGroup, template); + + // then + QuestionResponse questionResponse = templateResponse.sections().get(0).questions().get(0); + assertThat(questionResponse.optionGroup()).isNull(); + } + + @Test + void 템플릿_매핑_시_템플릿에_제공할_섹션이_없을_경우_예외가_발생한다() { + // given + Template template = new Template(List.of(1L)); + templateRepository.save(template); + + ReviewGroup reviewGroup = new ReviewGroup("리뷰이명", "프로젝트명", "reviewRequestCode", "groupAccessCode"); + reviewGroupRepository.save(reviewGroup); + + // when, then + assertThatThrownBy(() -> templateMapper.mapToTemplateResponse(reviewGroup, template)) + .isInstanceOf(SectionNotFoundException.class); + } + + @Test + void 템플릿_매핑_시_옵션_그룹에_해당하는_옵션_아이템이_없을_경우_예외가_발생한다() { + // given + Question2 question1 = new Question2(true, QuestionType.TEXT, "질문", "가이드라인", 1); + Question2 question2 = new Question2(true, QuestionType.CHECKBOX, "질문", "가이드라인", 1); + questionRepository.saveAll(List.of(question1, question2)); + + OptionGroup optionGroup = new OptionGroup(question2.getId(), 1, 2); + optionGroupRepository.save(optionGroup); + + Section section1 = new Section(VisibleType.ALWAYS, List.of(question1.getId()), null, "말머리1", 1); + Section section2 = new Section(VisibleType.ALWAYS, List.of(question2.getId()), null, "말머리2", 2); + sectionRepository.saveAll(List.of(section1, section2)); + + Template template = new Template(List.of(section1.getId(), section2.getId())); + templateRepository.save(template); + + ReviewGroup reviewGroup = new ReviewGroup("리뷰이명", "프로젝트명", "reviewRequestCode", "groupAccessCode"); + reviewGroupRepository.save(reviewGroup); + + // when, then + assertThatThrownBy(() -> templateMapper.mapToTemplateResponse(reviewGroup, template)) + .isInstanceOf(MissingOptionItemsInOptionGroupException.class); + } +} diff --git a/backend/src/test/java/reviewme/template/service/TemplateServiceTest.java b/backend/src/test/java/reviewme/template/service/TemplateServiceTest.java new file mode 100644 index 000000000..60fd93574 --- /dev/null +++ b/backend/src/test/java/reviewme/template/service/TemplateServiceTest.java @@ -0,0 +1,43 @@ +package reviewme.template.service; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import reviewme.review.domain.exception.ReviewGroupNotFoundByRequestReviewCodeException; +import reviewme.reviewgroup.domain.ReviewGroup; +import reviewme.reviewgroup.repository.ReviewGroupRepository; +import reviewme.support.ServiceTest; +import reviewme.template.domain.exception.DefaultTemplateNotFoundException; + +@ServiceTest +class TemplateServiceTest { + + @Autowired + TemplateService templateService; + + @Autowired + ReviewGroupRepository reviewGroupRepository; + + @Test + void 잘못된_리뷰_요청_코드로_리뷰_작성폼을_요청할_경우_예외가_발생한다() { + // given + ReviewGroup reviewGroup = new ReviewGroup("리뷰이명", "프로젝트명", "reviewRequestCode", "groupAccessCode"); + reviewGroupRepository.save(reviewGroup); + + // when, then + assertThatThrownBy(() -> templateService.generateReviewForm(reviewGroup.getReviewRequestCode() + " ")) + .isInstanceOf(ReviewGroupNotFoundByRequestReviewCodeException.class); + } + + @Test + void 리뷰이에게_작성될_리뷰_양식_생성_시_저장된_템플릿이_없을_경우_예외가_발생한다() { + // given + ReviewGroup reviewGroup = new ReviewGroup("리뷰이명", "프로젝트명", "reviewRequestCode", "groupAccessCode"); + reviewGroupRepository.save(reviewGroup); + + // when, then + assertThatThrownBy(() -> templateService.generateReviewForm(reviewGroup.getReviewRequestCode())) + .isInstanceOf(DefaultTemplateNotFoundException.class); + } +}