From 86b62da722d86852d1f76177b58dc148ef878244 Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sat, 23 Nov 2024 12:42:05 +0900 Subject: [PATCH 01/12] =?UTF-8?q?feat:=20StructuredTemplate=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/mapper/ReviewDetailMapper.java | 2 +- .../service/mapper/ReviewGatherMapper.java | 4 +- .../service/validator/ReviewValidator.java | 2 - .../reviewme/template/domain/Question.java | 2 +- .../reviewme/template/domain/Section.java | 34 ++++++ .../reviewme/template/domain/Template.java | 30 +++++ .../DuplicateQuestionIdException.java | 14 +++ .../DuplicateSectionIdException.java | 14 +++ .../OptionGroupNotExistException.java | 13 +++ .../OptionItemNotExistException.java | 13 +++ .../QuestionIdsNotExistException.java | 13 +++ .../exception/QuestionNotExistException.java | 13 +++ .../SectionIdsNotExistException.java | 13 +++ .../exception/SectionNotExistException.java | 13 +++ ...emplateCheckBoxQuestionFoundException.java | 15 +++ ...emplateInvisibleSectionFoundException.java | 31 +++++ ...uredTemplateNotExistTemplateException.java | 13 +++ ...emplateOptionGroupValidationException.java | 16 +++ ...emplateOptionItemsValidationException.java | 16 +++ ...edTemplateQuestionValidationException.java | 16 +++ ...redTemplateSectionValidationException.java | 15 +++ .../domain/structured/OptionGroups.java | 37 ++++++ .../domain/structured/OptionItems.java | 35 ++++++ .../template/domain/structured/Questions.java | 41 +++++++ .../template/domain/structured/Sections.java | 51 ++++++++ .../domain/structured/StructuredTemplate.java | 33 ++++++ .../structured/StructuredTemplateCreator.java | 109 ++++++++++++++++++ .../service/mapper/TemplateMapper.java | 30 ++--- 28 files changed, 613 insertions(+), 25 deletions(-) create mode 100644 backend/src/main/java/reviewme/template/domain/exception/DuplicateQuestionIdException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/DuplicateSectionIdException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/OptionGroupNotExistException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/OptionItemNotExistException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/QuestionIdsNotExistException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/QuestionNotExistException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/SectionIdsNotExistException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/SectionNotExistException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateCheckBoxQuestionFoundException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateInvisibleSectionFoundException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateNotExistTemplateException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateOptionGroupValidationException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateOptionItemsValidationException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateQuestionValidationException.java create mode 100644 backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateSectionValidationException.java create mode 100644 backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java create mode 100644 backend/src/main/java/reviewme/template/domain/structured/OptionItems.java create mode 100644 backend/src/main/java/reviewme/template/domain/structured/Questions.java create mode 100644 backend/src/main/java/reviewme/template/domain/structured/Sections.java create mode 100644 backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java create mode 100644 backend/src/main/java/reviewme/template/domain/structured/StructuredTemplateCreator.java diff --git a/backend/src/main/java/reviewme/review/service/mapper/ReviewDetailMapper.java b/backend/src/main/java/reviewme/review/service/mapper/ReviewDetailMapper.java index c2874c1c1..efbde5596 100644 --- a/backend/src/main/java/reviewme/review/service/mapper/ReviewDetailMapper.java +++ b/backend/src/main/java/reviewme/review/service/mapper/ReviewDetailMapper.java @@ -86,7 +86,7 @@ private SectionAnswerResponse mapToSectionResponse(Review review, Section sectio private QuestionAnswerResponse mapToQuestionResponse(Review review, Question question, Map optionGroupsByQuestion, Map> optionItemsByOptionGroup) { - if (question.isSelectable()) { + if (question.isCheckboxType()) { return mapToCheckboxQuestionResponse(review, question, optionGroupsByQuestion, optionItemsByOptionGroup); } else { return mapToTextQuestionResponse(review, question); diff --git a/backend/src/main/java/reviewme/review/service/mapper/ReviewGatherMapper.java b/backend/src/main/java/reviewme/review/service/mapper/ReviewGatherMapper.java index d53fd0905..85f7ceddd 100644 --- a/backend/src/main/java/reviewme/review/service/mapper/ReviewGatherMapper.java +++ b/backend/src/main/java/reviewme/review/service/mapper/ReviewGatherMapper.java @@ -51,7 +51,7 @@ private ReviewsGatheredByQuestionResponse mapToReviewsGatheredByQuestion(Questio @Nullable private List mapToTextResponse(Question question, List answers, List highlights) { - if (question.isSelectable()) { + if (question.isCheckboxType()) { return null; } Map> answerIdHighlights = highlights.stream() @@ -84,7 +84,7 @@ private List mapToHighlightResponse(List highlight @Nullable private List mapToVoteResponse(Question question, List answers) { - if (!question.isSelectable()) { + if (!question.isCheckboxType()) { return null; } diff --git a/backend/src/main/java/reviewme/review/service/validator/ReviewValidator.java b/backend/src/main/java/reviewme/review/service/validator/ReviewValidator.java index 96fb55ec4..f107c0fd8 100644 --- a/backend/src/main/java/reviewme/review/service/validator/ReviewValidator.java +++ b/backend/src/main/java/reviewme/review/service/validator/ReviewValidator.java @@ -15,7 +15,6 @@ import reviewme.review.service.exception.SubmittedQuestionAndProvidedQuestionMismatchException; import reviewme.template.domain.Question; import reviewme.template.domain.Section; -import reviewme.template.domain.SectionQuestion; import reviewme.template.repository.QuestionRepository; import reviewme.template.repository.SectionRepository; @@ -76,7 +75,6 @@ private Set extractDisplayedQuestionIds(Review review) { return sections.stream() .filter(section -> section.isVisibleBySelectedOptionIds(selectedOptionIds)) .flatMap(section -> section.getQuestionIds().stream()) - .map(SectionQuestion::getQuestionId) .collect(Collectors.toSet()); } } diff --git a/backend/src/main/java/reviewme/template/domain/Question.java b/backend/src/main/java/reviewme/template/domain/Question.java index f4384d854..5f8f010fa 100644 --- a/backend/src/main/java/reviewme/template/domain/Question.java +++ b/backend/src/main/java/reviewme/template/domain/Question.java @@ -48,7 +48,7 @@ public Question(boolean required, QuestionType questionType, String content, Str this.position = position; } - public boolean isSelectable() { + public boolean isCheckboxType() { return questionType == QuestionType.CHECKBOX; } diff --git a/backend/src/main/java/reviewme/template/domain/Section.java b/backend/src/main/java/reviewme/template/domain/Section.java index b9fa82b16..c00145d6f 100644 --- a/backend/src/main/java/reviewme/template/domain/Section.java +++ b/backend/src/main/java/reviewme/template/domain/Section.java @@ -13,11 +13,14 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import java.util.Collection; +import java.util.HashSet; import java.util.List; import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; +import reviewme.template.domain.exception.DuplicateQuestionIdException; +import reviewme.template.domain.exception.QuestionIdsNotExistException; @Entity @Table(name = "section") @@ -52,6 +55,7 @@ public class Section { public Section(VisibleType visibleType, List questionIds, Long onSelectedOptionId, String sectionName, String header, int position) { + validateQuestionIds(questionIds); this.visibleType = visibleType; this.questionIds = questionIds.stream() .map(SectionQuestion::new) @@ -62,6 +66,36 @@ public Section(VisibleType visibleType, List questionIds, this.position = position; } + private void validateQuestionIds(List questionIds) { + validateNotEmpty(questionIds); + validateNoDuplicates(questionIds); + } + + private void validateNotEmpty(List questionIds) { + if (questionIds == null || questionIds.isEmpty()) { + throw new QuestionIdsNotExistException(); + } + } + + private void validateNoDuplicates(List questionIds) { + int originalSize = questionIds.size(); + int deduplicatedSize = new HashSet<>(questionIds).size(); + + if (originalSize != deduplicatedSize) { + throw new DuplicateQuestionIdException(questionIds); + } + } + + public List getQuestionIds() { + return questionIds.stream() + .map(SectionQuestion::getQuestionId) + .toList(); + } + + public boolean isAlwaysVisible() { + return visibleType == VisibleType.ALWAYS; + } + public boolean isVisibleBySelectedOptionIds(Collection selectedOptionIds) { return visibleType == VisibleType.ALWAYS || selectedOptionIds.contains(onSelectedOptionId); } diff --git a/backend/src/main/java/reviewme/template/domain/Template.java b/backend/src/main/java/reviewme/template/domain/Template.java index 29b6f36d7..cda50f39e 100644 --- a/backend/src/main/java/reviewme/template/domain/Template.java +++ b/backend/src/main/java/reviewme/template/domain/Template.java @@ -9,11 +9,14 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; +import java.util.HashSet; import java.util.List; import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; +import reviewme.template.domain.exception.DuplicateSectionIdException; +import reviewme.template.domain.exception.SectionIdsNotExistException; @Entity @Table(name = "template") @@ -31,8 +34,35 @@ public class Template { private List sectionIds; public Template(List sectionIds) { + validateSectionIds(sectionIds); this.sectionIds = sectionIds.stream() .map(TemplateSection::new) .toList(); } + + private void validateSectionIds(List sectionIds) { + validateNotEmpty(sectionIds); + validateNoDuplicates(sectionIds); + } + + private void validateNotEmpty(List sectionIds) { + if (sectionIds == null || sectionIds.isEmpty()) { + throw new SectionIdsNotExistException(); + } + } + + private void validateNoDuplicates(List sectionIds) { + int originalSize = sectionIds.size(); + int deduplicatedSize = new HashSet<>(sectionIds).size(); + + if (originalSize != deduplicatedSize) { + throw new DuplicateSectionIdException(sectionIds); + } + } + + public List getSectionIds() { + return sectionIds.stream() + .map(TemplateSection::getSectionId) + .toList(); + } } diff --git a/backend/src/main/java/reviewme/template/domain/exception/DuplicateQuestionIdException.java b/backend/src/main/java/reviewme/template/domain/exception/DuplicateQuestionIdException.java new file mode 100644 index 000000000..fd8230f2b --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/DuplicateQuestionIdException.java @@ -0,0 +1,14 @@ +package reviewme.template.domain.exception; + +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class DuplicateQuestionIdException extends BadRequestException { + + public DuplicateQuestionIdException(List questionIds) { + super("섹션에는 중복된 질문이 있을 수 없어요."); + log.info("Duplicate question ID found during create Section - questionIds: {}", questionIds); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/DuplicateSectionIdException.java b/backend/src/main/java/reviewme/template/domain/exception/DuplicateSectionIdException.java new file mode 100644 index 000000000..8cd271eae --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/DuplicateSectionIdException.java @@ -0,0 +1,14 @@ +package reviewme.template.domain.exception; + +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class DuplicateSectionIdException extends BadRequestException { + + public DuplicateSectionIdException(List sectionIds) { + super("템플릿에는 중복된 섹션이 있을 수 없어요."); + log.info("Duplicate section ID Found during create Template - sectionIds: {}", sectionIds); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/OptionGroupNotExistException.java b/backend/src/main/java/reviewme/template/domain/exception/OptionGroupNotExistException.java new file mode 100644 index 000000000..f0cbba4dd --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/OptionGroupNotExistException.java @@ -0,0 +1,13 @@ +package reviewme.template.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class OptionGroupNotExistException extends BadRequestException { + + public OptionGroupNotExistException() { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.info("Not exist OptionGroup during create OptionGroups"); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/OptionItemNotExistException.java b/backend/src/main/java/reviewme/template/domain/exception/OptionItemNotExistException.java new file mode 100644 index 000000000..949e261e7 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/OptionItemNotExistException.java @@ -0,0 +1,13 @@ +package reviewme.template.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class OptionItemNotExistException extends BadRequestException { + + public OptionItemNotExistException() { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.info("Not exist OptionItem during create OptionItems"); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/QuestionIdsNotExistException.java b/backend/src/main/java/reviewme/template/domain/exception/QuestionIdsNotExistException.java new file mode 100644 index 000000000..4ab8f0241 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/QuestionIdsNotExistException.java @@ -0,0 +1,13 @@ +package reviewme.template.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class QuestionIdsNotExistException extends BadRequestException { + + public QuestionIdsNotExistException() { + super("섹션에는 하나 이상의 질문이 존재해야 해요."); + log.info("Not exist question during create Section"); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/QuestionNotExistException.java b/backend/src/main/java/reviewme/template/domain/exception/QuestionNotExistException.java new file mode 100644 index 000000000..6f58c55e3 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/QuestionNotExistException.java @@ -0,0 +1,13 @@ +package reviewme.template.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class QuestionNotExistException extends BadRequestException { + + public QuestionNotExistException() { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.info("Not exist question create Questions"); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/SectionIdsNotExistException.java b/backend/src/main/java/reviewme/template/domain/exception/SectionIdsNotExistException.java new file mode 100644 index 000000000..4429937df --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/SectionIdsNotExistException.java @@ -0,0 +1,13 @@ +package reviewme.template.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class SectionIdsNotExistException extends BadRequestException { + + public SectionIdsNotExistException() { + super("템플릿에는 하나 이상의 섹션이 존재해야 해요."); + log.info("Not exist section during create Template"); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/SectionNotExistException.java b/backend/src/main/java/reviewme/template/domain/exception/SectionNotExistException.java new file mode 100644 index 000000000..7f0df8dd1 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/SectionNotExistException.java @@ -0,0 +1,13 @@ +package reviewme.template.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class SectionNotExistException extends BadRequestException { + + public SectionNotExistException() { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.info("Not exist section during create Sections"); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateCheckBoxQuestionFoundException.java b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateCheckBoxQuestionFoundException.java new file mode 100644 index 000000000..eae089dc8 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateCheckBoxQuestionFoundException.java @@ -0,0 +1,15 @@ +package reviewme.template.domain.exception; + +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class StructuredTemplateCheckBoxQuestionFoundException extends BadRequestException { + + public StructuredTemplateCheckBoxQuestionFoundException(List questionIds) { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.warn("CheckBox Type Question Found during Only Text Answer StructuredTemplate creation: questionIds: {}", + questionIds); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateInvisibleSectionFoundException.java b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateInvisibleSectionFoundException.java new file mode 100644 index 000000000..db77b0d3a --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateInvisibleSectionFoundException.java @@ -0,0 +1,31 @@ +package reviewme.template.domain.exception; + +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class StructuredTemplateInvisibleSectionFoundException extends BadRequestException { + + private static final String ERROR_MESSAGE = "서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."; + + public StructuredTemplateInvisibleSectionFoundException(List invisibleSectionIds, List optionIds) { + super(ERROR_MESSAGE); + logWarning(invisibleSectionIds, optionIds); + } + + public StructuredTemplateInvisibleSectionFoundException(List invisibleSectionIds) { + super(ERROR_MESSAGE); + logWarning(invisibleSectionIds, null); + } + + private void logWarning(List invisibleSectionIds, List optionIds) { + if (optionIds != null) { + log.warn("Sections that cannot be exposed were found during StructuredTemplate creation: invisibleSectionIds: {}, optionIds: {}", + invisibleSectionIds, optionIds); + } else { + log.warn("Sections that cannot be exposed were found during StructuredTemplate creation: invisibleSectionIds: {}", + invisibleSectionIds); + } + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateNotExistTemplateException.java b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateNotExistTemplateException.java new file mode 100644 index 000000000..9e14e56df --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateNotExistTemplateException.java @@ -0,0 +1,13 @@ +package reviewme.template.domain.exception; + +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class StructuredTemplateNotExistTemplateException extends BadRequestException { + + public StructuredTemplateNotExistTemplateException() { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.warn("Not exist template during StructuredTemplate creation"); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateOptionGroupValidationException.java b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateOptionGroupValidationException.java new file mode 100644 index 000000000..a877792e9 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateOptionGroupValidationException.java @@ -0,0 +1,16 @@ +package reviewme.template.domain.exception; + +import java.util.Collection; +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class StructuredTemplateOptionGroupValidationException extends BadRequestException { + + public StructuredTemplateOptionGroupValidationException(Collection checkboxQuestionIds, + Collection questionIdsOfOptionGroups) { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.warn("OptionGroup validation failed during StructuredTemplate creation: checkboxQuestionIds: {}, questionIdsOfOptionGroups: {}", + checkboxQuestionIds, questionIdsOfOptionGroups); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateOptionItemsValidationException.java b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateOptionItemsValidationException.java new file mode 100644 index 000000000..a372fbb67 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateOptionItemsValidationException.java @@ -0,0 +1,16 @@ +package reviewme.template.domain.exception; + +import java.util.Collection; +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class StructuredTemplateOptionItemsValidationException extends BadRequestException { + + public StructuredTemplateOptionItemsValidationException(Collection optionGroupIds, + Collection optionGroupIdsOfItems) { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.warn("OptionItem validation failed during StructuredTemplate creation: optionGroupIds: {}, optionGroupIdsOfItems: {}", + optionGroupIds, optionGroupIdsOfItems); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateQuestionValidationException.java b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateQuestionValidationException.java new file mode 100644 index 000000000..9f1824cb1 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateQuestionValidationException.java @@ -0,0 +1,16 @@ +package reviewme.template.domain.exception; + +import java.util.Collection; +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class StructuredTemplateQuestionValidationException extends BadRequestException { + + public StructuredTemplateQuestionValidationException(Collection questionIdsOfSections, + Collection questionIds) { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.warn("Question validation failed during StructuredTemplate creation: questionIdsOfSections: {}, questionIds: {}", + questionIdsOfSections, questionIds); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateSectionValidationException.java b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateSectionValidationException.java new file mode 100644 index 000000000..3141b3b09 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateSectionValidationException.java @@ -0,0 +1,15 @@ +package reviewme.template.domain.exception; + +import java.util.Collection; +import lombok.extern.slf4j.Slf4j; +import reviewme.global.exception.BadRequestException; + +@Slf4j +public class StructuredTemplateSectionValidationException extends BadRequestException { + + public StructuredTemplateSectionValidationException(long templateId, Collection sectionIds) { + super("서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."); + log.warn("Section validation failed during StructuredTemplate creation: templateId: {}, sectionIds: {}", + templateId, sectionIds); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java b/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java new file mode 100644 index 000000000..865f57522 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java @@ -0,0 +1,37 @@ +package reviewme.template.domain.structured; + +import java.util.List; +import lombok.Getter; +import reviewme.template.domain.OptionGroup; +import reviewme.template.domain.exception.OptionGroupNotExistException; + +@Getter +public class OptionGroups { + + private final List optionGroups; + + public OptionGroups(List optionGroups) { + validateOptionGroups(optionGroups); + this.optionGroups = optionGroups.stream() + .distinct() + .toList(); + } + + private void validateOptionGroups(List optionGroups) { + if (optionGroups == null || optionGroups.isEmpty()) { + throw new OptionGroupNotExistException(); + } + } + + public List getOptionGroupIds() { + return optionGroups.stream() + .map(OptionGroup::getId) + .toList(); + } + + public List getQuestionIds() { + return optionGroups.stream() + .map(OptionGroup::getQuestionId) + .toList(); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/structured/OptionItems.java b/backend/src/main/java/reviewme/template/domain/structured/OptionItems.java new file mode 100644 index 000000000..6419d1281 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/structured/OptionItems.java @@ -0,0 +1,35 @@ +package reviewme.template.domain.structured; + +import java.util.List; +import reviewme.template.domain.OptionItem; +import reviewme.template.domain.exception.OptionItemNotExistException; + +public class OptionItems { + + private final List optionItems; + + public OptionItems(List optionItems) { + validateOptionItems(optionItems); + this.optionItems = optionItems.stream() + .distinct() + .toList(); + } + + private void validateOptionItems(List optionItems) { + if (optionItems == null || optionItems.isEmpty()) { + throw new OptionItemNotExistException(); + } + } + + public List getOptionItemIds() { + return optionItems.stream() + .map(OptionItem::getId) + .toList(); + } + + public List getOptionGroupIds() { + return optionItems.stream() + .map(OptionItem::getOptionGroupId) + .toList(); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/structured/Questions.java b/backend/src/main/java/reviewme/template/domain/structured/Questions.java new file mode 100644 index 000000000..7ec6efacf --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/structured/Questions.java @@ -0,0 +1,41 @@ +package reviewme.template.domain.structured; + +import java.util.List; +import reviewme.template.domain.Question; +import reviewme.template.domain.exception.QuestionNotExistException; + +public class Questions { + + private final List questions; + + public Questions(List questions) { + validateQuestions(questions); + this.questions = questions.stream() + .distinct() + .toList(); + } + + private void validateQuestions(List questions) { + if (questions == null || questions.isEmpty()) { + throw new QuestionNotExistException(); + } + } + + public List getQuestionIds() { + return questions.stream() + .map(Question::getId) + .toList(); + } + + public List getCheckBoxQuestionIds() { + return questions.stream() + .filter(Question::isCheckboxType) + .map(Question::getId) + .toList(); + } + + public boolean hasCheckboxQuestion() { + return questions.stream() + .anyMatch(Question::isCheckboxType); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/structured/Sections.java b/backend/src/main/java/reviewme/template/domain/structured/Sections.java new file mode 100644 index 000000000..24b8afeae --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/structured/Sections.java @@ -0,0 +1,51 @@ +package reviewme.template.domain.structured; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import reviewme.template.domain.Section; +import reviewme.template.domain.exception.SectionNotExistException; + +public class Sections { + + private final List
sections; + + public Sections(List
sections) { + validateSections(sections); + this.sections = sections.stream() + .distinct() + .toList(); + } + + private void validateSections(List
sections) { + if (sections == null || sections.isEmpty()) { + throw new SectionNotExistException(); + } + } + + public List getSectionIds() { + return sections.stream() + .map(Section::getId) + .toList(); + } + + public Set getQuestionIds() { + return sections.stream() + .flatMap(section -> section.getQuestionIds().stream()) + .collect(Collectors.toSet()); + } + + public List getInvisibleSectionIds(List selectedOptionIds) { + return sections.stream() + .filter(section -> !section.isVisibleBySelectedOptionIds(selectedOptionIds)) + .map(Section::getId) + .toList(); + } + + public List getInvisibleSectionIds() { + return sections.stream() + .filter(section -> !section.isAlwaysVisible()) + .map(Section::getId) + .toList(); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java b/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java new file mode 100644 index 000000000..cf531ba12 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java @@ -0,0 +1,33 @@ +package reviewme.template.domain.structured; + +import reviewme.template.domain.Template; + +public class StructuredTemplate { + + private final Template template; + private final Sections sections; + private final Questions questions; + private final OptionGroups optionGroups; + private final OptionItems optionItems; + + StructuredTemplate(Template template, Sections sections, Questions questions, OptionGroups optionGroups, + OptionItems optionItems) { + this.template = template; + this.sections = sections; + this.questions = questions; + this.optionGroups = optionGroups; + this.optionItems = optionItems; + } + + StructuredTemplate(Template template, Sections sections, Questions questions) { + this.template = template; + this.sections = sections; + this.questions = questions; + this.optionGroups = null; + this.optionItems = null; + } + + public long getTemplateId() { + return template.getId(); + } +} diff --git a/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplateCreator.java b/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplateCreator.java new file mode 100644 index 000000000..55db23762 --- /dev/null +++ b/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplateCreator.java @@ -0,0 +1,109 @@ +package reviewme.template.domain.structured; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import reviewme.template.domain.OptionGroup; +import reviewme.template.domain.OptionItem; +import reviewme.template.domain.Question; +import reviewme.template.domain.Section; +import reviewme.template.domain.Template; +import reviewme.template.domain.exception.StructuredTemplateCheckBoxQuestionFoundException; +import reviewme.template.domain.exception.StructuredTemplateInvisibleSectionFoundException; +import reviewme.template.domain.exception.StructuredTemplateNotExistTemplateException; +import reviewme.template.domain.exception.StructuredTemplateOptionGroupValidationException; +import reviewme.template.domain.exception.StructuredTemplateOptionItemsValidationException; +import reviewme.template.domain.exception.StructuredTemplateQuestionValidationException; +import reviewme.template.domain.exception.StructuredTemplateSectionValidationException; + +public class StructuredTemplateCreator { + + public StructuredTemplate create(Template template, List
sectionList, List questionList, + List optionGroupList, List optionItemList) { + Sections sections = new Sections(sectionList); + Questions questions = new Questions(questionList); + OptionGroups optionGroups = new OptionGroups(optionGroupList); + OptionItems optionItems = new OptionItems(optionItemList); + + validateTemplate(template); + validateSections(template, sections); + validateQuestions(sections, questions); + validateOptionGroup(questions, optionGroups); + validateOptionItems(optionGroups, optionItems); + validateSectionsVisibility(sections, optionItems); + + return new StructuredTemplate(template, sections, questions, optionGroups, optionItems); + } + + public StructuredTemplate createWithoutCheckBox(Template template, List
sectionList, List questionList) { + Sections sections = new Sections(sectionList); + Questions questions = new Questions(questionList); + + validateTemplate(template); + validateSections(template, sections); + validateSectionsVisibility(sections); + validateQuestions(sections, questions); + validateNoCheckBoxQuestion(questions); + + return new StructuredTemplate(template, sections, questions); + } + + private void validateTemplate(Template template) { + if (template == null) { + throw new StructuredTemplateNotExistTemplateException(); + } + } + + private void validateSections(Template template, Sections sections) { + List sectionIds = sections.getSectionIds(); + if (!new HashSet<>(template.getSectionIds()).containsAll(sectionIds)) { + throw new StructuredTemplateSectionValidationException(template.getId(), sectionIds); + } + } + + private void validateQuestions(Sections sections, Questions questions) { + Set questionIdsOfSections = sections.getQuestionIds(); + List questionIds = questions.getQuestionIds(); + if (!questionIdsOfSections.containsAll(questionIds)) { + throw new StructuredTemplateQuestionValidationException(questionIdsOfSections, questionIds); + } + } + + private void validateOptionGroup(Questions questions, OptionGroups optionGroups) { + List checkboxQuestionIds = questions.getCheckBoxQuestionIds(); + List questionIdsOfOptionGroup = optionGroups.getQuestionIds(); + if (!new HashSet<>(checkboxQuestionIds).containsAll(questionIdsOfOptionGroup)) { + throw new StructuredTemplateOptionGroupValidationException(checkboxQuestionIds, + questionIdsOfOptionGroup); + } + } + + private void validateOptionItems(OptionGroups optionGroups, OptionItems optionItems) { + List optionGroupIds = optionGroups.getOptionGroupIds(); + List OptionGroupIdsOfItems = optionItems.getOptionGroupIds(); + if (!new HashSet<>(optionGroupIds).containsAll(OptionGroupIdsOfItems)) { + throw new StructuredTemplateOptionItemsValidationException(optionGroupIds, OptionGroupIdsOfItems); + } + } + + private void validateSectionsVisibility(Sections sections, OptionItems optionItems) { + List optionItemIds = optionItems.getOptionItemIds(); + List invisibleSectionIds = sections.getInvisibleSectionIds(optionItemIds); + if (!invisibleSectionIds.isEmpty()) { + throw new StructuredTemplateInvisibleSectionFoundException(invisibleSectionIds, optionItemIds); + } + } + + private void validateSectionsVisibility(Sections sections) { + List invisibleSectionIds = sections.getInvisibleSectionIds(); + if (!invisibleSectionIds.isEmpty()) { + throw new StructuredTemplateInvisibleSectionFoundException(invisibleSectionIds); + } + } + + private void validateNoCheckBoxQuestion(Questions questions) { + if (questions.hasCheckboxQuestion()) { + throw new StructuredTemplateCheckBoxQuestionFoundException(questions.getQuestionIds()); + } + } +} diff --git a/backend/src/main/java/reviewme/template/service/mapper/TemplateMapper.java b/backend/src/main/java/reviewme/template/service/mapper/TemplateMapper.java index 5225bd7e6..19a737b08 100644 --- a/backend/src/main/java/reviewme/template/service/mapper/TemplateMapper.java +++ b/backend/src/main/java/reviewme/template/service/mapper/TemplateMapper.java @@ -4,17 +4,15 @@ import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Component; +import reviewme.reviewgroup.domain.ReviewGroup; import reviewme.template.domain.OptionGroup; import reviewme.template.domain.OptionItem; import reviewme.template.domain.Question; +import reviewme.template.domain.Section; +import reviewme.template.domain.Template; import reviewme.template.repository.OptionGroupRepository; import reviewme.template.repository.OptionItemRepository; import reviewme.template.repository.QuestionRepository; -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.repository.SectionRepository; import reviewme.template.repository.TemplateRepository; import reviewme.template.service.dto.response.OptionGroupResponse; @@ -31,8 +29,6 @@ @RequiredArgsConstructor public class TemplateMapper { - public static final String REVIEWEE_NAME_PLACEHOLDER = "{revieweeName}"; - private final TemplateRepository templateRepository; private final SectionRepository sectionRepository; private final QuestionRepository questionRepository; @@ -48,7 +44,7 @@ public TemplateResponse mapToTemplateResponse(ReviewGroup reviewGroup) { List sectionResponses = template.getSectionIds() .stream() - .map(this::mapToSectionResponse) + .map(sectionId -> mapToSectionResponse(template.getId(), sectionId)) .toList(); return new TemplateResponse( @@ -59,14 +55,12 @@ public TemplateResponse mapToTemplateResponse(ReviewGroup reviewGroup) { ); } - private SectionResponse mapToSectionResponse(TemplateSection templateSection) { - Section section = sectionRepository.findById(templateSection.getSectionId()) - .orElseThrow(() -> new SectionInTemplateNotFoundException( - templateSection.getTemplateId(), templateSection.getSectionId()) - ); + private SectionResponse mapToSectionResponse(long templateId, long sectionId) { + Section section = sectionRepository.findById(sectionId) + .orElseThrow(() -> new SectionInTemplateNotFoundException(templateId, sectionId)); List questionResponses = section.getQuestionIds() .stream() - .map(this::mapToQuestionResponse) + .map(questionId -> mapToQuestionResponse(section.getId(), questionId)) .toList(); return new SectionResponse( @@ -79,11 +73,9 @@ private SectionResponse mapToSectionResponse(TemplateSection templateSection) { ); } - private QuestionResponse mapToQuestionResponse(SectionQuestion sectionQuestion) { - Question question = questionRepository.findById(sectionQuestion.getQuestionId()) - .orElseThrow(() -> new QuestionInSectionNotFoundException( - sectionQuestion.getSectionId(), sectionQuestion.getQuestionId()) - ); + private QuestionResponse mapToQuestionResponse(long sectionId, long questionId) { + Question question = questionRepository.findById(questionId) + .orElseThrow(() -> new QuestionInSectionNotFoundException(sectionId, questionId)); OptionGroupResponse optionGroupResponse = optionGroupRepository.findByQuestionId(question.getId()) .map(this::mapToOptionGroupResponse) .orElse(null); From d5783eb2ad492d75682b86845280b69b3755cc01 Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 00:21:42 +0900 Subject: [PATCH 02/12] =?UTF-8?q?style:=20=EB=8B=A8=EC=96=B4=20=ED=91=9C?= =?UTF-8?q?=EA=B8=B0=20=EB=B3=80=EA=B2=BD=20(checkBox=20->=20checkbox)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/reviewme/DatabaseInitializer.java | 30 +++++++++---------- ...cludedNotProvidedOptionItemException.java} | 4 +-- ...ionGroupNotFoundByQuestionIdException.java | 2 +- .../service/mapper/ReviewListMapper.java | 4 +-- .../CheckboxTypedAnswerValidator.java | 4 +-- .../template/domain/structured/Questions.java | 2 +- .../review/domain/abstraction/ReviewTest.java | 6 ++-- .../CheckboxTypedAnswerValidatorTest.java | 16 +++++----- 8 files changed, 34 insertions(+), 34 deletions(-) rename backend/src/main/java/reviewme/review/service/exception/{CheckBoxAnswerIncludedNotProvidedOptionItemException.java => CheckboxAnswerIncludedNotProvidedOptionItemException.java} (84%) diff --git a/backend/src/main/java/reviewme/DatabaseInitializer.java b/backend/src/main/java/reviewme/DatabaseInitializer.java index 71c18843b..08f920553 100644 --- a/backend/src/main/java/reviewme/DatabaseInitializer.java +++ b/backend/src/main/java/reviewme/DatabaseInitializer.java @@ -53,10 +53,10 @@ public void setup() { long growthOptionId = optionItemRepository.save(new OptionItem("🌱성장 마인드셋 (예: 새로운 분야나 잘 모르는 분야에 도전하는 마음, 꾸준한 노력으로 프로젝트 이전보다 성장하는 모습)",categoryOptionGroupId,5, OptionType.CATEGORY )).getId(); // 커뮤니케이션 능력 섹션 - long checkBoxCommunicationQuestionId = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "커뮤니케이션, 협업 능력에서 어떤 부분이 인상 깊었는지 선택해주세요.", null, 1)).getId(); + long checkboxCommunicationQuestionId = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "커뮤니케이션, 협업 능력에서 어떤 부분이 인상 깊었는지 선택해주세요.", null, 1)).getId(); long textCommunicationQuestionId = questionRepository.save(new Question(true, QuestionType.TEXT, CATEGORY_TEXT_QUESTION, "상황을 자세하게 기록할수록 ${revieweeName}에게 도움이 돼요. ${revieweeName} 덕분에 팀이 원활한 소통을 이뤘거나, 함께 일하면서 배울 점이 있었는지 떠올려 보세요.", 2)).getId(); - long communicationSectionId = sectionRepository.save(new Section(VisibleType.CONDITIONAL, List.of(checkBoxCommunicationQuestionId, textCommunicationQuestionId), communicationOptionId, "커뮤니케이션 능력", CATEGORY_HEADER, 2)).getId(); - long communicationOptionGroupId = optionGroupRepository.save(new OptionGroup(checkBoxCommunicationQuestionId, KEYWORD_CHECKBOX_MIN_COUNT, KEYWORD_CHECKBOX_MAX_COUNT)).getId(); + long communicationSectionId = sectionRepository.save(new Section(VisibleType.CONDITIONAL, List.of(checkboxCommunicationQuestionId, textCommunicationQuestionId), communicationOptionId, "커뮤니케이션 능력", CATEGORY_HEADER, 2)).getId(); + long communicationOptionGroupId = optionGroupRepository.save(new OptionGroup(checkboxCommunicationQuestionId, KEYWORD_CHECKBOX_MIN_COUNT, KEYWORD_CHECKBOX_MAX_COUNT)).getId(); optionItemRepository.save(new OptionItem("반대 의견을 내더라도 듣는 사람이 기분 나쁘지 않게 이야기해요.",communicationOptionGroupId,1, OptionType.KEYWORD )); optionItemRepository.save(new OptionItem("팀원들의 의견을 잘 모아서 회의가 매끄럽게 진행되도록 해요.",communicationOptionGroupId,2, OptionType.KEYWORD )); optionItemRepository.save(new OptionItem("팀의 분위기를 주도해요.",communicationOptionGroupId,3, OptionType.KEYWORD )); @@ -66,10 +66,10 @@ public void setup() { optionItemRepository.save(new OptionItem("서로 다른 분야간의 소통도 중요하게 생각해요.",communicationOptionGroupId,7, OptionType.KEYWORD )); // 문제해결 능력 섹션 - long checkBoxProblemSolvingQuestionId = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "문제해결 능력에서 어느 부분이 인상 깊었는지 선택해주세요.", null, 1)).getId(); + long checkboxProblemSolvingQuestionId = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "문제해결 능력에서 어느 부분이 인상 깊었는지 선택해주세요.", null, 1)).getId(); long textProblemSolvingQuestionId = questionRepository.save(new Question(true, QuestionType.TEXT, CATEGORY_TEXT_QUESTION, "상황을 자세하게 기록할수록 ${revieweeName}에게 도움이 돼요. 어떤 문제 상황이 발생했고, ${revieweeName/가:이} 어떻게 해결했는지 그 과정을 떠올려 보세요.", 2)).getId(); - long problemSolvingSectionId = sectionRepository.save(new Section(VisibleType.CONDITIONAL, List.of(checkBoxProblemSolvingQuestionId, textProblemSolvingQuestionId), problemSolvingOptionId, "문제해결 능력", CATEGORY_HEADER, 3)).getId(); - long problemSolvingOptionGroupId = optionGroupRepository.save(new OptionGroup(checkBoxProblemSolvingQuestionId, KEYWORD_CHECKBOX_MIN_COUNT, KEYWORD_CHECKBOX_MAX_COUNT)).getId(); + long problemSolvingSectionId = sectionRepository.save(new Section(VisibleType.CONDITIONAL, List.of(checkboxProblemSolvingQuestionId, textProblemSolvingQuestionId), problemSolvingOptionId, "문제해결 능력", CATEGORY_HEADER, 3)).getId(); + long problemSolvingOptionGroupId = optionGroupRepository.save(new OptionGroup(checkboxProblemSolvingQuestionId, KEYWORD_CHECKBOX_MIN_COUNT, KEYWORD_CHECKBOX_MAX_COUNT)).getId(); optionItemRepository.save(new OptionItem("큰 문제를 작은 단위로 쪼개서 단계별로 해결해나가요.",problemSolvingOptionGroupId,1, OptionType.KEYWORD )); optionItemRepository.save(new OptionItem("낯선 문제를 만나도 당황하지 않고 차분하게 풀어나가요.",problemSolvingOptionGroupId,2, OptionType.KEYWORD )); optionItemRepository.save(new OptionItem("문제 해결을 위해 GPT등의 자원을 적극적으로 활용해요.",problemSolvingOptionGroupId,3, OptionType.KEYWORD )); @@ -80,10 +80,10 @@ public void setup() { optionItemRepository.save(new OptionItem("문제 원인과 해결책에 대한 가설을 세우고 직접 실험해봐요.",problemSolvingOptionGroupId,8, OptionType.KEYWORD )); // 시간 관리 능력 섹션 - long checkBoxTimeManagingQuestionId = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "시간 관리 능력에서 어느 부분이 인상 깊었는지 선택해주세요.", null, 1)).getId(); + long checkboxTimeManagingQuestionId = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "시간 관리 능력에서 어느 부분이 인상 깊었는지 선택해주세요.", null, 1)).getId(); long textTimeManagingQuestionId = questionRepository.save(new Question(true, QuestionType.TEXT, CATEGORY_TEXT_QUESTION, "상황을 자세하게 기록할수록 ${revieweeName}에게 도움이 돼요. ${revieweeName} 덕분에 팀이 효율적으로 시간관리를 할 수 있었는지 떠올려 보세요.", 2)).getId(); - long timeManagingSectionId = sectionRepository.save(new Section(VisibleType.CONDITIONAL, List.of(checkBoxTimeManagingQuestionId, textTimeManagingQuestionId), timeManagingOptionId, "시간관리 능력", CATEGORY_HEADER, 4)).getId(); - long timeManagingOptionGroupId = optionGroupRepository.save(new OptionGroup(checkBoxTimeManagingQuestionId, KEYWORD_CHECKBOX_MIN_COUNT, KEYWORD_CHECKBOX_MAX_COUNT)).getId(); + long timeManagingSectionId = sectionRepository.save(new Section(VisibleType.CONDITIONAL, List.of(checkboxTimeManagingQuestionId, textTimeManagingQuestionId), timeManagingOptionId, "시간관리 능력", CATEGORY_HEADER, 4)).getId(); + long timeManagingOptionGroupId = optionGroupRepository.save(new OptionGroup(checkboxTimeManagingQuestionId, KEYWORD_CHECKBOX_MIN_COUNT, KEYWORD_CHECKBOX_MAX_COUNT)).getId(); optionItemRepository.save(new OptionItem("프로젝트의 일정과 주요 마일스톤을 설정하여 체계적으로 일정을 관리해요.",timeManagingOptionGroupId,1, OptionType.KEYWORD )); optionItemRepository.save(new OptionItem("일정에 따라 마감 기한을 잘 지켜요.",timeManagingOptionGroupId,2, OptionType.KEYWORD )); optionItemRepository.save(new OptionItem("업무의 중요도와 긴급성을 고려하여 우선 순위를 정하고, 그에 따라 작업을 분배해요.",timeManagingOptionGroupId,3, OptionType.KEYWORD )); @@ -91,10 +91,10 @@ public void setup() { optionItemRepository.save(new OptionItem("회의 시간과 같은 약속된 시간을 잘 지켜요.",timeManagingOptionGroupId,5, OptionType.KEYWORD )); // 기술 역량 섹션 - long checkBoxTechnicalQuestionId = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "기술 역량, 전문 지식에서 어떤 부분이 인상 깊었는지 선택해주세요.", null, 1)).getId(); + long checkboxTechnicalQuestionId = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "기술 역량, 전문 지식에서 어떤 부분이 인상 깊었는지 선택해주세요.", null, 1)).getId(); long textTechnicalQuestionId = questionRepository.save(new Question(true, QuestionType.TEXT, CATEGORY_TEXT_QUESTION, "상황을 자세하게 기록할수록 ${revieweeName}에게 도움이 돼요. ${revieweeName} 덕분에 기술적 역량, 전문 지식적으로 도움을 받은 경험을 떠올려 보세요.", 2)).getId(); - long technicalSectionId = sectionRepository.save(new Section(VisibleType.CONDITIONAL, List.of(checkBoxTechnicalQuestionId, textTechnicalQuestionId), technicalOptionId, "기술 역량", CATEGORY_HEADER, 5)).getId(); - long technicalOptionGroupId = optionGroupRepository.save(new OptionGroup(checkBoxTechnicalQuestionId, KEYWORD_CHECKBOX_MIN_COUNT, KEYWORD_CHECKBOX_MAX_COUNT)).getId(); + long technicalSectionId = sectionRepository.save(new Section(VisibleType.CONDITIONAL, List.of(checkboxTechnicalQuestionId, textTechnicalQuestionId), technicalOptionId, "기술 역량", CATEGORY_HEADER, 5)).getId(); + long technicalOptionGroupId = optionGroupRepository.save(new OptionGroup(checkboxTechnicalQuestionId, KEYWORD_CHECKBOX_MIN_COUNT, KEYWORD_CHECKBOX_MAX_COUNT)).getId(); optionItemRepository.save(new OptionItem("관련 언어 / 라이브러리 / 프레임워크 지식이 풍부해요.",technicalOptionGroupId,1, OptionType.KEYWORD )); optionItemRepository.save(new OptionItem("인프라 지식이 풍부해요.",technicalOptionGroupId,2, OptionType.KEYWORD )); optionItemRepository.save(new OptionItem("CS 지식이 풍부해요.",technicalOptionGroupId,3, OptionType.KEYWORD )); @@ -109,10 +109,10 @@ public void setup() { optionItemRepository.save(new OptionItem("지속적인 학습과 공유를 통해 팀의 기술 수준을 높였어요.",technicalOptionGroupId,12, OptionType.KEYWORD )); // 성장 마인드셋 섹션 - long checkBoxGrowthQuestionId = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "성장 마인드셋에서 어떤 부분이 인상 깊었는지 선택해주세요.", null, 1)).getId(); + long checkboxGrowthQuestionId = questionRepository.save(new Question(true, QuestionType.CHECKBOX, "성장 마인드셋에서 어떤 부분이 인상 깊었는지 선택해주세요.", null, 1)).getId(); long textGrowthQuestionId = questionRepository.save(new Question(true, QuestionType.TEXT, CATEGORY_TEXT_QUESTION, "상황을 자세하게 기록할수록 ${revieweeName}에게 도움이 돼요. 인상깊었던 ${revieweeName}의 성장 마인드셋을 떠올려 보세요.", 2)).getId(); - long growthSectionId = sectionRepository.save(new Section(VisibleType.CONDITIONAL, List.of(checkBoxGrowthQuestionId, textGrowthQuestionId), growthOptionId, "성장 마인드셋", CATEGORY_HEADER, 6)).getId(); - long growthOptionGroupId = optionGroupRepository.save(new OptionGroup(checkBoxGrowthQuestionId, KEYWORD_CHECKBOX_MIN_COUNT, KEYWORD_CHECKBOX_MAX_COUNT)).getId(); + long growthSectionId = sectionRepository.save(new Section(VisibleType.CONDITIONAL, List.of(checkboxGrowthQuestionId, textGrowthQuestionId), growthOptionId, "성장 마인드셋", CATEGORY_HEADER, 6)).getId(); + long growthOptionGroupId = optionGroupRepository.save(new OptionGroup(checkboxGrowthQuestionId, KEYWORD_CHECKBOX_MIN_COUNT, KEYWORD_CHECKBOX_MAX_COUNT)).getId(); optionItemRepository.save(new OptionItem("어떤 상황에도 긍정적인 태도로 임해요.",growthOptionGroupId,1, OptionType.KEYWORD )); optionItemRepository.save(new OptionItem("주변 사람들한테 질문하는 것을 부끄러워하지 않아요.",growthOptionGroupId,2, OptionType.KEYWORD )); optionItemRepository.save(new OptionItem("어려움이 있어도 끝까지 해내요.",growthOptionGroupId,3, OptionType.KEYWORD )); diff --git a/backend/src/main/java/reviewme/review/service/exception/CheckBoxAnswerIncludedNotProvidedOptionItemException.java b/backend/src/main/java/reviewme/review/service/exception/CheckboxAnswerIncludedNotProvidedOptionItemException.java similarity index 84% rename from backend/src/main/java/reviewme/review/service/exception/CheckBoxAnswerIncludedNotProvidedOptionItemException.java rename to backend/src/main/java/reviewme/review/service/exception/CheckboxAnswerIncludedNotProvidedOptionItemException.java index e902d3e2f..1a850bb61 100644 --- a/backend/src/main/java/reviewme/review/service/exception/CheckBoxAnswerIncludedNotProvidedOptionItemException.java +++ b/backend/src/main/java/reviewme/review/service/exception/CheckboxAnswerIncludedNotProvidedOptionItemException.java @@ -5,9 +5,9 @@ import reviewme.global.exception.BadRequestException; @Slf4j -public class CheckBoxAnswerIncludedNotProvidedOptionItemException extends BadRequestException { +public class CheckboxAnswerIncludedNotProvidedOptionItemException extends BadRequestException { - public CheckBoxAnswerIncludedNotProvidedOptionItemException(long questionId, + public CheckboxAnswerIncludedNotProvidedOptionItemException(long questionId, List providedOptionIds, List submittedOptionIds) { super("제공되는 선택지에 없는 선택지를 응답했어요."); diff --git a/backend/src/main/java/reviewme/review/service/exception/OptionGroupNotFoundByQuestionIdException.java b/backend/src/main/java/reviewme/review/service/exception/OptionGroupNotFoundByQuestionIdException.java index 9039de3ae..f0b2899f8 100644 --- a/backend/src/main/java/reviewme/review/service/exception/OptionGroupNotFoundByQuestionIdException.java +++ b/backend/src/main/java/reviewme/review/service/exception/OptionGroupNotFoundByQuestionIdException.java @@ -8,6 +8,6 @@ public class OptionGroupNotFoundByQuestionIdException extends DataInconsistencyE public OptionGroupNotFoundByQuestionIdException(long questionId) { super("서버 내부에 문제가 발생했습니다. 잠시 후 다시 시도해주세요."); - log.error("User submitted checkBoxAnswer without provided options - questionId: {}", questionId, this); + log.error("User submitted checkboxAnswer without provided options - questionId: {}", questionId, this); } } diff --git a/backend/src/main/java/reviewme/review/service/mapper/ReviewListMapper.java b/backend/src/main/java/reviewme/review/service/mapper/ReviewListMapper.java index ab5ec4327..d0e4d74d1 100644 --- a/backend/src/main/java/reviewme/review/service/mapper/ReviewListMapper.java +++ b/backend/src/main/java/reviewme/review/service/mapper/ReviewListMapper.java @@ -48,13 +48,13 @@ private ReviewListElementResponse mapToReviewListElementResponse(Review review, private List mapToCategoryOptionResponse(Review review, List categoryOptionItems) { - Set checkBoxOptionIds = review.getAnswersByType(CheckboxAnswer.class) + Set checkboxOptionIds = review.getAnswersByType(CheckboxAnswer.class) .stream() .flatMap(answer -> answer.getSelectedOptionIds().stream()) .map(CheckboxAnswerSelectedOption::getSelectedOptionId) .collect(Collectors.toSet()); return categoryOptionItems.stream() - .filter(optionItem -> checkBoxOptionIds.contains(optionItem.getId())) + .filter(optionItem -> checkboxOptionIds.contains(optionItem.getId())) .map(optionItem -> new ReviewCategoryResponse(optionItem.getId(), optionItem.getContent())) .toList(); } diff --git a/backend/src/main/java/reviewme/review/service/validator/CheckboxTypedAnswerValidator.java b/backend/src/main/java/reviewme/review/service/validator/CheckboxTypedAnswerValidator.java index d6cd50eec..fac17743d 100644 --- a/backend/src/main/java/reviewme/review/service/validator/CheckboxTypedAnswerValidator.java +++ b/backend/src/main/java/reviewme/review/service/validator/CheckboxTypedAnswerValidator.java @@ -14,7 +14,7 @@ import reviewme.review.domain.Answer; import reviewme.review.domain.CheckboxAnswerSelectedOption; import reviewme.review.domain.CheckboxAnswer; -import reviewme.review.service.exception.CheckBoxAnswerIncludedNotProvidedOptionItemException; +import reviewme.review.service.exception.CheckboxAnswerIncludedNotProvidedOptionItemException; import reviewme.review.service.exception.OptionGroupNotFoundByQuestionIdException; import reviewme.review.service.exception.SelectedOptionItemCountOutOfRangeException; import reviewme.review.service.exception.SubmittedQuestionNotFoundException; @@ -53,7 +53,7 @@ private void validateOnlyIncludingProvidedOptionItem(CheckboxAnswer checkboxAnsw List answeredOptionItemIds = extractAnsweredOptionItemIds(checkboxAnswer); if (!new HashSet<>(providedOptionItemIds).containsAll(answeredOptionItemIds)) { - throw new CheckBoxAnswerIncludedNotProvidedOptionItemException( + throw new CheckboxAnswerIncludedNotProvidedOptionItemException( checkboxAnswer.getQuestionId(), providedOptionItemIds, answeredOptionItemIds ); } diff --git a/backend/src/main/java/reviewme/template/domain/structured/Questions.java b/backend/src/main/java/reviewme/template/domain/structured/Questions.java index 7ec6efacf..5b153ebd8 100644 --- a/backend/src/main/java/reviewme/template/domain/structured/Questions.java +++ b/backend/src/main/java/reviewme/template/domain/structured/Questions.java @@ -27,7 +27,7 @@ public List getQuestionIds() { .toList(); } - public List getCheckBoxQuestionIds() { + public List getCheckboxQuestionIds() { return questions.stream() .filter(Question::isCheckboxType) .map(Question::getId) diff --git a/backend/src/test/java/reviewme/review/domain/abstraction/ReviewTest.java b/backend/src/test/java/reviewme/review/domain/abstraction/ReviewTest.java index 47ead260f..35901fcc3 100644 --- a/backend/src/test/java/reviewme/review/domain/abstraction/ReviewTest.java +++ b/backend/src/test/java/reviewme/review/domain/abstraction/ReviewTest.java @@ -45,16 +45,16 @@ class ReviewTest { void 리뷰에_특정_질문에_대한_답변이_있는지_여부를_반환한다() { // given long textQuestionId = 1L; - long checkBoxQuestionId = 2L; + long checkboxQuestionId = 2L; TextAnswer textAnswer = new TextAnswer(textQuestionId, "답변"); - CheckboxAnswer checkboxAnswer = new CheckboxAnswer(checkBoxQuestionId, List.of(1L)); + CheckboxAnswer checkboxAnswer = new CheckboxAnswer(checkboxQuestionId, List.of(1L)); Review review = new Review(1L, 1L, List.of(textAnswer, checkboxAnswer)); // when, then assertAll( () -> assertThat(review.hasAnsweredQuestion(textQuestionId)).isTrue(), - () -> assertThat(review.hasAnsweredQuestion(checkBoxQuestionId)).isTrue() + () -> assertThat(review.hasAnsweredQuestion(checkboxQuestionId)).isTrue() ); } } diff --git a/backend/src/test/java/reviewme/review/service/validator/CheckboxTypedAnswerValidatorTest.java b/backend/src/test/java/reviewme/review/service/validator/CheckboxTypedAnswerValidatorTest.java index 57e7dfd4b..4612f4624 100644 --- a/backend/src/test/java/reviewme/review/service/validator/CheckboxTypedAnswerValidatorTest.java +++ b/backend/src/test/java/reviewme/review/service/validator/CheckboxTypedAnswerValidatorTest.java @@ -15,7 +15,7 @@ import reviewme.template.repository.OptionItemRepository; import reviewme.template.repository.QuestionRepository; import reviewme.review.domain.CheckboxAnswer; -import reviewme.review.service.exception.CheckBoxAnswerIncludedNotProvidedOptionItemException; +import reviewme.review.service.exception.CheckboxAnswerIncludedNotProvidedOptionItemException; import reviewme.review.service.exception.OptionGroupNotFoundByQuestionIdException; import reviewme.review.service.exception.SelectedOptionItemCountOutOfRangeException; import reviewme.review.service.exception.SubmittedQuestionNotFoundException; @@ -25,7 +25,7 @@ class CheckboxTypedAnswerValidatorTest { @Autowired - private CheckboxTypedAnswerValidator checkBoxAnswerValidator; + private CheckboxTypedAnswerValidator checkboxAnswerValidator; @Autowired private QuestionRepository questionRepository; @@ -43,7 +43,7 @@ class CheckboxTypedAnswerValidatorTest { CheckboxAnswer checkboxAnswer = new CheckboxAnswer(notSavedQuestionId, List.of(1L)); // when, then - assertThatCode(() -> checkBoxAnswerValidator.validate(checkboxAnswer)) + assertThatCode(() -> checkboxAnswerValidator.validate(checkboxAnswer)) .isInstanceOf(SubmittedQuestionNotFoundException.class); } @@ -54,7 +54,7 @@ class CheckboxTypedAnswerValidatorTest { CheckboxAnswer checkboxAnswer = new CheckboxAnswer(savedQuestion.getId(), List.of(1L)); // when, then - assertThatCode(() -> checkBoxAnswerValidator.validate(checkboxAnswer)) + assertThatCode(() -> checkboxAnswerValidator.validate(checkboxAnswer)) .isInstanceOf(OptionGroupNotFoundByQuestionIdException.class); } @@ -69,8 +69,8 @@ class CheckboxTypedAnswerValidatorTest { List.of(savedOptionItem.getId() + 1L)); // when, then - assertThatCode(() -> checkBoxAnswerValidator.validate(checkboxAnswer)) - .isInstanceOf(CheckBoxAnswerIncludedNotProvidedOptionItemException.class); + assertThatCode(() -> checkboxAnswerValidator.validate(checkboxAnswer)) + .isInstanceOf(CheckboxAnswerIncludedNotProvidedOptionItemException.class); } @Test @@ -86,7 +86,7 @@ class CheckboxTypedAnswerValidatorTest { List.of(savedOptionItem1.getId())); // when, then - assertThatCode(() -> checkBoxAnswerValidator.validate(checkboxAnswer)) + assertThatCode(() -> checkboxAnswerValidator.validate(checkboxAnswer)) .isInstanceOf(SelectedOptionItemCountOutOfRangeException.class); } @@ -104,7 +104,7 @@ class CheckboxTypedAnswerValidatorTest { savedQuestion.getId(), List.of(savedOptionItem1.getId(), savedOptionItem2.getId())); // when, then - assertThatCode(() -> checkBoxAnswerValidator.validate(checkboxAnswer)) + assertThatCode(() -> checkboxAnswerValidator.validate(checkboxAnswer)) .isInstanceOf(SelectedOptionItemCountOutOfRangeException.class); } } From 181b67a121315c9627c1e142a6007e61bb6670bd Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 00:22:38 +0900 Subject: [PATCH 03/12] =?UTF-8?q?test:=20Template=EC=99=80=20Section=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EB=90=9C=20=EA=B8=B0=EB=8A=A5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reviewme/template/domain/SectionTest.java | 60 +++++++++++++++++++ .../template/domain/TemplateTest.java | 45 ++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 backend/src/test/java/reviewme/template/domain/TemplateTest.java diff --git a/backend/src/test/java/reviewme/template/domain/SectionTest.java b/backend/src/test/java/reviewme/template/domain/SectionTest.java index 0e3f1339b..b139f0ee5 100644 --- a/backend/src/test/java/reviewme/template/domain/SectionTest.java +++ b/backend/src/test/java/reviewme/template/domain/SectionTest.java @@ -1,14 +1,74 @@ package reviewme.template.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 static reviewme.fixture.SectionFixture.조건부로_보이는_섹션; import static reviewme.fixture.SectionFixture.항상_보이는_섹션; import java.util.List; import org.junit.jupiter.api.Test; +import reviewme.template.domain.exception.DuplicateQuestionIdException; +import reviewme.template.domain.exception.QuestionIdsNotExistException; class SectionTest { + @Test + void 섹션_생성시_질문ID가_없을_경우_예외가_발생한다() { + // given, when, then + assertAll( + () -> assertThatThrownBy(() -> 항상_보이는_섹션(null)).isInstanceOf(QuestionIdsNotExistException.class), + () -> assertThatThrownBy(() -> 항상_보이는_섹션(List.of())).isInstanceOf(QuestionIdsNotExistException.class) + ); + } + + @Test + void 섹션_생성시_중복된_질문ID가_있을_경우_예외가_발생한다() { + // given + List questionIds = List.of(1L, 1L, 2L, 3L); + + // when, then + assertThatThrownBy(() -> 항상_보이는_섹션(questionIds)) + .isInstanceOf(DuplicateQuestionIdException.class); + } + + @Test + void 섹션의_질문ID들을_반환한다() { + // given + List questionIds = List.of(1L, 2L, 3L); + Section section = 항상_보이는_섹션(questionIds); + + // when + List actual = section.getQuestionIds(); + + // then + assertThat(actual).containsExactlyInAnyOrderElementsOf(questionIds); + } + + @Test + void 항상_보이는_섹션일_경우_ture를_반환한다() { + // given + Section section = 항상_보이는_섹션(List.of(1L)); + + // when + boolean actual = section.isAlwaysVisible(); + + // then + assertThat(actual).isTrue(); + } + + @Test + void 항상_보이는_섹션이_아닐_경우_false를_반환한다() { + // given + Section section = 조건부로_보이는_섹션(List.of(1L), 2L); + + // when + boolean actual = section.isAlwaysVisible(); + + // then + assertThat(actual).isFalse(); + } + @Test void 조건_옵션을_선택하면_섹션이_보인다() { // given diff --git a/backend/src/test/java/reviewme/template/domain/TemplateTest.java b/backend/src/test/java/reviewme/template/domain/TemplateTest.java new file mode 100644 index 000000000..c57127268 --- /dev/null +++ b/backend/src/test/java/reviewme/template/domain/TemplateTest.java @@ -0,0 +1,45 @@ +package reviewme.template.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 static reviewme.fixture.TemplateFixture.템플릿; + +import java.util.List; +import org.junit.jupiter.api.Test; +import reviewme.template.domain.exception.DuplicateSectionIdException; +import reviewme.template.domain.exception.SectionIdsNotExistException; + +class TemplateTest { + + @Test + void 템플릿_생성시_섹션ID가_없을_경우_예외가_발생한다() { + // given, when, then + assertAll( + () -> assertThatThrownBy(() -> 템플릿(null)).isInstanceOf(SectionIdsNotExistException.class), + () -> assertThatThrownBy(() -> 템플릿(List.of())).isInstanceOf(SectionIdsNotExistException.class) + ); + } + + @Test + void 템플릿_생성시_중복된_섹션ID가_있을_경우_예외가_발생한다() { + // given + List sectionIds = List.of(1L, 1L, 2L, 3L); + + // when, then + assertThatThrownBy(() -> 템플릿(sectionIds)).isInstanceOf(DuplicateSectionIdException.class); + } + + @Test + void 템플릿의_섹션ID들을_반환한다() { + // given + List sectionIds = List.of(1L, 2L, 3L); + Template template = 템플릿(sectionIds); + + // when + List actual = template.getSectionIds(); + + // then + assertThat(actual).containsExactlyInAnyOrderElementsOf(sectionIds); + } +} From 1e9d3e50bee782156a177e1bc2cf29e4f6eeb892 Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 11:42:51 +0900 Subject: [PATCH 04/12] =?UTF-8?q?refactor:=20=EC=A4=91=EB=B3=B5=EB=90=A0?= =?UTF-8?q?=20=EC=88=98=20=EC=9E=88=EB=8A=94=20=EB=B0=98=ED=99=98=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20Set=EC=9C=BC=EB=A1=9C=20=EB=B0=98=ED=99=98=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20(List=20->=20Set)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/structured/OptionGroups.java | 10 ++++++-- .../domain/structured/OptionItems.java | 7 ++++-- .../structured/StructuredTemplateCreator.java | 25 ++++++++++--------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java b/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java index 865f57522..8e8c8a679 100644 --- a/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java +++ b/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java @@ -1,6 +1,8 @@ package reviewme.template.domain.structured; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import lombok.Getter; import reviewme.template.domain.OptionGroup; import reviewme.template.domain.exception.OptionGroupNotExistException; @@ -29,9 +31,13 @@ public List getOptionGroupIds() { .toList(); } - public List getQuestionIds() { + public Set getQuestionIds() { return optionGroups.stream() .map(OptionGroup::getQuestionId) - .toList(); + .collect(Collectors.toSet()); + } + + public int size() { + return optionGroups.size(); } } diff --git a/backend/src/main/java/reviewme/template/domain/structured/OptionItems.java b/backend/src/main/java/reviewme/template/domain/structured/OptionItems.java index 6419d1281..5b0f13f94 100644 --- a/backend/src/main/java/reviewme/template/domain/structured/OptionItems.java +++ b/backend/src/main/java/reviewme/template/domain/structured/OptionItems.java @@ -1,6 +1,8 @@ package reviewme.template.domain.structured; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import reviewme.template.domain.OptionItem; import reviewme.template.domain.exception.OptionItemNotExistException; @@ -27,9 +29,10 @@ public List getOptionItemIds() { .toList(); } - public List getOptionGroupIds() { + public Set getOptionGroupIds() { return optionItems.stream() .map(OptionItem::getOptionGroupId) - .toList(); + .distinct() + .collect(Collectors.toSet()); } } diff --git a/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplateCreator.java b/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplateCreator.java index 55db23762..da3647ea3 100644 --- a/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplateCreator.java +++ b/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplateCreator.java @@ -43,7 +43,7 @@ public StructuredTemplate createWithoutCheckBox(Template template, List
validateSections(template, sections); validateSectionsVisibility(sections); validateQuestions(sections, questions); - validateNoCheckBoxQuestion(questions); + validateNoCheckboxQuestion(questions); return new StructuredTemplate(template, sections, questions); } @@ -55,33 +55,34 @@ private void validateTemplate(Template template) { } private void validateSections(Template template, Sections sections) { - List sectionIds = sections.getSectionIds(); - if (!new HashSet<>(template.getSectionIds()).containsAll(sectionIds)) { + Set sectionIdsOfTemplate = new HashSet<>(template.getSectionIds()); + Set sectionIds = new HashSet<>(sections.getSectionIds()); + if (!sectionIdsOfTemplate.equals(sectionIds)) { throw new StructuredTemplateSectionValidationException(template.getId(), sectionIds); } } private void validateQuestions(Sections sections, Questions questions) { Set questionIdsOfSections = sections.getQuestionIds(); - List questionIds = questions.getQuestionIds(); - if (!questionIdsOfSections.containsAll(questionIds)) { + Set questionIds = new HashSet<>(questions.getQuestionIds()); + if (!questionIdsOfSections.equals(questionIds)) { throw new StructuredTemplateQuestionValidationException(questionIdsOfSections, questionIds); } } private void validateOptionGroup(Questions questions, OptionGroups optionGroups) { - List checkboxQuestionIds = questions.getCheckBoxQuestionIds(); - List questionIdsOfOptionGroup = optionGroups.getQuestionIds(); - if (!new HashSet<>(checkboxQuestionIds).containsAll(questionIdsOfOptionGroup)) { + Set checkboxQuestionIds = new HashSet<>(questions.getCheckboxQuestionIds()); + Set questionIdsOfOptionGroup = optionGroups.getQuestionIds(); + if (!checkboxQuestionIds.equals(questionIdsOfOptionGroup) || checkboxQuestionIds.size() < optionGroups.size()) { throw new StructuredTemplateOptionGroupValidationException(checkboxQuestionIds, questionIdsOfOptionGroup); } } private void validateOptionItems(OptionGroups optionGroups, OptionItems optionItems) { - List optionGroupIds = optionGroups.getOptionGroupIds(); - List OptionGroupIdsOfItems = optionItems.getOptionGroupIds(); - if (!new HashSet<>(optionGroupIds).containsAll(OptionGroupIdsOfItems)) { + Set optionGroupIds = new HashSet<>(optionGroups.getOptionGroupIds()); + Set OptionGroupIdsOfItems = optionItems.getOptionGroupIds(); + if (!optionGroupIds.equals(OptionGroupIdsOfItems)) { throw new StructuredTemplateOptionItemsValidationException(optionGroupIds, OptionGroupIdsOfItems); } } @@ -101,7 +102,7 @@ private void validateSectionsVisibility(Sections sections) { } } - private void validateNoCheckBoxQuestion(Questions questions) { + private void validateNoCheckboxQuestion(Questions questions) { if (questions.hasCheckboxQuestion()) { throw new StructuredTemplateCheckBoxQuestionFoundException(questions.getQuestionIds()); } From c92a40ae4f11883135f9aed09c8c62faac420432 Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 11:43:39 +0900 Subject: [PATCH 05/12] =?UTF-8?q?test:=20structured=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/structured/OptionGroupsTest.java | 67 +++ .../domain/structured/OptionItemsTest.java | 67 +++ .../domain/structured/QuestionsTest.java | 84 ++++ .../domain/structured/SectionsTest.java | 102 ++++ .../StructuredTemplateCreatorTest.java | 467 ++++++++++++++++++ 5 files changed, 787 insertions(+) create mode 100644 backend/src/test/java/reviewme/template/domain/structured/OptionGroupsTest.java create mode 100644 backend/src/test/java/reviewme/template/domain/structured/OptionItemsTest.java create mode 100644 backend/src/test/java/reviewme/template/domain/structured/QuestionsTest.java create mode 100644 backend/src/test/java/reviewme/template/domain/structured/SectionsTest.java create mode 100644 backend/src/test/java/reviewme/template/domain/structured/StructuredTemplateCreatorTest.java diff --git a/backend/src/test/java/reviewme/template/domain/structured/OptionGroupsTest.java b/backend/src/test/java/reviewme/template/domain/structured/OptionGroupsTest.java new file mode 100644 index 000000000..7946e13e5 --- /dev/null +++ b/backend/src/test/java/reviewme/template/domain/structured/OptionGroupsTest.java @@ -0,0 +1,67 @@ +package reviewme.template.domain.structured; + +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 static reviewme.fixture.OptionGroupFixture.선택지_그룹; + +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import reviewme.support.ServiceTest; +import reviewme.template.domain.OptionGroup; +import reviewme.template.domain.exception.OptionGroupNotExistException; +import reviewme.template.repository.OptionGroupRepository; + +@ServiceTest +class OptionGroupsTest { + + @Autowired + private OptionGroupRepository optionGroupRepository; + + @Test + void 존재하지_않는_옵션그룹으로_생성할_경우_예외가_발생한다() { + // given, when, then + assertAll( + () -> assertThatThrownBy(() -> new OptionGroups(null)) + .isInstanceOf(OptionGroupNotExistException.class), + () -> assertThatThrownBy(() -> new OptionGroups(List.of())) + .isInstanceOf(OptionGroupNotExistException.class) + ); + } + + @Test + void 옵션그룹ID들을_반환한다() { + // given + OptionGroup optionGroup1 = optionGroupRepository.save(선택지_그룹(1L)); + OptionGroup optionGroup2 = optionGroupRepository.save(선택지_그룹(1L)); + OptionGroup optionGroup3 = optionGroupRepository.save(선택지_그룹(1L)); + + OptionGroups optionGroups = new OptionGroups(List.of(optionGroup1, optionGroup2, optionGroup3)); + + // when + List actual = optionGroups.getOptionGroupIds(); + + // then + assertThat(actual).containsExactlyInAnyOrder(optionGroup1.getId(), optionGroup2.getId(), optionGroup3.getId()); + } + + @Test + void 옵션그룹의_질문ID들을_반환한다() { + // given + long questionId1 = 1L; + long questionId2 = 2L; + OptionGroup optionGroup1 = optionGroupRepository.save(선택지_그룹(questionId1)); + OptionGroup optionGroup2 = optionGroupRepository.save(선택지_그룹(questionId2)); + OptionGroup optionGroup3 = optionGroupRepository.save(선택지_그룹(questionId2)); + + OptionGroups optionGroups = new OptionGroups(List.of(optionGroup1, optionGroup2, optionGroup3)); + + // when + Set actual = optionGroups.getQuestionIds(); + + // then + assertThat(actual).containsExactlyInAnyOrder(questionId1, questionId2); + } +} diff --git a/backend/src/test/java/reviewme/template/domain/structured/OptionItemsTest.java b/backend/src/test/java/reviewme/template/domain/structured/OptionItemsTest.java new file mode 100644 index 000000000..7cd441351 --- /dev/null +++ b/backend/src/test/java/reviewme/template/domain/structured/OptionItemsTest.java @@ -0,0 +1,67 @@ +package reviewme.template.domain.structured; + +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 static reviewme.fixture.OptionItemFixture.선택지; + +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import reviewme.support.ServiceTest; +import reviewme.template.domain.OptionItem; +import reviewme.template.domain.exception.OptionItemNotExistException; +import reviewme.template.repository.OptionItemRepository; + +@ServiceTest +class OptionItemsTest { + + @Autowired + private OptionItemRepository optionItemRepository; + + @Test + void 존재하지_않는_옵션아이템으로_생성할_경우_예외가_발생한다() { + // given, when, then + assertAll( + () -> assertThatThrownBy(() -> new OptionItems(null)) + .isInstanceOf(OptionItemNotExistException.class), + () -> assertThatThrownBy(() -> new OptionItems(List.of())) + .isInstanceOf(OptionItemNotExistException.class) + ); + } + + @Test + void 옵션아이템ID들을_반환한다() { + // given + OptionItem optionItem1 = optionItemRepository.save(선택지(1L)); + OptionItem optionItem2 = optionItemRepository.save(선택지(1L)); + OptionItem optionItem3 = optionItemRepository.save(선택지(1L)); + + OptionItems optionItems = new OptionItems(List.of(optionItem1, optionItem2, optionItem3)); + + // when + List actual = optionItems.getOptionItemIds(); + + // then + assertThat(actual).containsExactlyInAnyOrder(optionItem1.getId(), optionItem2.getId(), optionItem3.getId()); + } + + @Test + void 옵션아이템의_옵션그룹ID들을_반환한다() { + // given + long optionGroupId1 = 1L; + long optionGroupId2 = 2L; + OptionItem optionItem1 = optionItemRepository.save(선택지(optionGroupId1)); + OptionItem optionItem2 = optionItemRepository.save(선택지(optionGroupId1)); + OptionItem optionItem3 = optionItemRepository.save(선택지(optionGroupId2)); + + OptionItems optionItems = new OptionItems(List.of(optionItem1, optionItem2, optionItem3)); + + // when + Set actual = optionItems.getOptionGroupIds(); + + // then + assertThat(actual).containsExactlyInAnyOrder(optionGroupId1, optionGroupId2); + } +} diff --git a/backend/src/test/java/reviewme/template/domain/structured/QuestionsTest.java b/backend/src/test/java/reviewme/template/domain/structured/QuestionsTest.java new file mode 100644 index 000000000..07fede9d9 --- /dev/null +++ b/backend/src/test/java/reviewme/template/domain/structured/QuestionsTest.java @@ -0,0 +1,84 @@ +package reviewme.template.domain.structured; + +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 static reviewme.fixture.QuestionFixture.서술형_필수_질문; +import static reviewme.fixture.QuestionFixture.선택형_필수_질문; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import reviewme.support.ServiceTest; +import reviewme.template.domain.Question; +import reviewme.template.domain.exception.QuestionNotExistException; +import reviewme.template.repository.QuestionRepository; + +@ServiceTest +class QuestionsTest { + + @Autowired + private QuestionRepository questionRepository; + + @Test + void 존재하지_않는_질문으로_생성할_경우_예외가_발생한다() { + // given, when, then + assertAll( + () -> assertThatThrownBy(() -> new Questions(null)).isInstanceOf(QuestionNotExistException.class), + () -> assertThatThrownBy(() -> new Questions(List.of())).isInstanceOf(QuestionNotExistException.class) + ); + } + + @Test + void 질문ID들을_반환한다() { + // given + Question question1 = questionRepository.save(서술형_필수_질문()); + Question question2 = questionRepository.save(서술형_필수_질문()); + Question question3 = questionRepository.save(서술형_필수_질문()); + + Questions questions = new Questions(List.of(question1, question2, question3)); + + // when + List actual = questions.getQuestionIds(); + + // then + assertThat(actual).containsExactlyInAnyOrder(question1.getId(), question2.getId(), question3.getId()); + } + + @Test + void 체크박스형_질문ID들을_반환한다() { + // given + Question textQuestion = questionRepository.save(서술형_필수_질문()); + Question checkboxQuestion1 = questionRepository.save(선택형_필수_질문()); + Question checkboxQuestion2 = questionRepository.save(선택형_필수_질문()); + + Questions questions = new Questions(List.of(textQuestion, checkboxQuestion1, checkboxQuestion2)); + + // when + List actual = questions.getCheckboxQuestionIds(); + + // then + assertThat(actual).containsExactlyInAnyOrder(checkboxQuestion1.getId(), checkboxQuestion2.getId()); + } + + @Test + void 체크박스_질문이_있는지_확인한다() { + // given + Question textQuestion = questionRepository.save(서술형_필수_질문()); + Question checkboxQuestion1 = questionRepository.save(선택형_필수_질문()); + Question checkboxQuestion2 = questionRepository.save(선택형_필수_질문()); + + Questions questions1 = new Questions(List.of(textQuestion, checkboxQuestion1, checkboxQuestion2)); + Questions questions2 = new Questions(List.of(textQuestion)); + + // when + boolean actual1 = questions1.hasCheckboxQuestion(); + boolean actual2 = questions2.hasCheckboxQuestion(); + + // then + assertAll( + () -> assertThat(actual1).isTrue(), + () -> assertThat(actual2).isFalse() + ); + } +} diff --git a/backend/src/test/java/reviewme/template/domain/structured/SectionsTest.java b/backend/src/test/java/reviewme/template/domain/structured/SectionsTest.java new file mode 100644 index 000000000..3920bbaa2 --- /dev/null +++ b/backend/src/test/java/reviewme/template/domain/structured/SectionsTest.java @@ -0,0 +1,102 @@ +package reviewme.template.domain.structured; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.*; +import static reviewme.fixture.SectionFixture.조건부로_보이는_섹션; +import static reviewme.fixture.SectionFixture.항상_보이는_섹션; + +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import reviewme.support.ServiceTest; +import reviewme.template.domain.Section; +import reviewme.template.domain.exception.SectionNotExistException; +import reviewme.template.repository.SectionRepository; + +@ServiceTest +class SectionsTest { + + @Autowired + private SectionRepository sectionRepository; + + @Test + void 존재하지_않는_섹션으로_생성할_경우_예외가_발생한다() { + // given, when, then + assertAll( + () -> assertThatThrownBy(() -> new Sections(null)).isInstanceOf(SectionNotExistException.class), + () -> assertThatThrownBy(() -> new Sections(List.of())).isInstanceOf(SectionNotExistException.class) + ); + } + + @Test + void 섹션ID들을_반환한다() { + // given + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(1L))); + Section section2 = sectionRepository.save(항상_보이는_섹션(List.of(1L))); + Section section3 = sectionRepository.save(항상_보이는_섹션(List.of(1L))); + + Sections sections = new Sections(List.of(section1, section2, section3)); + + // when + List actual = sections.getSectionIds(); + + // then + assertThat(actual).containsExactlyInAnyOrder(section1.getId(),section2.getId(), section3.getId()); + } + + @Test + void 섹션의_질문ID들을_반환한다() { + // given + long questionId1 = 1L; + long questionId2 = 2L; + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(questionId1))); + Section section2 = sectionRepository.save(항상_보이는_섹션(List.of(questionId2))); + Section section3 = sectionRepository.save(항상_보이는_섹션(List.of(questionId2))); + + Sections sections = new Sections(List.of(section1, section2, section3)); + + // when + Set actual = sections.getQuestionIds(); + + // then + assertThat(actual).containsExactlyInAnyOrder(questionId1, questionId2); + } + + @Test + void 선택형_옵션들이_주어질때_노출되지_않는_섹션의_ID를_반환한다() { + // given + long optionItemId1 = 1L; + long optionItemId2 = 2L; + Section alwaysVisibleSection = sectionRepository.save(항상_보이는_섹션(List.of(1L))); + Section conditionalSection1 = sectionRepository.save(조건부로_보이는_섹션(List.of(1L), optionItemId1)); + Section conditionalSection2 = sectionRepository.save(조건부로_보이는_섹션(List.of(1L), optionItemId2)); + + Sections sections = new Sections(List.of(alwaysVisibleSection, conditionalSection1, conditionalSection2)); + + // when + List actual = sections.getInvisibleSectionIds(List.of(optionItemId1)); + + // then + assertThat(actual).containsExactlyInAnyOrder(conditionalSection2.getId()); + } + + @Test + void 선택형_옵션들이_주어지지_않을때_노출되지_않는_섹션의_ID를_반환한다() { + // given + long optionItemId1 = 1L; + long optionItemId2 = 2L; + Section alwaysVisibleSection = sectionRepository.save(항상_보이는_섹션(List.of(1L))); + Section conditionalSection1 = sectionRepository.save(조건부로_보이는_섹션(List.of(1L), optionItemId1)); + Section conditionalSection2 = sectionRepository.save(조건부로_보이는_섹션(List.of(1L), optionItemId2)); + + Sections sections = new Sections(List.of(alwaysVisibleSection, conditionalSection1, conditionalSection2)); + + // when + List actual = sections.getInvisibleSectionIds(); + + // then + assertThat(actual).containsExactlyInAnyOrder(conditionalSection1.getId(), conditionalSection2.getId()); + } +} diff --git a/backend/src/test/java/reviewme/template/domain/structured/StructuredTemplateCreatorTest.java b/backend/src/test/java/reviewme/template/domain/structured/StructuredTemplateCreatorTest.java new file mode 100644 index 000000000..415edaf41 --- /dev/null +++ b/backend/src/test/java/reviewme/template/domain/structured/StructuredTemplateCreatorTest.java @@ -0,0 +1,467 @@ +package reviewme.template.domain.structured; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static reviewme.fixture.OptionGroupFixture.선택지_그룹; +import static reviewme.fixture.OptionItemFixture.선택지; +import static reviewme.fixture.QuestionFixture.서술형_옵션_질문; +import static reviewme.fixture.QuestionFixture.서술형_필수_질문; +import static reviewme.fixture.QuestionFixture.선택형_옵션_질문; +import static reviewme.fixture.QuestionFixture.선택형_필수_질문; +import static reviewme.fixture.SectionFixture.조건부로_보이는_섹션; +import static reviewme.fixture.SectionFixture.항상_보이는_섹션; +import static reviewme.fixture.TemplateFixture.템플릿; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import reviewme.support.ServiceTest; +import reviewme.template.domain.OptionGroup; +import reviewme.template.domain.OptionItem; +import reviewme.template.domain.Question; +import reviewme.template.domain.Section; +import reviewme.template.domain.Template; +import reviewme.template.domain.exception.StructuredTemplateCheckBoxQuestionFoundException; +import reviewme.template.domain.exception.StructuredTemplateInvisibleSectionFoundException; +import reviewme.template.domain.exception.StructuredTemplateNotExistTemplateException; +import reviewme.template.domain.exception.StructuredTemplateOptionGroupValidationException; +import reviewme.template.domain.exception.StructuredTemplateOptionItemsValidationException; +import reviewme.template.domain.exception.StructuredTemplateQuestionValidationException; +import reviewme.template.domain.exception.StructuredTemplateSectionValidationException; +import reviewme.template.repository.OptionGroupRepository; +import reviewme.template.repository.OptionItemRepository; +import reviewme.template.repository.QuestionRepository; +import reviewme.template.repository.SectionRepository; +import reviewme.template.repository.TemplateRepository; + +@ServiceTest +class StructuredTemplateCreatorTest { + + @Autowired + private TemplateRepository templateRepository; + + @Autowired + private SectionRepository sectionRepository; + + @Autowired + private QuestionRepository questionRepository; + + @Autowired + private OptionGroupRepository optionGroupRepository; + + @Autowired + private OptionItemRepository optionItemRepository; + + @Nested + @DisplayName("일반적인 구조화된 템플릿 생성 테스트") + class DefaultStructuredTemplateCreate { + + @Test + void 모든_타입의_질문이_포함된_구조화된_템플릿을_생성한다() { + // given + Question requiredCheckboxQuestion = questionRepository.save(선택형_필수_질문()); + Question optionalCheckboxQuestion = questionRepository.save(선택형_옵션_질문()); + Question requiredTextQuestion = questionRepository.save(서술형_필수_질문()); + Question optionalTextQuestion = questionRepository.save(서술형_옵션_질문()); + + OptionGroup optionGroup1 = optionGroupRepository.save(선택지_그룹(requiredCheckboxQuestion.getId())); + OptionItem optionItem1 = optionItemRepository.save(선택지(optionGroup1.getId())); + OptionItem optionItem2 = optionItemRepository.save(선택지(optionGroup1.getId())); + + OptionGroup optionGroup2 = optionGroupRepository.save(선택지_그룹(optionalCheckboxQuestion.getId())); + OptionItem optionItem3 = optionItemRepository.save(선택지(optionGroup2.getId())); + OptionItem optionItem4 = optionItemRepository.save(선택지(optionGroup2.getId())); + + Section section1 = sectionRepository.save( + 항상_보이는_섹션(List.of(requiredCheckboxQuestion.getId(), optionalTextQuestion.getId()))); + Section section2 = sectionRepository.save( + 조건부로_보이는_섹션(List.of(optionalCheckboxQuestion.getId(), requiredTextQuestion.getId()), + optionItem1.getId())); + Section section3 = sectionRepository.save( + 조건부로_보이는_섹션(List.of(optionalCheckboxQuestion.getId(), requiredTextQuestion.getId()), + optionItem2.getId())); + + Template template = templateRepository.save(템플릿( + List.of(section1.getId(), section2.getId(), section3.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertDoesNotThrow(() -> templateCreator.create( + template, + List.of(section1, section2, section3), + List.of(requiredCheckboxQuestion, optionalCheckboxQuestion, requiredTextQuestion, + optionalTextQuestion), + List.of(optionGroup1, optionGroup2), + List.of(optionItem1, optionItem2, optionItem3, optionItem4) + )); + } + + @Test + void 구조화된_템플릿_생성시_템플릿이_존재하지_않으면_예외가_발생한다() { + // given + Question checkboxQuestion = questionRepository.save(선택형_필수_질문()); + Question textQuestion = questionRepository.save(서술형_필수_질문()); + + OptionGroup optionGroup = optionGroupRepository.save(선택지_그룹(checkboxQuestion.getId())); + OptionItem optionItem1 = optionItemRepository.save(선택지(optionGroup.getId())); + OptionItem optionItem2 = optionItemRepository.save(선택지(optionGroup.getId())); + + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(checkboxQuestion.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션(List.of(textQuestion.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.create( + null, + List.of(section1, section2), + List.of(checkboxQuestion, textQuestion), + List.of(optionGroup), + List.of(optionItem1, optionItem2)) + ).isInstanceOf(StructuredTemplateNotExistTemplateException.class); + } + + @Test + void 구조화된_템플릿_생성시_템플릿과_섹션의_관계가_일치하지_않으면_예외가_발생한다() { + // given + Question checkboxQuestion = questionRepository.save(선택형_필수_질문()); + Question textQuestion = questionRepository.save(서술형_필수_질문()); + + OptionGroup optionGroup = optionGroupRepository.save(선택지_그룹(checkboxQuestion.getId())); + OptionItem optionItem1 = optionItemRepository.save(선택지(optionGroup.getId())); + OptionItem optionItem2 = optionItemRepository.save(선택지(optionGroup.getId())); + + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(checkboxQuestion.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션(List.of(textQuestion.getId()))); + + Template template = templateRepository.save(템플릿(List.of(section1.getId(), section2.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.create( + template, + List.of(section1), + List.of(checkboxQuestion, textQuestion), + List.of(optionGroup), + List.of(optionItem1, optionItem2)) + ).isInstanceOf(StructuredTemplateSectionValidationException.class); + } + + @Test + void 구조화된_템플릿_생성시_섹션과_질문의_관계가_일치하지_않으면_예외가_발생한다() { + // given + Question checkboxQuestion = questionRepository.save(선택형_필수_질문()); + Question textQuestion = questionRepository.save(서술형_필수_질문()); + + OptionGroup optionGroup = optionGroupRepository.save(선택지_그룹(checkboxQuestion.getId())); + OptionItem optionItem1 = optionItemRepository.save(선택지(optionGroup.getId())); + OptionItem optionItem2 = optionItemRepository.save(선택지(optionGroup.getId())); + + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(checkboxQuestion.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션(List.of(textQuestion.getId()))); + + Template template = templateRepository.save(템플릿(List.of(section1.getId(), section2.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.create( + template, + List.of(section1, section2), + List.of(checkboxQuestion), + List.of(optionGroup), + List.of(optionItem1, optionItem2)) + ).isInstanceOf(StructuredTemplateQuestionValidationException.class); + } + + @Test + void 구조화된_템플릿_생성시_질문과_옵션그룹의_관계가_일치하지_않으면_예외가_발생한다_하나의_질문에_두개_이상의_그룹이_묶인_경우() { + // given + Question checkboxQuestion = questionRepository.save(선택형_필수_질문()); + Question textQuestion = questionRepository.save(서술형_필수_질문()); + + OptionGroup optionGroup1 = optionGroupRepository.save(선택지_그룹(checkboxQuestion.getId())); + OptionItem optionItem1 = optionItemRepository.save(선택지(optionGroup1.getId())); + OptionItem optionItem2 = optionItemRepository.save(선택지(optionGroup1.getId())); + + OptionGroup optionGroup2 = optionGroupRepository.save(선택지_그룹(checkboxQuestion.getId())); + OptionItem optionItem3 = optionItemRepository.save(선택지(optionGroup2.getId())); + OptionItem optionItem4 = optionItemRepository.save(선택지(optionGroup2.getId())); + + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(checkboxQuestion.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션(List.of(textQuestion.getId()))); + + Template template = templateRepository.save(템플릿(List.of(section1.getId(), section2.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.create( + template, + List.of(section1, section2), + List.of(checkboxQuestion, textQuestion), + List.of(optionGroup1, optionGroup2), + List.of(optionItem1, optionItem2, optionItem3, optionItem4)) + ).isInstanceOf(StructuredTemplateOptionGroupValidationException.class); + } + + @Test + void 구조화된_템플릿_생성시_질문과_옵션그룹의_관계가_일치하지_않으면_예외가_발생한다_질문과_관계없는_그룹이_있는_경우() { + // given + Question checkboxQuestion1 = questionRepository.save(선택형_필수_질문()); + Question checkboxQuestion2 = questionRepository.save(선택형_필수_질문()); + Question textQuestion = questionRepository.save(서술형_필수_질문()); + + OptionGroup optionGroup1 = optionGroupRepository.save(선택지_그룹(checkboxQuestion1.getId())); + OptionItem optionItem1 = optionItemRepository.save(선택지(optionGroup1.getId())); + OptionItem optionItem2 = optionItemRepository.save(선택지(optionGroup1.getId())); + + OptionGroup optionGroup2 = optionGroupRepository.save(선택지_그룹(checkboxQuestion2.getId())); + OptionItem optionItem3 = optionItemRepository.save(선택지(optionGroup2.getId())); + OptionItem optionItem4 = optionItemRepository.save(선택지(optionGroup2.getId())); + + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(checkboxQuestion1.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션(List.of(textQuestion.getId()))); + + Template template = templateRepository.save(템플릿(List.of(section1.getId(), section2.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.create( + template, + List.of(section1, section2), + List.of(checkboxQuestion1, textQuestion), + List.of(optionGroup1, optionGroup2), + List.of(optionItem1, optionItem2, optionItem3, optionItem4)) + ).isInstanceOf(StructuredTemplateOptionGroupValidationException.class); + } + + @Test + void 구조화된_템플릿_생성시_옵션그룹과_옵션항목의_관계가_일치하지_않으면_예외가_발생한다() { + // given + Question checkboxQuestion1 = questionRepository.save(선택형_필수_질문()); + Question checkboxQuestion2 = questionRepository.save(선택형_필수_질문()); + Question textQuestion = questionRepository.save(서술형_필수_질문()); + + OptionGroup optionGroup1 = optionGroupRepository.save(선택지_그룹(checkboxQuestion1.getId())); + OptionItem optionItem1 = optionItemRepository.save(선택지(optionGroup1.getId())); + OptionItem optionItem2 = optionItemRepository.save(선택지(optionGroup1.getId())); + + OptionGroup optionGroup2 = optionGroupRepository.save(선택지_그룹(checkboxQuestion2.getId())); + OptionItem optionItem3 = optionItemRepository.save(선택지(optionGroup2.getId())); + OptionItem optionItem4 = optionItemRepository.save(선택지(optionGroup2.getId())); + + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(checkboxQuestion1.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션(List.of(textQuestion.getId()))); + + Template template = templateRepository.save(템플릿(List.of(section1.getId(), section2.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.create( + template, + List.of(section1, section2), + List.of(checkboxQuestion1, textQuestion), + List.of(optionGroup1), + List.of(optionItem1, optionItem2, optionItem3, optionItem4)) + ).isInstanceOf(StructuredTemplateOptionItemsValidationException.class); + } + + @Test + void 구조화된_템플릿_생성시_노출될_수_없는_섹션이_있으면_예외가_발생한다() { + // given + Question checkboxQuestion1 = questionRepository.save(선택형_필수_질문()); + Question checkboxQuestion2 = questionRepository.save(선택형_필수_질문()); + Question textQuestion1 = questionRepository.save(서술형_필수_질문()); + Question textQuestion2 = questionRepository.save(서술형_필수_질문()); + + OptionGroup optionGroup1 = optionGroupRepository.save(선택지_그룹(checkboxQuestion1.getId())); + OptionItem optionItem1 = optionItemRepository.save(선택지(optionGroup1.getId())); + OptionItem optionItem2 = optionItemRepository.save(선택지(optionGroup1.getId())); + + OptionGroup optionGroup2 = optionGroupRepository.save(선택지_그룹(checkboxQuestion2.getId())); + OptionItem optionItem3 = optionItemRepository.save(선택지(optionGroup2.getId())); + + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(checkboxQuestion1.getId()))); + Section section2 = sectionRepository.save( + 조건부로_보이는_섹션(List.of(textQuestion1.getId()), optionItem1.getId())); + Section section3 = sectionRepository.save( + 조건부로_보이는_섹션(List.of(textQuestion2.getId()), optionItem3.getId())); + + Template template = templateRepository.save( + 템플릿(List.of(section1.getId(), section2.getId(), section3.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.create( + template, + List.of(section1, section2, section3), + List.of(checkboxQuestion1, textQuestion1, textQuestion2), + List.of(optionGroup1), + List.of(optionItem1, optionItem2)) + ).isInstanceOf(StructuredTemplateInvisibleSectionFoundException.class); + } + } + + @Nested + @DisplayName("체크박스 타입의 질문이 없는 구조화된 템플릿 생성 테스트") + class NoCheckboxStructuredTemplateCreate { + + @Test + void 체크박스_타입의_질문이_제외된_구조화된_템플릿을_생성한다() { + // given + Question requiredQuestion1 = questionRepository.save(서술형_필수_질문()); + Question requiredQuestion2 = questionRepository.save(서술형_필수_질문()); + Question optionalQuestion = questionRepository.save(서술형_옵션_질문()); + + Section section1 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion1.getId(), optionalQuestion.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion2.getId(), optionalQuestion.getId()))); + + Template template = templateRepository.save(템플릿(List.of(section1.getId(), section2.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertDoesNotThrow(() -> templateCreator.createWithoutCheckBox( + template, + List.of(section1, section2), + List.of(requiredQuestion1, requiredQuestion2, optionalQuestion) + )); + } + + @Test + void 체크박스_타입의_질문이_제외된_구조화된_템플릿_생성시_템플릿이_존재하지_않으면_예외가_발생한다() { + // given + Question requiredQuestion1 = questionRepository.save(서술형_필수_질문()); + Question requiredQuestion2 = questionRepository.save(서술형_필수_질문()); + Question optionalQuestion = questionRepository.save(서술형_옵션_질문()); + + Section section1 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion1.getId(), optionalQuestion.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion2.getId(), optionalQuestion.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.createWithoutCheckBox( + null, + List.of(section1, section2), + List.of(requiredQuestion1, requiredQuestion2, optionalQuestion) + )).isInstanceOf(StructuredTemplateNotExistTemplateException.class); + } + + @Test + void 체크박스_타입의_질문이_제외된_구조화된_템플릿_생성시_템플릿과_섹션의_관계가_일치하지_않으면_예외가_발생한다() { + // given + Question requiredQuestion1 = questionRepository.save(서술형_필수_질문()); + Question requiredQuestion2 = questionRepository.save(서술형_필수_질문()); + Question optionalQuestion = questionRepository.save(서술형_옵션_질문()); + + Section section1 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion1.getId(), optionalQuestion.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion2.getId(), optionalQuestion.getId()))); + + Template template = templateRepository.save(템플릿(List.of(section1.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.createWithoutCheckBox( + template, + List.of(section1, section2), + List.of(requiredQuestion1, requiredQuestion2, optionalQuestion) + )).isInstanceOf(StructuredTemplateSectionValidationException.class); + } + + @Test + void 체크박스_타입의_질문이_제외된_구조화된_템플릿_생성시_섹션과_질문의_관계가_일치하지_않으면_예외가_발생한다() { + // given + Question requiredQuestion1 = questionRepository.save(서술형_필수_질문()); + Question requiredQuestion2 = questionRepository.save(서술형_필수_질문()); + Question optionalQuestion1 = questionRepository.save(서술형_옵션_질문()); + Question optionalQuestion2 = questionRepository.save(서술형_옵션_질문()); + + Section section1 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion1.getId(), optionalQuestion1.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion2.getId(), optionalQuestion1.getId()))); + + Template template = templateRepository.save(템플릿(List.of(section1.getId(), section2.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.createWithoutCheckBox( + template, + List.of(section1, section2), + List.of(requiredQuestion1, requiredQuestion2, optionalQuestion1, optionalQuestion2) + )).isInstanceOf(StructuredTemplateQuestionValidationException.class); + } + + @Test + void 체크박스_타입의_질문이_제외된_구조화된_템플릿_생성시_노출될_수_없는_섹션이_있으면_예외가_발생한다() { + // given + Question requiredQuestion1 = questionRepository.save(서술형_필수_질문()); + Question requiredQuestion2 = questionRepository.save(서술형_필수_질문()); + Question optionalQuestion1 = questionRepository.save(서술형_옵션_질문()); + + Section section1 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion1.getId(), optionalQuestion1.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion2.getId(), optionalQuestion1.getId()))); + Section section3 = sectionRepository.save(조건부로_보이는_섹션( + List.of(requiredQuestion1.getId()), 1L)); + + Template template = templateRepository.save(템플릿( + List.of(section1.getId(), section2.getId(), section3.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.createWithoutCheckBox( + template, + List.of(section1, section2, section3), + List.of(requiredQuestion1, requiredQuestion2, optionalQuestion1) + )).isInstanceOf(StructuredTemplateInvisibleSectionFoundException.class); + } + + @Test + void 체크박스_타입의_질문이_제외된_구조화된_템플릿_생성시_체크박스_타입의_질문이_존재하면_예외가_발생한다() { + // given + Question requiredQuestion1 = questionRepository.save(서술형_필수_질문()); + Question requiredQuestion2 = questionRepository.save(서술형_필수_질문()); + Question optionalQuestion = questionRepository.save(서술형_옵션_질문()); + Question checkboxQuestion = questionRepository.save(선택형_옵션_질문()); + + Section section1 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion1.getId(), optionalQuestion.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion2.getId(), optionalQuestion.getId()))); + Section section3 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion2.getId(), checkboxQuestion.getId()))); + + Template template = templateRepository.save(템플릿( + List.of(section1.getId(), section2.getId(), section3.getId()))); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + // when, then + assertThatThrownBy(() -> templateCreator.createWithoutCheckBox( + template, + List.of(section1, section2, section3), + List.of(requiredQuestion1, requiredQuestion2, optionalQuestion, checkboxQuestion) + )).isInstanceOf(StructuredTemplateCheckBoxQuestionFoundException.class); + } + } +} From 2feace125a8239178ad16f7c66169a1342a135fb Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 12:07:53 +0900 Subject: [PATCH 06/12] =?UTF-8?q?feat:=20StructuredTemplateService=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/structured/StructuredTemplate.java | 2 + .../service/StructuredTemplateService.java | 51 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 backend/src/main/java/reviewme/template/service/StructuredTemplateService.java diff --git a/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java b/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java index cf531ba12..0abf6018c 100644 --- a/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java +++ b/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java @@ -1,7 +1,9 @@ package reviewme.template.domain.structured; +import lombok.Getter; import reviewme.template.domain.Template; +@Getter public class StructuredTemplate { private final Template template; diff --git a/backend/src/main/java/reviewme/template/service/StructuredTemplateService.java b/backend/src/main/java/reviewme/template/service/StructuredTemplateService.java new file mode 100644 index 000000000..a1bee9583 --- /dev/null +++ b/backend/src/main/java/reviewme/template/service/StructuredTemplateService.java @@ -0,0 +1,51 @@ +package reviewme.template.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import reviewme.template.domain.OptionGroup; +import reviewme.template.domain.OptionItem; +import reviewme.template.domain.Question; +import reviewme.template.domain.Section; +import reviewme.template.domain.Template; +import reviewme.template.domain.structured.StructuredTemplate; +import reviewme.template.domain.structured.StructuredTemplateCreator; +import reviewme.template.repository.OptionGroupRepository; +import reviewme.template.repository.OptionItemRepository; +import reviewme.template.repository.QuestionRepository; +import reviewme.template.repository.SectionRepository; +import reviewme.template.repository.TemplateRepository; +import reviewme.template.service.exception.TemplateNotFoundException; + +@Service +@RequiredArgsConstructor +public class StructuredTemplateService { + + private final TemplateRepository templateRepository; + private final SectionRepository sectionRepository; + private final QuestionRepository questionRepository; + private final OptionGroupRepository optionGroupRepository; + private final OptionItemRepository optionItemRepository; + + @Transactional + public StructuredTemplate getStructuredTemplateById(long templateId) { + Template template = templateRepository.findById(templateId) + .orElseThrow(() -> new TemplateNotFoundException(templateId)); + + List
sections = sectionRepository.findAllByTemplateId(template.getId()); + List questions = questionRepository.findAllByTemplatedId(template.getId()); + List questionIds = questions.stream() + .map(Question::getId) + .toList(); + List optionGroups = optionGroupRepository.findAllByQuestionIds(questionIds); + List optionItems = optionItemRepository.findAllByQuestionIds(questionIds); + + StructuredTemplateCreator templateCreator = new StructuredTemplateCreator(); + + if (optionGroups.isEmpty() && optionItems.isEmpty()) { + return templateCreator.createWithoutCheckBox(template, sections, questions); + } + return templateCreator.create(template, sections, questions, optionGroups, optionItems); + } +} From f55c75cf4ba2fb13d976163f445b2a7850cd0246 Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 12:08:05 +0900 Subject: [PATCH 07/12] =?UTF-8?q?test:=20StructuredTemplateService=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StructuredTemplateServiceTest.java | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 backend/src/test/java/reviewme/template/service/StructuredTemplateServiceTest.java diff --git a/backend/src/test/java/reviewme/template/service/StructuredTemplateServiceTest.java b/backend/src/test/java/reviewme/template/service/StructuredTemplateServiceTest.java new file mode 100644 index 000000000..7e1cf2f3d --- /dev/null +++ b/backend/src/test/java/reviewme/template/service/StructuredTemplateServiceTest.java @@ -0,0 +1,128 @@ +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 static reviewme.fixture.OptionGroupFixture.선택지_그룹; +import static reviewme.fixture.OptionItemFixture.선택지; +import static reviewme.fixture.QuestionFixture.서술형_옵션_질문; +import static reviewme.fixture.QuestionFixture.서술형_필수_질문; +import static reviewme.fixture.QuestionFixture.선택형_옵션_질문; +import static reviewme.fixture.QuestionFixture.선택형_필수_질문; +import static reviewme.fixture.SectionFixture.조건부로_보이는_섹션; +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.support.ServiceTest; +import reviewme.template.domain.OptionGroup; +import reviewme.template.domain.OptionItem; +import reviewme.template.domain.Question; +import reviewme.template.domain.Section; +import reviewme.template.domain.Template; +import reviewme.template.domain.structured.StructuredTemplate; +import reviewme.template.repository.OptionGroupRepository; +import reviewme.template.repository.OptionItemRepository; +import reviewme.template.repository.QuestionRepository; +import reviewme.template.repository.SectionRepository; +import reviewme.template.repository.TemplateRepository; +import reviewme.template.service.exception.TemplateNotFoundException; + +@ServiceTest +class StructuredTemplateServiceTest { + + @Autowired + private StructuredTemplateService structuredTemplateService; + + @Autowired + private TemplateRepository templateRepository; + + @Autowired + private SectionRepository sectionRepository; + + @Autowired + private QuestionRepository questionRepository; + + @Autowired + private OptionGroupRepository optionGroupRepository; + + @Autowired + private OptionItemRepository optionItemRepository; + + @Test + void 템플릿_아이디를_통해_구조화된_템플릿을_응답한다() { + // given + Question requiredCheckboxQuestion = questionRepository.save(선택형_필수_질문()); + Question optionalCheckboxQuestion = questionRepository.save(선택형_옵션_질문()); + Question requiredTextQuestion = questionRepository.save(서술형_필수_질문()); + + OptionGroup optionGroup1 = optionGroupRepository.save(선택지_그룹(requiredCheckboxQuestion.getId())); + OptionItem optionItem1 = optionItemRepository.save(선택지(optionGroup1.getId())); + OptionItem optionItem2 = optionItemRepository.save(선택지(optionGroup1.getId())); + + OptionGroup optionGroup2 = optionGroupRepository.save(선택지_그룹(optionalCheckboxQuestion.getId())); + OptionItem optionItem3 = optionItemRepository.save(선택지(optionGroup2.getId())); + OptionItem optionItem4 = optionItemRepository.save(선택지(optionGroup2.getId())); + + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(requiredCheckboxQuestion.getId()))); + Section section2 = sectionRepository.save(조건부로_보이는_섹션( + List.of(requiredTextQuestion.getId(), optionalCheckboxQuestion.getId()), optionItem1.getId())); + + Template template = templateRepository.save(템플릿(List.of(section1.getId(), section2.getId()))); + + // when + StructuredTemplate actual = structuredTemplateService.getStructuredTemplateById(template.getId()); + + // then + assertAll( + () -> assertThat(actual.getTemplateId()).isEqualTo(template.getId()), + () -> assertThat(actual.getSections().getSectionIds()) + .containsExactlyInAnyOrder(section1.getId(), section2.getId()), + () -> assertThat(actual.getQuestions().getQuestionIds()) + .containsExactlyInAnyOrder(requiredCheckboxQuestion.getId(), optionalCheckboxQuestion.getId(), + requiredTextQuestion.getId()), + () -> assertThat(actual.getOptionGroups().getOptionGroupIds()) + .containsExactlyInAnyOrder(optionGroup1.getId(), optionGroup2.getId()), + () -> assertThat(actual.getOptionItems().getOptionItemIds()) + .containsExactlyInAnyOrder(optionItem1.getId(), optionItem2.getId(), optionItem3.getId(), + optionItem4.getId()) + ); + } + + @Test + void 템플릿_아이디를_통해_체크박스_타입의_질문이_없는_구조화된_템플릿을_응답한다() { + // given + Question requiredQuestion = questionRepository.save(서술형_필수_질문()); + Question optionalQuestion = questionRepository.save(서술형_옵션_질문()); + + Section section1 = sectionRepository.save(항상_보이는_섹션(List.of(requiredQuestion.getId()))); + Section section2 = sectionRepository.save(항상_보이는_섹션( + List.of(requiredQuestion.getId(), optionalQuestion.getId()))); + + Template template = templateRepository.save(템플릿(List.of(section1.getId(), section2.getId()))); + + // when + StructuredTemplate actual = structuredTemplateService.getStructuredTemplateById(template.getId()); + + // then + assertAll( + () -> assertThat(actual.getTemplateId()).isEqualTo(template.getId()), + () -> assertThat(actual.getSections().getSectionIds()) + .containsExactlyInAnyOrder(section1.getId(), section2.getId()), + () -> assertThat(actual.getQuestions().getQuestionIds()) + .containsExactlyInAnyOrder(requiredQuestion.getId(), optionalQuestion.getId()) + ); + } + + @Test + void 템플릿_아이디에_해당하는_템플릿이_없을_경우_예외가_발생한다() { + // given + long wrongTemplateId = 1L; + + // when, then + assertThatThrownBy(() -> structuredTemplateService.getStructuredTemplateById(wrongTemplateId)) + .isInstanceOf(TemplateNotFoundException.class); + } +} From 8cc8d2e9a5011bc7031e82d860e545ba811bb814 Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 12:11:17 +0900 Subject: [PATCH 08/12] =?UTF-8?q?test:=20=EB=B3=80=EA=B2=BD=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EA=B8=B0?= =?UTF-8?q?=EC=A1=B4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reviewme/reviewgroup/service/ReviewGroupServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/test/java/reviewme/reviewgroup/service/ReviewGroupServiceTest.java b/backend/src/test/java/reviewme/reviewgroup/service/ReviewGroupServiceTest.java index 6bdc22f44..ee57d3169 100644 --- a/backend/src/test/java/reviewme/reviewgroup/service/ReviewGroupServiceTest.java +++ b/backend/src/test/java/reviewme/reviewgroup/service/ReviewGroupServiceTest.java @@ -46,7 +46,7 @@ class ReviewGroupServiceTest { @Test void 코드가_중복되는_경우_다시_생성한다() { // given - templateRepository.save(템플릿(List.of())); + templateRepository.save(템플릿(List.of(1L))); reviewGroupRepository.save(리뷰_그룹("0000", "1111")); given(randomCodeGenerator.generate(anyInt())) .willReturn("0000") // ReviewRequestCode From 61f0e99b6cd07223e55eb8164d621caa144ae935 Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 15:49:26 +0900 Subject: [PATCH 09/12] =?UTF-8?q?refactor:=20Section=20=EB=A1=9C=EB=94=A9?= =?UTF-8?q?=20=EC=8B=9C,=20FETCH=20JOIN=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/reviewme/template/repository/SectionRepository.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/reviewme/template/repository/SectionRepository.java b/backend/src/main/java/reviewme/template/repository/SectionRepository.java index d40fa5a24..74b7a5d8a 100644 --- a/backend/src/main/java/reviewme/template/repository/SectionRepository.java +++ b/backend/src/main/java/reviewme/template/repository/SectionRepository.java @@ -12,8 +12,8 @@ public interface SectionRepository extends JpaRepository { @Query(""" SELECT s FROM Section s - JOIN TemplateSection ts - ON s.id = ts.sectionId + JOIN FETCH s.questionIds + JOIN TemplateSection ts ON s.id = ts.sectionId WHERE ts.templateId = :templateId ORDER BY s.position ASC """) From f9c07a4d4b3e8375eb8db784749a3e2f312e47d1 Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 15:56:11 +0900 Subject: [PATCH 10/12] =?UTF-8?q?refactor:=20OptionGroups=20getter=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/reviewme/template/domain/structured/OptionGroups.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java b/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java index 8e8c8a679..f8ade9fe1 100644 --- a/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java +++ b/backend/src/main/java/reviewme/template/domain/structured/OptionGroups.java @@ -3,11 +3,9 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import lombok.Getter; import reviewme.template.domain.OptionGroup; import reviewme.template.domain.exception.OptionGroupNotExistException; -@Getter public class OptionGroups { private final List optionGroups; From 9091b48468d5782336cb8020495cb591bc5130c5 Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 15:57:31 +0900 Subject: [PATCH 11/12] =?UTF-8?q?refactor:=20StructuredTemplate=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=B2=B4=EC=9D=B4=EB=8B=9D=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../template/domain/structured/StructuredTemplate.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java b/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java index 0abf6018c..fac5ddfb1 100644 --- a/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java +++ b/backend/src/main/java/reviewme/template/domain/structured/StructuredTemplate.java @@ -22,11 +22,7 @@ public class StructuredTemplate { } StructuredTemplate(Template template, Sections sections, Questions questions) { - this.template = template; - this.sections = sections; - this.questions = questions; - this.optionGroups = null; - this.optionItems = null; + this(template, sections, questions, null, null); } public long getTemplateId() { From 4bf7a00a16d053752f895964a05455014581f268 Mon Sep 17 00:00:00 2001 From: KIMGYUTAE Date: Sun, 24 Nov 2024 16:00:42 +0900 Subject: [PATCH 12/12] =?UTF-8?q?refactor:=20=EC=83=81=EC=88=98=EB=AA=85?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20(INTERNAL=5FSERVER=5FERROR=5FMESSAGE=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StructuredTemplateInvisibleSectionFoundException.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateInvisibleSectionFoundException.java b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateInvisibleSectionFoundException.java index db77b0d3a..7b70a1955 100644 --- a/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateInvisibleSectionFoundException.java +++ b/backend/src/main/java/reviewme/template/domain/exception/StructuredTemplateInvisibleSectionFoundException.java @@ -7,15 +7,15 @@ @Slf4j public class StructuredTemplateInvisibleSectionFoundException extends BadRequestException { - private static final String ERROR_MESSAGE = "서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."; + private static final String INTERNAL_SERVER_ERROR_MESSAGE = "서버 내부에서 문제가 발생했어요. 서버에 문의해주세요."; public StructuredTemplateInvisibleSectionFoundException(List invisibleSectionIds, List optionIds) { - super(ERROR_MESSAGE); + super(INTERNAL_SERVER_ERROR_MESSAGE); logWarning(invisibleSectionIds, optionIds); } public StructuredTemplateInvisibleSectionFoundException(List invisibleSectionIds) { - super(ERROR_MESSAGE); + super(INTERNAL_SERVER_ERROR_MESSAGE); logWarning(invisibleSectionIds, null); }