diff --git a/src/main/java/com/formssafe/domain/content/question/service/DescriptiveQuestionService.java b/src/main/java/com/formssafe/domain/content/question/service/DescriptiveQuestionService.java index f58dd68..b0de2fa 100644 --- a/src/main/java/com/formssafe/domain/content/question/service/DescriptiveQuestionService.java +++ b/src/main/java/com/formssafe/domain/content/question/service/DescriptiveQuestionService.java @@ -4,13 +4,14 @@ import com.formssafe.domain.content.question.repository.DescriptiveQuestionRepository; import com.formssafe.global.error.ErrorCode; import com.formssafe.global.error.type.BadRequestException; -import java.time.LocalDateTime; -import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.List; + @Service @Transactional(readOnly = true) @RequiredArgsConstructor @@ -20,7 +21,7 @@ public class DescriptiveQuestionService { public DescriptiveQuestion getDescriptiveQuestionByUuid(String id, Long formId) { DescriptiveQuestion descriptiveQuestion = descriptiveQuestionRepository.findByUuidAndFormId(id, formId) - .orElseThrow(() -> new BadRequestException(ErrorCode.SYSTEM_ERROR, "설문에 존재하지 않는 문항입니다.") + .orElseThrow(() -> new BadRequestException(ErrorCode.DESCRIPTIVE_QUESTION_NOT_EXIST, "설문에 존재하지 않는 문항입니다.") ); return descriptiveQuestion; } diff --git a/src/main/java/com/formssafe/domain/content/question/service/ObjectiveQuestionService.java b/src/main/java/com/formssafe/domain/content/question/service/ObjectiveQuestionService.java index 7acce04..2f56861 100644 --- a/src/main/java/com/formssafe/domain/content/question/service/ObjectiveQuestionService.java +++ b/src/main/java/com/formssafe/domain/content/question/service/ObjectiveQuestionService.java @@ -4,13 +4,14 @@ import com.formssafe.domain.content.question.repository.ObjectiveQuestionRepository; import com.formssafe.global.error.ErrorCode; import com.formssafe.global.error.type.BadRequestException; -import java.time.LocalDateTime; -import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.List; + @Service @Transactional(readOnly = true) @RequiredArgsConstructor @@ -20,7 +21,7 @@ public class ObjectiveQuestionService { public ObjectiveQuestion getObjectiveQuestionByUuid(String id, Long formId) { ObjectiveQuestion objectiveQuestion = objectiveQuestionRepository.findByUuidAndFormId(id, formId).orElseThrow( - () -> new BadRequestException(ErrorCode.SYSTEM_ERROR, "설문에 존재하지 않는 문항입니다.") + () -> new BadRequestException(ErrorCode.OBJECTIVE_QUESTION_NOT_EXIST, "설문에 존재하지 않는 문항입니다.") ); return objectiveQuestion; } diff --git a/src/main/java/com/formssafe/domain/file/controller/FileController.java b/src/main/java/com/formssafe/domain/file/controller/FileController.java index ef362e6..3f6ed74 100644 --- a/src/main/java/com/formssafe/domain/file/controller/FileController.java +++ b/src/main/java/com/formssafe/domain/file/controller/FileController.java @@ -14,11 +14,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @Tag(name = "files", description = "s3 presigned-url 받아오기") @RestController @@ -36,6 +32,6 @@ public class FileController { @GetMapping("/upload/{fileName}") @ResponseStatus(HttpStatus.OK) FileResponseDto createPresignedUrl(@PathVariable String fileName, @AuthenticationPrincipal LoginUserDto loginUser) { - return fileService.createPresignedUrl("image", fileName, loginUser); + return fileService.createPresignedUrl("image", fileName); } } diff --git a/src/main/java/com/formssafe/domain/file/service/FileService.java b/src/main/java/com/formssafe/domain/file/service/FileService.java index d1f91ab..05dc328 100644 --- a/src/main/java/com/formssafe/domain/file/service/FileService.java +++ b/src/main/java/com/formssafe/domain/file/service/FileService.java @@ -6,14 +6,14 @@ import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; import com.formssafe.domain.file.dto.FileResponseDto; -import com.formssafe.domain.user.dto.UserRequest.LoginUserDto; -import java.net.URL; -import java.util.Date; -import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.net.URL; +import java.util.Date; +import java.util.UUID; + @Service @RequiredArgsConstructor public class FileService { @@ -23,7 +23,7 @@ public class FileService { private final AmazonS3 amazonS3; - public FileResponseDto createPresignedUrl(String prefix, String fileName, LoginUserDto loginUser) { + public FileResponseDto createPresignedUrl(String prefix, String fileName) { if (!prefix.isEmpty()) { fileName = createPath(prefix, fileName); } diff --git a/src/main/java/com/formssafe/domain/submission/controller/SubmissionController.java b/src/main/java/com/formssafe/domain/submission/controller/SubmissionController.java index 4f7e04a..882dd1c 100644 --- a/src/main/java/com/formssafe/domain/submission/controller/SubmissionController.java +++ b/src/main/java/com/formssafe/domain/submission/controller/SubmissionController.java @@ -16,14 +16,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/v1/forms") @@ -78,9 +71,9 @@ public void modifySubmission(@PathVariable long formId, @RequestBody SubmissionC schema = @Schema(implementation = ErrorResponse.class), examples = @ExampleObject(value = "{\"error\": \"세션이 존재하지 않습니다.\"}"))) @GetMapping("/{formId}/submission") - public ResponseEntity getSumbission(@PathVariable long formId, - @AuthenticationPrincipal LoginUserDto loginUser) { - SubmissionResponseDto submissionResponseDto = submissionService.getSubmission(formId, loginUser); + public ResponseEntity getTempSumbission(@PathVariable long formId, + @AuthenticationPrincipal LoginUserDto loginUser) { + SubmissionResponseDto submissionResponseDto = submissionService.getTempSubmission(formId, loginUser); if (submissionResponseDto == null) { return ResponseEntity.noContent().build(); } diff --git a/src/main/java/com/formssafe/domain/submission/service/SubmissionService.java b/src/main/java/com/formssafe/domain/submission/service/SubmissionService.java index 9a9ffbd..42fd170 100644 --- a/src/main/java/com/formssafe/domain/submission/service/SubmissionService.java +++ b/src/main/java/com/formssafe/domain/submission/service/SubmissionService.java @@ -27,15 +27,16 @@ import com.formssafe.global.error.ErrorCode; import com.formssafe.global.error.type.BadRequestException; import com.formssafe.global.util.DateTimeUtil; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + @Service @Transactional(readOnly = true) @RequiredArgsConstructor @@ -105,7 +106,7 @@ public void modify(long formId, SubmissionCreateDto request, LoginUserDto loginU } } - public SubmissionResponseDto getSubmission(long formId, LoginUserDto loginUser) { + public SubmissionResponseDto getTempSubmission(long formId, LoginUserDto loginUser) { User user = userService.getUserById(loginUser.id()); Submission submission = submissionRepository.findSubmissionByFormIDAndUserId(formId, loginUser.id()) @@ -119,6 +120,10 @@ public SubmissionResponseDto getSubmission(long formId, LoginUserDto loginUser) return SubmissionResponseDto.from(formId, submissionDetailResponseDtos, submission.isTemp()); } + public Submission getSubmission(long formId, LoginUserDto loginUser) { + return submissionRepository.findSubmissionByFormIDAndUserId(formId, loginUser.id()).orElse(null); + } + public List getSubmissionDetailDto(Submission submission) { List submissions = new ArrayList<>(); submissions.addAll(getDescriptiveSubmissionFromSubmission(submission)); @@ -200,6 +205,10 @@ private void createDetailSubmission(List submissionDetailDt } descriptiveSubmissionRepository.saveAll(descriptiveSubmissions); objectiveSubmissionRepository.saveAll(objectiveSubmissions); + + //TODO : 확인 필요 + submission.getDescriptiveSubmissionList().addAll(descriptiveSubmissions); + submission.getObjectiveSubmissionList().addAll(objectiveSubmissions); } private void validate(User user, Form form) { diff --git a/src/main/java/com/formssafe/domain/submission/service/SubmissionValidateService.java b/src/main/java/com/formssafe/domain/submission/service/SubmissionValidateService.java index fe2eccb..a657ad5 100644 --- a/src/main/java/com/formssafe/domain/submission/service/SubmissionValidateService.java +++ b/src/main/java/com/formssafe/domain/submission/service/SubmissionValidateService.java @@ -15,14 +15,14 @@ public class SubmissionValidateService { public void validDescriptiveSubmission(DescriptiveSubmission descriptiveSubmission, DescriptiveQuestionType descriptiveQuestionType) { if (descriptiveQuestionType.displayName().equals("short")) { - if (descriptiveSubmission.getContent().length() > 500) { + if (descriptiveSubmission.getContent().length() > 500 || descriptiveSubmission.getContent().isEmpty()) { throw new BadRequestException(ErrorCode.SHORT_QUESTION_SUBMISSION_CONTENT_OVER_LIMIT, - "short형 질문의 응답은 500자 이내여야 합니다. : " + descriptiveSubmission.getContent()); + "short형 질문의 응답은 1자 이상 500자 이내여야 합니다. : " + descriptiveSubmission.getContent()); } } else if (descriptiveQuestionType.displayName().equals("long")) { - if (descriptiveSubmission.getContent().length() > 5000) { + if (descriptiveSubmission.getContent().length() > 5000 || descriptiveSubmission.getContent().isEmpty()) { throw new BadRequestException(ErrorCode.LONG_QUESTION_SUBMISSION_CONTENT_OVER_LIMIT, - "long형 질문의 응답은 5000자 이내여야 합니다. : " + descriptiveSubmission.getContent()); + "long형 질문의 응답은 1자 이상 5000자 이내여야 합니다. : " + descriptiveSubmission.getContent()); } } } diff --git a/src/main/java/com/formssafe/domain/user/service/UserService.java b/src/main/java/com/formssafe/domain/user/service/UserService.java index c238553..98df80f 100644 --- a/src/main/java/com/formssafe/domain/user/service/UserService.java +++ b/src/main/java/com/formssafe/domain/user/service/UserService.java @@ -31,10 +31,8 @@ public class UserService { private final UserValidateService userValidateService; public User getUserById(Long id) { - User user = userRepository.findById(id) + return userRepository.findById(id) .orElseThrow(() -> new UserNotFoundException(ErrorCode.USER_NOT_FOUND, "존재하지 않는 userId 입니다 : " + id)); - - return user; } @Transactional @@ -43,6 +41,7 @@ public void join(JoinDto request, LoginUserDto loginUser) { if (user.isActive()) { throw new BadRequestException(ErrorCode.USER_ALREADY_JOIN, "이미 회원가입하셨습니다."); } + verifyNickname(request.nickname()); user.updateNickname(request.nickname()); user.activate(); @@ -57,15 +56,18 @@ public UserProfileDto getProfile(LoginUserDto loginUser) { @Transactional public void updateNickname(NicknameUpdateDto request, LoginUserDto loginUser) { String nickname = request.nickname(); - userValidateService.validUserNickname(nickname); + verifyNickname(nickname); User user = getUserById(loginUser.id()); + user.updateNickname(request.nickname()); + } - if (user.getNickname().equals(nickname) || userRepository.existsByNickname(nickname)) { + private void verifyNickname(String nickname) { + userValidateService.validUserNickname(nickname); + + if (userRepository.existsByNickname(nickname)) { throw new BadRequestException(ErrorCode.USER_NICKNAME_DUPLICATE, "중복된 닉네임이 존재합니다."); } - - user.updateNickname(request.nickname()); } @Transactional diff --git a/src/main/java/com/formssafe/global/error/ErrorCode.java b/src/main/java/com/formssafe/global/error/ErrorCode.java index 57ecbb7..072ee22 100644 --- a/src/main/java/com/formssafe/global/error/ErrorCode.java +++ b/src/main/java/com/formssafe/global/error/ErrorCode.java @@ -1,13 +1,10 @@ package com.formssafe.global.error; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.FORBIDDEN; -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; -import static org.springframework.http.HttpStatus.UNAUTHORIZED; - import lombok.Getter; import org.springframework.http.HttpStatus; +import static org.springframework.http.HttpStatus.*; + @Getter public enum ErrorCode { /** @@ -107,7 +104,9 @@ public enum ErrorCode { QUESTION_DTO_CONVERT_ERROR(BAD_REQUEST, "CONTENT002", "Question 엔티티를 DTO로 변환할 수 없습니다."), UNEXPECTED_CONTENT_TYPE(BAD_REQUEST, "CONTENT003", "올바르지 않은 Content type입니다."), INVALID_CONTENT_TITLE_LENGTH(BAD_REQUEST, "CONTENT004", "설문 문항 제목은 1자 이상 100자 이하여야 합니다."), - INVALID_CONTENT_DESCRIPTION_LENGTH(BAD_REQUEST, "CONTENT005", "설문 문항 설명은 1000자 이하여야 합니다."); + INVALID_CONTENT_DESCRIPTION_LENGTH(BAD_REQUEST, "CONTENT005", "설문 문항 설명은 1000자 이하여야 합니다."), + OBJECTIVE_QUESTION_NOT_EXIST(BAD_REQUEST, "CONTENT006", "객관식 설문 문항이 존재하지 않습니다."), + DESCRIPTIVE_QUESTION_NOT_EXIST(BAD_REQUEST, "CONTENT007", "주관식 설문 문항이 존재하지 않습니다."); private final HttpStatus httpStatus; diff --git a/src/test/java/com/formssafe/domain/activity/service/ActivityServiceTest.java b/src/test/java/com/formssafe/domain/activity/service/ActivityServiceTest.java index 456c8b8..12ae3d1 100644 --- a/src/test/java/com/formssafe/domain/activity/service/ActivityServiceTest.java +++ b/src/test/java/com/formssafe/domain/activity/service/ActivityServiceTest.java @@ -1,46 +1,54 @@ package com.formssafe.domain.activity.service; -import static com.formssafe.util.Fixture.createDeletedForm; -import static com.formssafe.util.Fixture.createForm; -import static com.formssafe.util.Fixture.createTemporaryForm; -import static org.assertj.core.api.Assertions.assertThat; - import com.formssafe.config.IntegrationTestConfig; import com.formssafe.domain.activity.dto.ActivityParam.SearchDto; import com.formssafe.domain.activity.dto.ActivityResponse.FormListResponseDto; +import com.formssafe.domain.activity.dto.ActivityResponse.ParticipateSubmissionDto; +import com.formssafe.domain.content.question.entity.*; import com.formssafe.domain.form.entity.Form; -import com.formssafe.domain.form.repository.FormRepository; +import com.formssafe.domain.submission.dto.SubmissionResponse; +import com.formssafe.domain.submission.entity.DescriptiveSubmission; +import com.formssafe.domain.submission.entity.ObjectiveSubmission; +import com.formssafe.domain.submission.entity.Submission; import com.formssafe.domain.user.dto.UserRequest.LoginUserDto; import com.formssafe.domain.user.entity.User; -import com.formssafe.domain.user.repository.UserRepository; +import com.formssafe.global.error.ErrorCode; +import com.formssafe.util.EntityManagerUtil; +import jakarta.persistence.EntityManager; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; + import java.util.ArrayList; import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; +import static com.formssafe.util.Fixture.*; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@DisplayName("[등록한 설문/참여한 설문/참여한 응답 조회]") class ActivityServiceTest extends IntegrationTestConfig { - private final UserRepository userRepository; - private final FormRepository formRepository; private final ActivityService activityService; + private final EntityManager em; private User testUser; private User otherUser; @Autowired - public ActivityServiceTest(UserRepository userRepository, - FormRepository formRepository, - ActivityService activityService) { - this.userRepository = userRepository; - this.formRepository = formRepository; + public ActivityServiceTest(ActivityService activityService, EntityManager em) { this.activityService = activityService; + this.em = em; + } @BeforeEach void setUp() { - testUser = userRepository.findById(1L).orElseThrow(IllegalStateException::new); - otherUser = userRepository.findById(2L).orElseThrow(IllegalStateException::new); + testUser = createUser("activityUser", "activityUser@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + otherUser = em.find(User.class, 2L); } @Nested @@ -56,16 +64,135 @@ class 내가_등록한_설문_전체_조회 { formList.add(createTemporaryForm(testUser, "설문4", "설문설명4")); formList.add(createTemporaryForm(otherUser, "설문5", "설문설명5")); formList.add(createForm(otherUser, "설문6", "설문설명6")); - formRepository.saveAll(formList); + + for (Form form : formList) + em.persist(form); + + EntityManagerUtil.flushAndClearContext(em); LoginUserDto loginUser = new LoginUserDto(testUser.getId()); + //when FormListResponseDto createdFormResponse = activityService.getCreatedFormList(SearchDto.createNull(), loginUser); + //then - assertThat(createdFormResponse.forms()).hasSize(3) - .extracting("title") - .containsExactlyInAnyOrder("설문1", "설문2", "설문4"); + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(createdFormResponse.forms()).hasSize(3) + .extracting("title") + .containsExactlyInAnyOrder("설문1", "설문2", "설문4"); + }); + } + } + + @Nested + class 내가_참여한_설문_전체_조회 { + @Test + void 로그인한_유저가_참여한_설문을_전체_조회한다() { + //given + User submissionUser = createUser("submissionUser", "submissionUser@example.com"); + em.persist(submissionUser); + EntityManagerUtil.flushAndClearContext(em); + + List
formList = new ArrayList<>(); + Form form1 = createForm(testUser, "설문1", "설문설명1"); + Form form2 = createForm(testUser, "설문2", "설문설명2"); + Form form3 = createDeletedForm(testUser, "설문3", "설문설명3"); + Form form4 = createTemporaryForm(testUser, "설문4", "설문설명4"); + Form form5 = createTemporaryForm(otherUser, "설문5", "설문설명5"); + Form form6 = createForm(otherUser, "설문6", "설문설명6"); + + formList.add(form1); + formList.add(form2); + formList.add(form3); + formList.add(form4); + formList.add(form5); + formList.add(form6); + + for (Form form : formList) + em.persist(form); + EntityManagerUtil.flushAndClearContext(em); + + List submissionList = new ArrayList<>(); + submissionList.add(createSubmission(submissionUser, form1)); + submissionList.add(createTempSubmission(submissionUser, form2)); + submissionList.add(createSubmission(submissionUser, form6)); + + for (Submission submission : submissionList) + em.persist(submission); + + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUser = new LoginUserDto(submissionUser.getId()); + + //when + FormListResponseDto createdFormResponse = activityService.getParticipatedFormList(SearchDto.createNull(), + loginUser); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(createdFormResponse.forms()).hasSize(3) + .extracting("title") + .containsExactlyInAnyOrder("설문1", "설문2", "설문6"); + }); + } + } + + @Nested + class 내가_참여한_응답_조회 { + @Test + void 로그인한_사용자가_참여한_설문의_응답을_조회한다() { + //given + Form testForm = createFormWithQuestionCnt(testUser, "설문 제목", "설문 설명", 2); + em.persist(testForm); + + DescriptiveQuestion descriptiveQuestion = createDescriptiveQuestion(testForm, DescriptiveQuestionType.LONG, "주관식 설문 문항1", 1, true); + ObjectiveQuestion objectiveQuestion = createObjectiveQuestion(testForm, ObjectiveQuestionType.SINGLE, "객관식 질문1", 4, List.of(new ObjectiveQuestionOption(1, "보기1"), new ObjectiveQuestionOption(2, "보기2")), true); + + em.persist(descriptiveQuestion); + em.persist(objectiveQuestion); + + Submission submission = createSubmission(testUser, testForm); + ObjectiveSubmission objectiveSubmission = createObjectiveSubmission(submission, "주관식 설문 문항 1", objectiveQuestion); + DescriptiveSubmission descriptiveSubmission = createDescriptiveSubmission(submission, "3", descriptiveQuestion); + + em.persist(submission); + em.persist(objectiveSubmission); + em.persist(descriptiveSubmission); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + + //when + ParticipateSubmissionDto participateSubmissionDto = activityService.getSelfResponse(testForm.getId(), loginUserDto); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(participateSubmissionDto).isNotNull(); + softAssertions.assertThat(participateSubmissionDto.formId()).isEqualTo(testForm.getId()); + softAssertions.assertThat(participateSubmissionDto.responses()).hasSize(2); + + SubmissionResponse.SubmissionDetailResponseDto response1 = participateSubmissionDto.responses().get(0); + softAssertions.assertThat(response1.questionId()).isEqualTo(descriptiveQuestion.getUuid()); + + SubmissionResponse.SubmissionDetailResponseDto response2 = participateSubmissionDto.responses().get(1); + softAssertions.assertThat(response2.questionId()).isEqualTo(objectiveQuestion.getUuid()); + }); + } + + @Test + void 로그인한_사용자가_선택한_설문에_대한_응답이_없는_경우_예외가_발생한다() { + //given + Form testForm = createFormWithQuestionCnt(testUser, "설문 제목", "설문 설명", 2); + em.persist(testForm); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + + //when then + assertThatThrownBy(() -> activityService.getSelfResponse(testForm.getId(), loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.NO_SUBMISSION_PARTICIPATED); } } } \ No newline at end of file diff --git a/src/test/java/com/formssafe/domain/content/service/ContentServiceTest.java b/src/test/java/com/formssafe/domain/content/service/ContentServiceTest.java index 115fcad..037ccb9 100644 --- a/src/test/java/com/formssafe/domain/content/service/ContentServiceTest.java +++ b/src/test/java/com/formssafe/domain/content/service/ContentServiceTest.java @@ -1,81 +1,150 @@ package com.formssafe.domain.content.service; -import static com.formssafe.util.Fixture.createContentCreate; -import static com.formssafe.util.Fixture.createForm; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - import com.formssafe.config.IntegrationTestConfig; +import com.formssafe.domain.content.decoration.entity.Decoration; import com.formssafe.domain.content.dto.ContentRequest.ContentCreateDto; +import com.formssafe.domain.content.question.entity.DescriptiveQuestion; +import com.formssafe.domain.content.question.entity.ObjectiveQuestion; import com.formssafe.domain.form.entity.Form; -import com.formssafe.domain.form.repository.FormRepository; -import com.formssafe.domain.reward.entity.RewardCategory; -import com.formssafe.domain.reward.repository.RewardCategoryRepository; import com.formssafe.domain.user.entity.User; -import com.formssafe.domain.user.repository.UserRepository; import com.formssafe.global.error.ErrorCode; import com.formssafe.global.error.type.BadRequestException; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import com.formssafe.util.EntityManagerUtil; +import jakarta.persistence.EntityManager; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; -@DisplayName("[설문 문항 생성/삭제]") +import java.util.Arrays; +import java.util.List; + +import static com.formssafe.util.Fixture.*; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@DisplayName("[설문 문항 생성]") class ContentServiceTest extends IntegrationTestConfig { - private final UserRepository userRepository; - private final FormRepository formRepository; - private final RewardCategoryRepository rewardCategoryRepository; + private final EntityManager em; private final ContentService contentService; private User testUser; @Autowired - public ContentServiceTest(UserRepository userRepository, - FormRepository formRepository, - RewardCategoryRepository rewardCategoryRepository, + public ContentServiceTest(EntityManager em, ContentService contentService) { - this.userRepository = userRepository; - this.formRepository = formRepository; - this.rewardCategoryRepository = rewardCategoryRepository; + this.em = em; this.contentService = contentService; } @BeforeEach void setUp() { - rewardCategoryRepository.save(RewardCategory.builder().rewardCategoryName("커피").build()); - testUser = userRepository.findById(1L).orElseThrow(IllegalStateException::new); - } + testUser = em.find(User.class, 1L); - @DisplayName("설문 문항 제목이 1자 이상 100자 이하가 아니라면 예외가 발생한다") - @ParameterizedTest - @ValueSource(ints = {0, 101}) - void fail_invalidContentTitleLength(int titleLength) { - //given - Form form = formRepository.save(createForm(testUser, "설문", "설명")); - - String title = "a".repeat(titleLength); - List contents = List.of(createContentCreate("short", title, null, null, false)); - //when then - assertThatThrownBy(() -> contentService.createContents(contents, form)) - .isInstanceOf(BadRequestException.class) - .extracting("errorCode") - .isEqualTo(ErrorCode.INVALID_CONTENT_TITLE_LENGTH); + em.persist(createRewardCategory("커피")); + EntityManagerUtil.flushAndClearContext(em); } - @DisplayName("설문 문항 설명이 2000자 이하가 아니라면 예외가 발생한다") - @Test - void fail_invalidContentDescriptionLength() { - //given - Form form = formRepository.save(createForm(testUser, "설문", "설명")); - - String description = "a".repeat(2001); - List contents = List.of(createContentCreate("short", "title", description, null, false)); - //when then - assertThatThrownBy(() -> contentService.createContents(contents, form)) - .isInstanceOf(BadRequestException.class) - .extracting("errorCode") - .isEqualTo(ErrorCode.INVALID_CONTENT_DESCRIPTION_LENGTH); + @Nested + class 설문_컨텐츠_생성 { + @Test + void 설문_컨텐츠를_등록할_수_있다() { + Form form = createForm(testUser, "설문", "설명"); + em.persist(form); + EntityManagerUtil.flushAndClearContext(em); + + List contentCreateDtos = Arrays.asList( + createContentCreate("single", "객관식 1개 선택", "객관식 질문1 입니다.", 1, List.of("1번문항", "2번문항"), true), + createContentCreate("checkbox", "객관식 체크 박스", "객관식 질문2 입니다.", 2, List.of("1번문항"), false), + createContentCreate("dropdown", "객관식 드롭다운", "객관식 질문3 입니다.", 3, List.of("1번문항"), true), + createContentCreate("short", "서술형 질문1", "주관식 질문1 입니다.", 4, null, true), + createContentCreate("long", "서술형 질문2", "주관식 질문2 입니다.", 5, null, false), + createContentCreate("text", "데코레이션", "데코레이션 1입니다.", 6, null, false) + ); + + //when + contentService.createContents(contentCreateDtos, form); + + //then + Form result = em.find(Form.class, form.getId()); + SoftAssertions.assertSoftly(softAssertions -> { + List decorations = result.getDecorationList().stream() + .map(Decoration::getId) + .toList(); + + List objectiveQuestions = result.getObjectiveQuestionList().stream() + .map(ObjectiveQuestion::getId) + .toList(); + + List descriptiveQuestions = result.getDescriptiveQuestionList().stream() + .map(DescriptiveQuestion::getId) + .toList(); + + softAssertions.assertThat(decorations).hasSize(1); + softAssertions.assertThat(descriptiveQuestions).hasSize(2); + softAssertions.assertThat(objectiveQuestions).hasSize(3); + }); + } + + @Test + void 설문_컨텐츠의_타입이_올바르지_않은_경우_예외가_발생한다() { + Form form = createForm(testUser, "설문", "설명"); + em.persist(form); + EntityManagerUtil.flushAndClearContext(em); + + List contents = List.of(createContentCreate("testType", "title", null, null, false)); + assertThatThrownBy(() -> contentService.createContents(contents, form)) + .extracting("errorCode") + .isEqualTo(ErrorCode.INVALID_OPTION); + } + + @Test + void 객관식_질문의_보기가_존재하지_않을때_예외가_발생한다() { + Form form = createForm(testUser, "설문", "설명"); + em.persist(form); + EntityManagerUtil.flushAndClearContext(em); + + List contents = List.of(createContentCreate("single", "title", null, null, false)); + assertThatThrownBy(() -> contentService.createContents(contents, form)) + .extracting("errorCode") + .isEqualTo(ErrorCode.OBJECTIVE_QUESTION_REQUIRED_AT_LEAST_ONE_OPTION); + } + + @ParameterizedTest + @ValueSource(ints = {0, 101}) + void 설문_문항_제목이_1자_이상_100자_이하가_아니라면_예외가_발생한다(int titleLength) { + //given + Form form = createForm(testUser, "설문", "설명"); + em.persist(form); + EntityManagerUtil.flushAndClearContext(em); + + String title = "a".repeat(titleLength); + List contents = List.of(createContentCreate("short", title, null, null, false)); + //when then + assertThatThrownBy(() -> contentService.createContents(contents, form)) + .isInstanceOf(BadRequestException.class) + .extracting("errorCode") + .isEqualTo(ErrorCode.INVALID_CONTENT_TITLE_LENGTH); + } + + @Test + void 설문_문항_설명이_2000자_이하가_아니라면_예외가_발생한다() { + //given + Form form = createForm(testUser, "설문", "설명"); + em.persist(form); + EntityManagerUtil.flushAndClearContext(em); + + String description = "a".repeat(2001); + List contents = List.of(createContentCreate("short", "title", description, null, false)); + //when then + assertThatThrownBy(() -> contentService.createContents(contents, form)) + .isInstanceOf(BadRequestException.class) + .extracting("errorCode") + .isEqualTo(ErrorCode.INVALID_CONTENT_DESCRIPTION_LENGTH); + } } + + } \ No newline at end of file diff --git a/src/test/java/com/formssafe/domain/file/service/FileServiceTest.java b/src/test/java/com/formssafe/domain/file/service/FileServiceTest.java new file mode 100644 index 0000000..18cea8c --- /dev/null +++ b/src/test/java/com/formssafe/domain/file/service/FileServiceTest.java @@ -0,0 +1,57 @@ +package com.formssafe.domain.file.service; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; +import com.formssafe.config.IntegrationTestConfig; +import com.formssafe.domain.file.dto.FileResponseDto; +import jakarta.persistence.EntityManager; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.net.URL; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@DisplayName("[S3 서비스 사용]") +class FileServiceTest extends IntegrationTestConfig { + private final FileService fileService; + private final EntityManager em; + + @MockBean + private AmazonS3 amazonS3; + + @Autowired + public FileServiceTest(final FileService fileService, final EntityManager em) { + this.fileService = fileService; + this.em = em; + } + + @Test + void 사용자가_파일_이름에_해당하는_저장소에서_PresignedUrl을_받는다() throws Exception { + //given + String prefix = "image"; + String fileName = "test-file.jpg"; + String expectedUrl = "https://example.com/test-file.txt"; + + when(amazonS3.generatePresignedUrl(any(GeneratePresignedUrlRequest.class))) + .thenReturn(new URL(expectedUrl)); + + //when + FileResponseDto fileResponseDto = fileService.createPresignedUrl(prefix, fileName); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + assertThat(fileResponseDto).isNotNull(); + assertThat(fileResponseDto.path()).isEqualTo(expectedUrl); + }); + } +} \ No newline at end of file diff --git a/src/test/java/com/formssafe/domain/submission/service/SubmissionServiceTest.java b/src/test/java/com/formssafe/domain/submission/service/SubmissionServiceTest.java new file mode 100644 index 0000000..637e220 --- /dev/null +++ b/src/test/java/com/formssafe/domain/submission/service/SubmissionServiceTest.java @@ -0,0 +1,543 @@ +package com.formssafe.domain.submission.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.formssafe.config.IntegrationTestConfig; +import com.formssafe.domain.content.decoration.entity.Decoration; +import com.formssafe.domain.content.decoration.entity.DecorationType; +import com.formssafe.domain.content.question.entity.*; +import com.formssafe.domain.form.entity.Form; +import com.formssafe.domain.form.entity.FormStatus; +import com.formssafe.domain.submission.dto.SubmissionRequest.SubmissionCreateDto; +import com.formssafe.domain.submission.dto.SubmissionResponse.SubmissionDetailResponseDto; +import com.formssafe.domain.submission.dto.SubmissionResponse.SubmissionResponseDto; +import com.formssafe.domain.submission.entity.DescriptiveSubmission; +import com.formssafe.domain.submission.entity.Submission; +import com.formssafe.domain.user.dto.UserRequest.LoginUserDto; +import com.formssafe.domain.user.entity.User; +import com.formssafe.global.error.ErrorCode; +import com.formssafe.util.EntityManagerUtil; +import jakarta.persistence.EntityManager; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.formssafe.domain.submission.dto.SubmissionRequest.SubmissionDetailDto; +import static com.formssafe.util.Fixture.*; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@DisplayName("[설문 참여/임시 참여 응답 조회/임시 참여 응답 수정]") +class SubmissionServiceTest extends IntegrationTestConfig { + private static final Logger log = LoggerFactory.getLogger(SubmissionServiceTest.class); + private final EntityManager em; + private final SubmissionService submissionService; + + private User testUser; + private User submissionUser; + private Form testForm; + private DescriptiveQuestion descriptiveQuestion1; + private DescriptiveQuestion descriptiveQuestion2; + private Decoration decoration; + private ObjectiveQuestion objectiveQuestion1; + private ObjectiveQuestion objectiveQuestion2; + private ObjectiveQuestion objectiveQuestion3; + + + @Autowired + public SubmissionServiceTest(final EntityManager em, + final SubmissionService submissionService) { + this.em = em; + this.submissionService = submissionService; + } + + @BeforeEach + void before() { + testUser = em.find(User.class, 1L); + submissionUser = createUser("submissionUser", "submissionUser@example.com"); + em.persist(submissionUser); + + testForm = createFormWithQuestionCnt(testUser, "설문 제목", "설문 설명", 5); + em.persist(testForm); + + descriptiveQuestion1 = createDescriptiveQuestion(testForm, DescriptiveQuestionType.LONG, "주관식 설문 문항1", 1, true); + descriptiveQuestion2 = createDescriptiveQuestion(testForm, DescriptiveQuestionType.SHORT, "주관식 설문 문항2", 2, false); + decoration = createDecoration(testForm, DecorationType.TEXT, "설문 데코레이션1", 3); + objectiveQuestion1 = createObjectiveQuestion(testForm, ObjectiveQuestionType.SINGLE, "객관식 질문1", 4, List.of(new ObjectiveQuestionOption(1, "보기1"), new ObjectiveQuestionOption(2, "보기2")), true); + objectiveQuestion2 = createObjectiveQuestion(testForm, ObjectiveQuestionType.DROPDOWN, "객관식 질문2", 5, List.of(new ObjectiveQuestionOption(1, "보기1")), false); + objectiveQuestion3 = createObjectiveQuestion(testForm, ObjectiveQuestionType.CHECKBOX, "객관식 질문3", 6, List.of(new ObjectiveQuestionOption(1, "보기1"), new ObjectiveQuestionOption(2, "보기2"), new ObjectiveQuestionOption(3, "보기3")), false); + + em.persist(descriptiveQuestion1); + em.persist(descriptiveQuestion2); + em.persist(decoration); + em.persist(objectiveQuestion1); + em.persist(objectiveQuestion2); + em.persist(objectiveQuestion3); + EntityManagerUtil.flushAndClearContext(em); + } + + @Nested + class 설문_참여 { + @Test + void 사용자는_설문에_대한_응답을_작성할_수_있다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto descriptiveSubmission2 = new SubmissionDetailDto("short", descriptiveQuestion2.getUuid(), "주관식 설문 문항2 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + SubmissionDetailDto objectiveSubmission2 = new SubmissionDetailDto("dropdown", objectiveQuestion2.getUuid(), 1); + SubmissionDetailDto objectiveSubmission3 = new SubmissionDetailDto("checkbox", objectiveQuestion3.getUuid(), new int[]{1, 2}); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(descriptiveSubmission2); + submissionDetailDtos.add(objectiveSubmission1); + submissionDetailDtos.add(objectiveSubmission2); + submissionDetailDtos.add(objectiveSubmission3); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, false); + + //when + submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto); + + //then + Submission submission = submissionService.getSubmission(testForm.getId(), loginUserDto); + ObjectMapper objectMapper = new ObjectMapper(); + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(submission).isNotNull(); + softAssertions.assertThat(submission.getForm().getId()).isEqualTo(testForm.getId()); + softAssertions.assertThat(submission.getUser().getId()).isEqualTo(submissionUser.getId()); + softAssertions.assertThat(submission.getDescriptiveSubmissionList()).hasSize(2); + softAssertions.assertThat(submission.getObjectiveSubmissionList()).hasSize(3); + + Map descriptiveAnswers = submission.getDescriptiveSubmissionList().stream() + .collect(Collectors.toMap( + descriptiveSubmission -> descriptiveSubmission.getDescriptiveQuestion().getUuid(), DescriptiveSubmission::getContent + )); + softAssertions.assertThat(descriptiveAnswers.get(descriptiveQuestion1.getUuid())).isEqualTo("주관식 설문 문항1 답변"); + softAssertions.assertThat(descriptiveAnswers.get(descriptiveQuestion2.getUuid())).isEqualTo("주관식 설문 문항2 답변"); + + submission.getObjectiveSubmissionList().forEach(objectiveSubmission -> { + if (objectiveSubmission.getObjectiveQuestion().getQuestionType().displayName().equals("single") || objectiveSubmission.getObjectiveQuestion().getQuestionType().displayName().equals("dropdown")) { + // 객관식 단일 선택 검증 + Integer selectedOption = Integer.parseInt(objectiveSubmission.getContent()); + softAssertions.assertThat(selectedOption).isNotNull(); + } else if (objectiveSubmission.getObjectiveQuestion().getQuestionType().displayName().equals("checkbox")) { + // 체크박스 선택 검증 + try { + List selectedOptions = objectMapper.readValue(objectiveSubmission.getContent(), new TypeReference>() { + }); + softAssertions.assertThat(selectedOptions).hasSize(2); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + }); + }); + } + + @Test + void 사용자는_설문에_대한_임시_응답을_작성할_수_있다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto); + + //then + Submission submission = submissionService.getSubmission(testForm.getId(), loginUserDto); + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(submission).isNotNull(); + softAssertions.assertThat(submission.isTemp()).isTrue(); + softAssertions.assertThat(submission.getForm().getId()).isEqualTo(testForm.getId()); + softAssertions.assertThat(submission.getUser().getId()).isEqualTo(submissionUser.getId()); + }); + } + + @Test + void 사용자가_하나의_설문에_대해_두_개_이상의_응답을_작성할_시_예외가_발생한다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + Submission createSubmission = createSubmission(submissionUser, testForm); + em.persist(createSubmission); + EntityManagerUtil.flushAndClearContext(em); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.ONLY_ONE_SUBMISSION_ALLOWED); + } + + @ParameterizedTest + @EnumSource(value = FormStatus.class, names = {"NOT_STARTED", "DONE", "REWARDED"}) + void 응답하고자_하는_설문의_상태가_진행중이_아닐시_예외가_발생한다(FormStatus status) { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + Form notProgressTestForm = createFormWithStatus(testUser, "설문 제목", "설문 설명", status); + em.persist(notProgressTestForm); + EntityManagerUtil.flushAndClearContext(em); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(notProgressTestForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.FORM_STATUS_NOT_IN_PROGRESS); + } + + @Test + void 사용자가_응답하고자_하는_설문이_자신이_만든_설문일시_예외가_발생한다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.CANNOT_SUBMIT_FORM_YOU_CREATED); + } + + @Test + void 사용자가_응답한_객관식_문항이_존재하지_않는_경우_예외가_발생한다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", "randomUUID", 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.OBJECTIVE_QUESTION_NOT_EXIST); + } + + @ParameterizedTest + @ValueSource(strings = {"short", ""}) + void 사용자가_응답한_객관식_문항의_설문_타입과_응답의_설문_타입이_같지_않는_경우_예외가_발생한다(String questionType) { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto(questionType, descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.SUBMISSION_TYPE_MISMATCH); + } + + @Test + void 사용자가_응답한_주관식_문항이_존재하지_않는_경우_예외가_발생한다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", "randomUUID", "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.DESCRIPTIVE_QUESTION_NOT_EXIST); + } + + @ParameterizedTest + @ValueSource(strings = {"checkbox", "dropdown", ""}) + void 사용자가_응답한_주관식_문항의_설문_타입과_응답의_설문_타입이_같지_않는_경우_예외가_발생한다(String questionType) { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto(questionType, objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.SUBMISSION_TYPE_MISMATCH); + } + + @ParameterizedTest + @ValueSource(ints = {0, 501}) + void 사용자가_응답한_short형_주관식_문항의_응답이_1자_이상_500자_이하가_아니라면_예외가_발생한다(int submissionLength) { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + String content = "a".repeat(submissionLength); + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("short", descriptiveQuestion2.getUuid(), content); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.SHORT_QUESTION_SUBMISSION_CONTENT_OVER_LIMIT); + } + + @ParameterizedTest + @ValueSource(ints = {0, 5001}) + void 사용자가_응답한_long형_주관식_문항의_응답이_1자_이상_5000자_이하가_아니라면_예외가_발생한다(int submissionLength) { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + String content = "a".repeat(submissionLength); + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), content); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.LONG_QUESTION_SUBMISSION_CONTENT_OVER_LIMIT); + } + + @Test + void 사용자가_작성한_응답이_문항의_갯수보다_많을경우_예외가_발생한다() { + //ErrorCode.ENTRY_SUBMITTED_EXCEEDS_QUESTIONS + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto descriptiveSubmission2 = new SubmissionDetailDto("short", descriptiveQuestion2.getUuid(), "주관식 설문 문항2 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + SubmissionDetailDto objectiveSubmission2 = new SubmissionDetailDto("dropdown", objectiveQuestion2.getUuid(), 1); + SubmissionDetailDto objectiveSubmission3 = new SubmissionDetailDto("checkbox", objectiveQuestion3.getUuid(), new int[]{1, 2}); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(descriptiveSubmission2); + submissionDetailDtos.add(objectiveSubmission1); + submissionDetailDtos.add(objectiveSubmission2); + submissionDetailDtos.add(objectiveSubmission3); + submissionDetailDtos.add(objectiveSubmission3); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.ENTRY_SUBMITTED_EXCEEDS_QUESTIONS); + } + + @Test + void 사용자가_작성한_필수_응답의_갯수가_필수_문항의_갯수보다_적을_경우_예외가_발생한다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 문항1 답변"); + + submissionDetailDtos.add(descriptiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + + //when + assertThatThrownBy(() -> submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.REQUIRED_QUESTIONS_UNANSWERED); + } + } + + @Nested + class 임시_참여_응답_조회 { + @Test + void 사용자가_자신의_임시_응답_설문에_대한_조회를_할_수_있다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto); + + //when + SubmissionResponseDto submissionResponseDto = submissionService.getTempSubmission(testForm.getId(), loginUserDto); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(submissionResponseDto).isNotNull(); + softAssertions.assertThat(submissionResponseDto.isTemp()).isTrue(); + softAssertions.assertThat(submissionResponseDto.formId()).isEqualTo(testForm.getId()); + softAssertions.assertThat(submissionResponseDto.responses()).hasSize(2); + + SubmissionDetailResponseDto response1 = submissionResponseDto.responses().get(0); + softAssertions.assertThat(response1.questionId()).isEqualTo(descriptiveQuestion1.getUuid()); + + SubmissionDetailResponseDto response2 = submissionResponseDto.responses().get(1); + softAssertions.assertThat(response2.questionId()).isEqualTo(objectiveQuestion1.getUuid()); + }); + } + + @Test + void 사용자가_저장한_임시_응답이_존재하지_않을_경우_null을_반환한다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + //when + SubmissionResponseDto submissionResponseDto = submissionService.getTempSubmission(testForm.getId(), loginUserDto); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(submissionResponseDto).isNull(); + }); + } + } + + @Nested + class 임시_참여_응답_수정 { + @Test + void 사용자는_임시_응답_설문에_대한_응답을_작성할_수_있다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, true); + submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto); + + //when + submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, false); + submissionService.modify(testForm.getId(), submissionCreateDto, loginUserDto); + + //then + Submission submission = submissionService.getSubmission(testForm.getId(), loginUserDto); + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(submission).isNotNull(); + softAssertions.assertThat(submission.isTemp()).isEqualTo(false); + }); + } + + @Test + void 사용자가_해당_설문에_작성한_임시_응답이_존재하지_않을_시_예외가_발생한다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, false); + + //when then + assertThatThrownBy(() -> submissionService.modify(testForm.getId(), submissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.NO_EXISTING_SUBMISSION_FOUND); + } + + @Test + void 사용자가_해당_설문에_작성한_응답이_최종_응답일_경우_예외가_발생한다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(submissionUser.getId()); + + List submissionDetailDtos = new ArrayList<>(); + SubmissionDetailDto descriptiveSubmission1 = new SubmissionDetailDto("long", descriptiveQuestion1.getUuid(), "주관식 설문 문항1 답변"); + SubmissionDetailDto objectiveSubmission1 = new SubmissionDetailDto("single", objectiveQuestion1.getUuid(), 2); + + submissionDetailDtos.add(descriptiveSubmission1); + submissionDetailDtos.add(objectiveSubmission1); + + SubmissionCreateDto submissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, false); + submissionService.create(testForm.getId(), submissionCreateDto, loginUserDto); + + SubmissionCreateDto modifySubmissionCreateDto = new SubmissionCreateDto(submissionDetailDtos, false); + + //when then + assertThatThrownBy(() -> submissionService.modify(testForm.getId(), modifySubmissionCreateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.NOT_TEMPORARY_SUBMISSION); + + } + } +} \ No newline at end of file diff --git a/src/test/java/com/formssafe/domain/subscribe/service/SubscribeServiceTest.java b/src/test/java/com/formssafe/domain/subscribe/service/SubscribeServiceTest.java new file mode 100644 index 0000000..9708e5b --- /dev/null +++ b/src/test/java/com/formssafe/domain/subscribe/service/SubscribeServiceTest.java @@ -0,0 +1,126 @@ +package com.formssafe.domain.subscribe.service; + +import com.formssafe.config.IntegrationTestConfig; +import com.formssafe.domain.reward.entity.RewardCategory; +import com.formssafe.domain.subscribe.dto.SubscribeRequest.RewardListDto; +import com.formssafe.domain.subscribe.dto.SubscribeResponse.CategoryListDto; +import com.formssafe.domain.subscribe.entity.Subscribe; +import com.formssafe.domain.user.dto.UserRequest.LoginUserDto; +import com.formssafe.domain.user.entity.User; +import com.formssafe.global.error.ErrorCode; +import com.formssafe.util.EntityManagerUtil; +import jakarta.persistence.EntityManager; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Arrays; +import java.util.List; + +import static com.formssafe.util.Fixture.createRewardCategory; +import static com.formssafe.util.Fixture.createSubscribe; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@DisplayName("[경품 카테고리 조회/구독 설정]") +class SubscribeServiceTest extends IntegrationTestConfig { + private final SubscribeService subscribeService; + private final EntityManager em; + + private User testUser; + private RewardCategory rewardCategory; + private Subscribe subscribe; + + @Autowired + public SubscribeServiceTest(final SubscribeService subscribeService, final EntityManager em) { + this.em = em; + this.subscribeService = subscribeService; + } + + @BeforeEach + void before() { + testUser = em.find(User.class, 1L); + + rewardCategory = createRewardCategory("커피"); + em.persist(rewardCategory); + + subscribe = createSubscribe(testUser, rewardCategory); + em.persist(subscribe); + EntityManagerUtil.flushAndClearContext(em); + } + + @Nested + class 경품_카테고리_조회 { + @Test + void 사용자는_자신의_구독_상태를_포함한_경품_카테고리를_조회할_수_있다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + + //when + List categoryList = subscribeService.getRewardCategoryWithSubscribe(loginUserDto); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + List coffeeCategory = categoryList.stream() + .filter(category -> "커피".equals(category.name())) + .toList(); + + List nonCoffeeCategories = categoryList.stream() + .filter(category -> !"커피".equals(category.name())) + .toList(); + + softAssertions.assertThat(coffeeCategory).hasSize(1) + .as("구독한 카테고리는 구독상태가 true로 반환된다") + .isNotEmpty() + .extracting("isSelected") + .containsOnly(true); + + softAssertions.assertThat(nonCoffeeCategories) + .as("구독하지않은 카테고리는 구독상태가 false로 반환된다") + .isNotEmpty() + .extracting("isSelected") // id 필드 추출 + .containsOnly(false); + }); + } + } + + @Nested + class 경품_카테고리_구독_설정 { + @Test + void 사용자는_자신이_원하는_카테고리에_대한_구독_설정을_할_수_있다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + List subscribedCategoryId = Arrays.asList(1L, 2L, 3L); + RewardListDto rewardListDto = new RewardListDto(subscribedCategoryId); + + //when + subscribeService.subscribeCategory(loginUserDto, rewardListDto); + + //then + List categoryList = subscribeService.getRewardCategoryWithSubscribe(loginUserDto); + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(categoryList) + .filteredOn(category -> subscribedCategoryId.contains(category.id())) + .allMatch(CategoryListDto::isSelected); + + softAssertions.assertThat(categoryList) + .filteredOn(category -> !subscribedCategoryId.contains(category.id())) + .noneMatch(CategoryListDto::isSelected); + }); + } + + @Test + void 사용자가_입력한_카테고리명이_존재하지_않을시_예외가_발생한다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + List subscribedCategoryId = Arrays.asList(1L, 7L); + RewardListDto rewardListDto = new RewardListDto(subscribedCategoryId); + + //when then + assertThatThrownBy(() -> subscribeService.subscribeCategory(loginUserDto, rewardListDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.REWARD_CATEGORY_NOT_FOUND); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/formssafe/domain/user/service/UserServiceTest.java b/src/test/java/com/formssafe/domain/user/service/UserServiceTest.java new file mode 100644 index 0000000..627222d --- /dev/null +++ b/src/test/java/com/formssafe/domain/user/service/UserServiceTest.java @@ -0,0 +1,297 @@ +package com.formssafe.domain.user.service; + +import com.formssafe.config.IntegrationTestConfig; +import com.formssafe.domain.form.service.FormService; +import com.formssafe.domain.oauth.OauthServerType; +import com.formssafe.domain.oauth.client.OauthMemberClientComposite; +import com.formssafe.domain.user.dto.UserRequest.JoinDto; +import com.formssafe.domain.user.dto.UserRequest.LoginUserDto; +import com.formssafe.domain.user.dto.UserRequest.NicknameUpdateDto; +import com.formssafe.domain.user.dto.UserResponse.UserProfileDto; +import com.formssafe.domain.user.entity.User; +import com.formssafe.global.error.ErrorCode; +import com.formssafe.util.EntityManagerUtil; +import jakarta.persistence.EntityManager; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; + +import static com.formssafe.util.Fixture.*; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@DisplayName("[사용자 회원가입/사용자 인증/프로필 조회/닉네임 변경/탈퇴]") +class UserServiceTest extends IntegrationTestConfig { + private final UserService userService; + private final EntityManager em; + + @MockBean + private OauthMemberClientComposite oauthMemberClientComposite; + + @MockBean + private FormService formService; + + @Autowired + public UserServiceTest(final UserService userService, final EntityManager em) { + this.userService = userService; + this.em = em; + } + + @Nested + class 사용자_회원가입 { + @Test + void 사이트에_처음_방문한_사용자는_입력한_닉네임으로_회원가입_할_수_있다() { + //given + User testUser = createNotActiveUser("testUser", "testUser@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + JoinDto joinDto = new JoinDto("joinTestUser"); + + //when + userService.join(joinDto, loginUserDto); + User user = em.find(User.class, testUser.getId()); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(user).isNotNull(); + softAssertions.assertThat(user.isActive()).isTrue(); + }); + } + + @Test + void 사이트에_처음_방문하지_않은_사용자는_회원가입_시_예외가_발생한다() { + //given + User testUser = createUser("testUser", "userTest@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + JoinDto joinDto = new JoinDto("joinTestUser"); + + //when then + assertThatThrownBy(() -> userService.join(joinDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.USER_ALREADY_JOIN); + } + + @ParameterizedTest + @ValueSource(ints = {0, 21}) + void 사용자가_입력한_닉네임이_유효하지_않다면_예외가_발생한다(int nicknameLength) { + //given + User testUser = createNotActiveUser("testUser", "userTest@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + String nickname = "a".repeat(nicknameLength); + JoinDto joinDto = new JoinDto(nickname); + + //when then + assertThatThrownBy(() -> userService.join(joinDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.INVALID_USER_NICKNAME); + } + + @Test + void 사용자가_입력한_닉네임과_중복된_닉네임이_존재한다면_예외가_발생한다() { + //given + User testUser = createNotActiveUser("testUser", "userTest@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + JoinDto joinDto = new JoinDto("test"); + + //when then + assertThatThrownBy(() -> userService.join(joinDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.USER_NICKNAME_DUPLICATE); + } + } + + @Nested + class 사용자_인증 { + @Test + void 등록된_사용자는_자신의_정보를_인증할_수_있다() { + //given + User testUser = createUser("getTest", "userTest@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + + //when + User user = userService.getUserById(loginUserDto.id()); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(user).isNotNull(); + softAssertions.assertThat(user.getId()).isEqualTo(testUser.getId()); + softAssertions.assertThat(user.getEmail()).isEqualTo(testUser.getEmail()); + softAssertions.assertThat(user.getNickname()).isEqualTo(testUser.getNickname()); + }); + } + + @Test + void 등록되지_않은_사용자는_자신의_정보_인증시_예외가_발생한다() { + //given + LoginUserDto loginUserDto = new LoginUserDto(3L); + + //when then + assertThatThrownBy(() -> userService.getUserById(loginUserDto.id())) + .extracting("errorCode") + .isEqualTo(ErrorCode.USER_NOT_FOUND); + } + + @Test + void 탈퇴한_사용자는_자신의_정보_인증시_예외가_발생한다() { + //given + User testUser = createDeletedUser("deleteUser1", "delete-oauthId1", "delete_email1@email.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + //given + LoginUserDto loginUserDto = new LoginUserDto(3L); + + //when then + assertThatThrownBy(() -> userService.getUserById(loginUserDto.id())) + .extracting("errorCode") + .isEqualTo(ErrorCode.USER_NOT_FOUND); + } + } + + @Nested + class 사용자_프로필_조회 { + @Test + void 등록된_사용자는_자신의_프로필을_조회할_수_있다() { + //given + User profileTestUser = createUser("profileTestUser", "profileTest@example.com"); + em.persist(profileTestUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(profileTestUser.getId()); + + //when + UserProfileDto assertUser = userService.getProfile(loginUserDto); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(assertUser).isNotNull(); + softAssertions.assertThat(assertUser.email()).isEqualTo("profileTest@example.com"); + softAssertions.assertThat(assertUser.nickname()).isEqualTo("profileTestUser"); + softAssertions.assertThat(assertUser.isActive()).isEqualTo(true); + } + ); + } + } + + @Nested + class 사용자_닉네임_변경 { + @Test + void 등록된_사용자는_자신의_닉네임을_변경할_수_있다() { + //given + User testUser = createUser("testUser", "testUser@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + NicknameUpdateDto nicknameUpdateDto = new NicknameUpdateDto("changeNickname"); + + //when + userService.updateNickname(nicknameUpdateDto, loginUserDto); + User user = em.find(User.class, testUser.getId()); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(user).isNotNull(); + softAssertions.assertThat(user.getNickname()).isEqualTo("changeNickname"); + }); + } + + @ParameterizedTest + @ValueSource(ints = {0, 21}) + void 사용자가_입력한_닉네임이_유효하지_않다면_예외가_발생한다(int nicknameLength) { + //given + User testUser = createUser("testUser", "testUser@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + + String nickname = "a".repeat(nicknameLength); + NicknameUpdateDto nicknameUpdateDto = new NicknameUpdateDto(nickname); + + //when then + assertThatThrownBy(() -> userService.updateNickname(nicknameUpdateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.INVALID_USER_NICKNAME); + } + + @Test + void 사용자가_입력한_닉네임과_중복된_닉네임이_존재한다면_예외가_발생한다() { + //given + User testUser = createUser("testUser", "testUser@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + NicknameUpdateDto nicknameUpdateDto = new NicknameUpdateDto("test"); + + //when then + assertThatThrownBy(() -> userService.updateNickname(nicknameUpdateDto, loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.USER_NICKNAME_DUPLICATE); + } + } + + @Nested + class 사용자_회원_탈퇴 { + @Test + void 등록된_사용자는_회원_탈퇴를_진행할_수_있다() { + //given + User testUser = createUser("testUser", "testUser@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId()); + doNothing().when(oauthMemberClientComposite).deleteAccount(any(OauthServerType.class), anyString()); + doNothing().when(formService).deleteFormByUser(any(User.class)); + + //when + userService.deleteAccount(testUser.getId(), loginUserDto); + User user = em.find(User.class, testUser.getId()); + + //then + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(user).isNotNull(); + softAssertions.assertThat(user.isDeleted()).isTrue(); + }); + } + + @Test + void 입력받은_사용자_ID와_세션내_ID가_다를시_예외가_발생한다() { + //given + User testUser = createUser("testUser", "testUser@example.com"); + em.persist(testUser); + EntityManagerUtil.flushAndClearContext(em); + + LoginUserDto loginUserDto = new LoginUserDto(testUser.getId() + 1); + doNothing().when(oauthMemberClientComposite).deleteAccount(any(OauthServerType.class), anyString()); + doNothing().when(formService).deleteFormByUser(any(User.class)); + + //when then + assertThatThrownBy(() -> userService.deleteAccount(testUser.getId(), loginUserDto)) + .extracting("errorCode") + .isEqualTo(ErrorCode.INVALID_USER); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/formssafe/util/EntityManagerUtil.java b/src/test/java/com/formssafe/util/EntityManagerUtil.java new file mode 100644 index 0000000..15d8488 --- /dev/null +++ b/src/test/java/com/formssafe/util/EntityManagerUtil.java @@ -0,0 +1,14 @@ +package com.formssafe.util; + +import jakarta.persistence.EntityManager; + +public final class EntityManagerUtil { + + private EntityManagerUtil() { + } + + public static void flushAndClearContext(EntityManager entityManager) { + entityManager.flush(); + entityManager.clear(); + } +} \ No newline at end of file diff --git a/src/test/java/com/formssafe/util/Fixture.java b/src/test/java/com/formssafe/util/Fixture.java index 70137c5..84f1b52 100644 --- a/src/test/java/com/formssafe/util/Fixture.java +++ b/src/test/java/com/formssafe/util/Fixture.java @@ -1,11 +1,9 @@ package com.formssafe.util; +import com.formssafe.domain.content.decoration.entity.Decoration; +import com.formssafe.domain.content.decoration.entity.DecorationType; import com.formssafe.domain.content.dto.ContentRequest.ContentCreateDto; -import com.formssafe.domain.content.question.entity.DescriptiveQuestion; -import com.formssafe.domain.content.question.entity.DescriptiveQuestionType; -import com.formssafe.domain.content.question.entity.ObjectiveQuestion; -import com.formssafe.domain.content.question.entity.ObjectiveQuestionOption; -import com.formssafe.domain.content.question.entity.ObjectiveQuestionType; +import com.formssafe.domain.content.question.entity.*; import com.formssafe.domain.form.entity.Form; import com.formssafe.domain.form.entity.FormStatus; import com.formssafe.domain.notification.entity.Notification; @@ -13,12 +11,16 @@ import com.formssafe.domain.oauth.OauthServerType; import com.formssafe.domain.reward.entity.Reward; import com.formssafe.domain.reward.entity.RewardCategory; +import com.formssafe.domain.submission.entity.DescriptiveSubmission; +import com.formssafe.domain.submission.entity.ObjectiveSubmission; import com.formssafe.domain.submission.entity.Submission; +import com.formssafe.domain.subscribe.entity.Subscribe; import com.formssafe.domain.tag.entity.FormTag; import com.formssafe.domain.tag.entity.Tag; import com.formssafe.domain.user.entity.Authority; import com.formssafe.domain.user.entity.OauthId; import com.formssafe.domain.user.entity.User; + import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -58,6 +60,20 @@ public static User createUser(Long id, .build(); } + public static User createUser(String nickname, String email) { + return User.builder() + .oauthId(new OauthId("123", OauthServerType.GOOGLE)) + .nickname(nickname) + .email(email) + .imageUrl( + "https://www.wfla.com/wp-content/uploads/sites/71/2023/05/GettyImages-1389862392.jpg?w=1280&h=720&crop=1") + .authority(Authority.ROLE_USER) + .refreshToken("refreshToken1") + .isActive(true) + .isDeleted(false) + .build(); + } + public static User createUser(String nickname, String oauthId, String email) { @@ -90,6 +106,20 @@ public static User createDeletedUser(String nickname, .build(); } + public static User createNotActiveUser(String nickname, String email) { + return User.builder() + .oauthId(new OauthId("123", OauthServerType.GOOGLE)) + .nickname(nickname) + .email(email) + .imageUrl( + "https://www.wfla.com/wp-content/uploads/sites/71/2023/05/GettyImages-1389862392.jpg?w=1280&h=720&crop=1") + .authority(Authority.ROLE_USER) + .refreshToken("refreshToken1") + .isActive(false) + .isDeleted(false) + .build(); + } + public static List createUsers(int size) { List users = new ArrayList<>(); @@ -252,6 +282,27 @@ public static Form createFormWithImages(User author, .build(); } + public static Form createFormWithQuestionCnt(User author, + String title, + String detail, + int questionCnt) { + return Form.builder() + .user(author) + .title(title) + .detail(detail) + .startDate(LocalDateTime.now()) + .endDate(LocalDateTime.now().plusDays(2)) + .expectTime(10) + .isEmailVisible(false) + .questionCnt(questionCnt) + .privacyDisposalDate(null) + .status(FormStatus.PROGRESS) + .isTemp(false) + .isDeleted(false) + .build(); + } + + public static Tag createTag(String tagName) { return Tag.builder() .tagName(tagName) @@ -300,6 +351,22 @@ public static DescriptiveQuestion createDescriptiveQuestion(Form form, .build(); } + public static DescriptiveQuestion createDescriptiveQuestion(Form form, + DescriptiveQuestionType type, + String title, + int position, + boolean isRequired) { + return DescriptiveQuestion.builder() + .form(form) + .questionType(type) + .title(title) + .detail("주관식 질문 설명") + .position(position) + .isRequired(isRequired) + .isPrivacy(false) + .build(); + } + public static ObjectiveQuestion createObjectiveQuestion(Form form, ObjectiveQuestionType type, String title, @@ -317,9 +384,48 @@ public static ObjectiveQuestion createObjectiveQuestion(Form form, .build(); } + public static ObjectiveQuestion createObjectiveQuestion(Form form, + ObjectiveQuestionType type, + String title, + int position, + List options, + boolean isRequired) { + return ObjectiveQuestion.builder() + .form(form) + .questionType(type) + .title(title) + .detail("객관식 질문 설명") + .position(position) + .questionOption(options) + .isRequired(isRequired) + .isPrivacy(false) + .build(); + } + + public static Decoration createDecoration(Form form, + DecorationType type, + String detail, + int position) { + return Decoration.builder() + .form(form) + .type(type) + .detail(detail) + .position(position) + .build(); + } + + public static ContentCreateDto createContentCreate(String type, + String title, + String description, + List options, + boolean isPrivacy) { + return new ContentCreateDto(type, title, description, options, false, isPrivacy); + } + public static ContentCreateDto createContentCreate(String type, String title, String description, + int position, List options, boolean isPrivacy) { return new ContentCreateDto(type, title, description, options, false, isPrivacy); @@ -352,4 +458,45 @@ public static Notification createNotification(User receiver, .isRead(isRead) .build(); } + + public static Subscribe createSubscribe(User user, + RewardCategory rewardCategory) { + return Subscribe.builder() + .user(user) + .rewardCategory(rewardCategory) + .build(); + } + + public static Submission createSubmission(User user, + Form form) { + return Submission.builder() + .user(user) + .form(form) + .isTemp(false) + .build(); + } + + public static Submission createTempSubmission(User user, Form form) { + return Submission.builder() + .user(user) + .form(form) + .isTemp(true) + .build(); + } + + public static ObjectiveSubmission createObjectiveSubmission(Submission submission, String content, ObjectiveQuestion objectiveQuestion) { + return ObjectiveSubmission.builder() + .submission(submission) + .content(content) + .objectiveQuestion(objectiveQuestion) + .build(); + } + + public static DescriptiveSubmission createDescriptiveSubmission(Submission submission, String content, DescriptiveQuestion descriptiveQuestion) { + return DescriptiveSubmission.builder() + .submission(submission) + .content(content) + .descriptiveQuestion(descriptiveQuestion) + .build(); + } }