From 46b4b56453ec36a3e244651357946b3ced2fed9b Mon Sep 17 00:00:00 2001 From: jongmee Date: Sun, 1 Sep 2024 13:30:27 +0900 Subject: [PATCH 1/5] =?UTF-8?q?refactor:=20=EB=AA=A8=EC=A7=91=EA=B3=B5?= =?UTF-8?q?=EA=B3=A0=20=EB=A7=88=EA=B0=90=EC=9D=BC=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=EC=9D=84=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EC=97=90=EC=84=9C=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/RecruitmentService.java | 15 +++++++- .../crews/recruitment/domain/Recruitment.java | 16 --------- .../application/RecruitmentServiceTest.java | 24 +++++++++++++ .../recruitment/domain/RecruitmentTest.java | 36 ------------------- 4 files changed, 38 insertions(+), 53 deletions(-) delete mode 100644 src/test/java/com/server/crews/recruitment/domain/RecruitmentTest.java diff --git a/src/main/java/com/server/crews/recruitment/application/RecruitmentService.java b/src/main/java/com/server/crews/recruitment/application/RecruitmentService.java index e7829477..8b40dc71 100644 --- a/src/main/java/com/server/crews/recruitment/application/RecruitmentService.java +++ b/src/main/java/com/server/crews/recruitment/application/RecruitmentService.java @@ -25,6 +25,7 @@ import com.server.crews.recruitment.repository.SelectiveQuestionRepository; import java.time.Clock; import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.List; import java.util.Map; import java.util.Optional; @@ -55,11 +56,22 @@ public RecruitmentDetailsResponse saveRecruitment(Long publisherId, RecruitmentS .orElseThrow(() -> new CrewsException(ErrorCode.USER_NOT_FOUND)); String code = UUID.randomUUID().toString(); Recruitment recruitment = RecruitmentMapper.recruitmentSaveRequestToRecruitment(request, code, publisher); + validateDeadline(recruitment.getDeadline()); Recruitment savedRecruitment = recruitmentRepository.save(recruitment); savedRecruitment.sortQuestions(); return RecruitmentMapper.recruitmentToRecruitmentDetailsResponse(savedRecruitment); } + private void validateDeadline(LocalDateTime deadline) { + LocalDateTime now = LocalDateTime.now(Clock.system(ZoneId.of("Asia/Seoul"))); + if (deadline.isBefore(now)) { + throw new CrewsException(ErrorCode.INVALID_DEADLINE); + } + if (deadline.getMinute() != 0 || deadline.getSecond() != 0 || deadline.getNano() != 0) { + throw new CrewsException(ErrorCode.INVALID_DEADLINE); + } + } + @Transactional public void startRecruiting(Long publisherId) { Recruitment recruitment = recruitmentRepository.findByPublisher(publisherId) @@ -121,7 +133,8 @@ public void updateDeadline(Long publisherId, DeadlineUpdateRequest request) { Recruitment recruitment = recruitmentRepository.findByPublisher(publisherId) .orElseThrow(() -> new CrewsException(ErrorCode.RECRUITMENT_NOT_FOUND)); LocalDateTime deadline = LocalDateTime.parse(request.deadline()); - recruitment.updateDeadline(deadline); + validateDeadline(deadline); + recruitment.updateDeadline(deadline); // Todo: progress 검증, 이전 마감일 이후인지 검증 } @Transactional diff --git a/src/main/java/com/server/crews/recruitment/domain/Recruitment.java b/src/main/java/com/server/crews/recruitment/domain/Recruitment.java index 548f8a99..feb5bb66 100644 --- a/src/main/java/com/server/crews/recruitment/domain/Recruitment.java +++ b/src/main/java/com/server/crews/recruitment/domain/Recruitment.java @@ -1,8 +1,6 @@ package com.server.crews.recruitment.domain; import com.server.crews.auth.domain.Administrator; -import com.server.crews.global.exception.CrewsException; -import com.server.crews.global.exception.ErrorCode; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.ConstraintMode; @@ -20,9 +18,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; -import java.time.Clock; import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.ArrayList; import java.util.List; import lombok.AccessLevel; @@ -77,7 +73,6 @@ public class Recruitment { public Recruitment(Long id, String code, String title, String description, LocalDateTime deadline, Administrator publisher, List
sections) { - validateDeadline(deadline); this.id = id; this.code = code; this.title = title; @@ -89,20 +84,9 @@ public Recruitment(Long id, String code, String title, String description, Local } public void updateDeadline(LocalDateTime deadline) { - validateDeadline(deadline); this.deadline = deadline; } - private void validateDeadline(LocalDateTime deadline) { - LocalDateTime now = LocalDateTime.now(Clock.system(ZoneId.of("Asia/Seoul"))); - if (deadline.isBefore(now)) { - throw new CrewsException(ErrorCode.INVALID_DEADLINE); - } - if (deadline.getMinute() != 0 || deadline.getSecond() != 0 || deadline.getNano() != 0) { - throw new CrewsException(ErrorCode.INVALID_DEADLINE); - } - } - public void addSections(List
sections) { sections.forEach(section -> section.updateRecruitment(this)); this.sections.addAll(sections); diff --git a/src/test/java/com/server/crews/recruitment/application/RecruitmentServiceTest.java b/src/test/java/com/server/crews/recruitment/application/RecruitmentServiceTest.java index ba55dfe4..4172a9c5 100644 --- a/src/test/java/com/server/crews/recruitment/application/RecruitmentServiceTest.java +++ b/src/test/java/com/server/crews/recruitment/application/RecruitmentServiceTest.java @@ -7,6 +7,7 @@ import static com.server.crews.fixture.RecruitmentFixture.DEFAULT_DESCRIPTION; import static com.server.crews.fixture.RecruitmentFixture.DEFAULT_TITLE; import static com.server.crews.fixture.RecruitmentFixture.RECRUITMENT_SAVE_REQUEST; +import static com.server.crews.fixture.RecruitmentFixture.SECTION_REQUESTS; import static com.server.crews.fixture.SectionFixture.BACKEND_SECTION_NAME; import static com.server.crews.fixture.SectionFixture.FRONTEND_SECTION_NAME; import static org.assertj.core.api.Assertions.assertThat; @@ -32,6 +33,11 @@ import com.server.crews.recruitment.dto.response.RecruitmentDetailsResponse; import com.server.crews.recruitment.dto.response.SectionResponse; import com.server.crews.recruitment.repository.RecruitmentRepository; +import java.time.Clock; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -64,6 +70,24 @@ void createRecruitment() { assertThat(response.id()).isNotNull(); } + @Test + @DisplayName("모집 마감일은 지금 이전이 될 수 없다.") + void validateDeadline() { + // given + Administrator publisher = LIKE_LION_ADMIN().administrator(); + + LocalTime time = LocalTime.of(0, 0); + LocalDate date = LocalDate.now(Clock.system(ZoneId.of("Asia/Seoul"))).minusDays(1); + LocalDateTime invalidDeadline = LocalDateTime.of(date, time); + RecruitmentSaveRequest recruitmentSaveRequest = new RecruitmentSaveRequest(null, DEFAULT_TITLE, DEFAULT_DESCRIPTION, SECTION_REQUESTS, + invalidDeadline.toString()); + + // when & then + assertThatThrownBy(() -> recruitmentService.saveRecruitment(publisher.getId(), recruitmentSaveRequest)) + .isInstanceOf(CrewsException.class) + .hasMessage(ErrorCode.INVALID_DEADLINE.getMessage()); + } + @Test @DisplayName("지원서 양식을 수정 저장한다.") void updateRecruitment() { diff --git a/src/test/java/com/server/crews/recruitment/domain/RecruitmentTest.java b/src/test/java/com/server/crews/recruitment/domain/RecruitmentTest.java deleted file mode 100644 index 8e96b9d0..00000000 --- a/src/test/java/com/server/crews/recruitment/domain/RecruitmentTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.server.crews.recruitment.domain; - -import static com.server.crews.fixture.RecruitmentFixture.DEFAULT_CODE; -import static com.server.crews.fixture.RecruitmentFixture.DEFAULT_DESCRIPTION; -import static com.server.crews.fixture.RecruitmentFixture.DEFAULT_TITLE; -import static com.server.crews.fixture.UserFixture.TEST_ADMIN; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.server.crews.global.exception.CrewsException; -import com.server.crews.global.exception.ErrorCode; -import java.time.Clock; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class RecruitmentTest { - - @Test - @DisplayName("모집 마감일은 지금 이전이 될 수 없다.") - void validateDeadline() { - // given - LocalTime time = LocalTime.of(0, 0); - LocalDate date = LocalDate.now(Clock.system(ZoneId.of("Asia/Seoul"))).minusDays(1); - LocalDateTime invalidDeadline = LocalDateTime.of(date, time); - - // when & then - assertThatThrownBy(() -> new Recruitment(null, DEFAULT_CODE, DEFAULT_TITLE, DEFAULT_DESCRIPTION, - invalidDeadline, TEST_ADMIN(), List.of())) - .isInstanceOf(CrewsException.class) - .hasMessage(ErrorCode.INVALID_DEADLINE.getMessage()); - } -} From c52ddbb43f6cf75e24f20fe2149cb9faf8299ca1 Mon Sep 17 00:00:00 2001 From: jongmee Date: Sun, 1 Sep 2024 13:30:46 +0900 Subject: [PATCH 2/5] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/crews/recruitment/domain/NarrativeQuestion.java | 4 ---- .../server/crews/recruitment/domain/SelectiveQuestion.java | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/main/java/com/server/crews/recruitment/domain/NarrativeQuestion.java b/src/main/java/com/server/crews/recruitment/domain/NarrativeQuestion.java index ca1a8c67..53c3c28b 100644 --- a/src/main/java/com/server/crews/recruitment/domain/NarrativeQuestion.java +++ b/src/main/java/com/server/crews/recruitment/domain/NarrativeQuestion.java @@ -56,8 +56,4 @@ public NarrativeQuestion(Long id, String content, Boolean necessity, Integer ord public void updateSection(final Section section) { this.section = section; } - - public void setByExistingId(Long id) { - this.id = id; - } } diff --git a/src/main/java/com/server/crews/recruitment/domain/SelectiveQuestion.java b/src/main/java/com/server/crews/recruitment/domain/SelectiveQuestion.java index c066c11a..9dd37038 100644 --- a/src/main/java/com/server/crews/recruitment/domain/SelectiveQuestion.java +++ b/src/main/java/com/server/crews/recruitment/domain/SelectiveQuestion.java @@ -68,8 +68,4 @@ public SelectiveQuestion(Long id, List choices, String content, Boolean public void updateSection(final Section section) { this.section = section; } - - public void setByExistingId(Long id) { - this.id = id; - } } From e70c5d2bc4fade824146f1d607d38730fbbdd449 Mon Sep 17 00:00:00 2001 From: jongmee Date: Sun, 1 Sep 2024 13:31:17 +0900 Subject: [PATCH 3/5] =?UTF-8?q?test:=20=EA=B0=9C=EB=B0=9C=20=EB=94=94?= =?UTF-8?q?=EB=B2=84=EA=B9=85=EC=9A=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/DatabaseInitializer.java | 96 +++++++++++++++++++ .../java/com/server/crews/api/ApiTest.java | 4 + .../crews/environ/service/ServiceTest.java | 4 + 3 files changed, 104 insertions(+) create mode 100644 src/main/java/com/server/crews/global/config/DatabaseInitializer.java diff --git a/src/main/java/com/server/crews/global/config/DatabaseInitializer.java b/src/main/java/com/server/crews/global/config/DatabaseInitializer.java new file mode 100644 index 00000000..c77bff6f --- /dev/null +++ b/src/main/java/com/server/crews/global/config/DatabaseInitializer.java @@ -0,0 +1,96 @@ +package com.server.crews.global.config; + +import com.server.crews.applicant.domain.Application; +import com.server.crews.applicant.domain.NarrativeAnswer; +import com.server.crews.applicant.domain.SelectiveAnswer; +import com.server.crews.applicant.repository.ApplicationRepository; +import com.server.crews.auth.domain.Administrator; +import com.server.crews.auth.domain.Applicant; +import com.server.crews.auth.repository.AdministratorRepository; +import com.server.crews.auth.repository.ApplicantRepository; +import com.server.crews.recruitment.domain.Choice; +import com.server.crews.recruitment.domain.NarrativeQuestion; +import com.server.crews.recruitment.domain.Recruitment; +import com.server.crews.recruitment.domain.Section; +import com.server.crews.recruitment.domain.SelectiveQuestion; +import com.server.crews.recruitment.repository.RecruitmentRepository; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.context.annotation.Profile; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Profile(value = "!prod") +@Component +@Transactional +@RequiredArgsConstructor +public class DatabaseInitializer implements ApplicationRunner { + private final PasswordEncoder passwordEncoder; + private final AdministratorRepository administratorRepository; + private final RecruitmentRepository recruitmentRepository; + private final ApplicantRepository applicantRepository; + private final ApplicationRepository applicationRepository; + + @Override + public void run(ApplicationArguments args) { + Administrator administrator = new Administrator("admin", passwordEncoder.encode("12341234!")); + administratorRepository.save(administrator); + + NarrativeQuestion introductionQuestion = new NarrativeQuestion(null, "자기소개해주세요", true, 1, 500); + List personalityChoices = List.of(new Choice(null, "성실함"), new Choice(null, "밝음"), + new Choice(null, "꼼꼼함")); + SelectiveQuestion personalityQuestion = new SelectiveQuestion(null, personalityChoices, "장점을 골라주세요", true, 2, 1, + 1); + Section commonSection = new Section(null, "공통", "웹 어플리케이션 서버 개발 파트", List.of(introductionQuestion), + List.of(personalityQuestion)); + + NarrativeQuestion backendNarrativeQuestion = new NarrativeQuestion(null, "백엔드 파트와 관련된 활동 하나를 서술해주세요.", true, 1, + 600); + List backendStackChoices = List.of(new Choice(null, "Django, Python"), + new Choice(null, "Springboot, Java")); + SelectiveQuestion backendStackQuestion = new SelectiveQuestion(null, backendStackChoices, + "멋사 프로젝트에서 사용하고 싶은 스택을 골라주세요", true, 2, 1, 2); + Section backendSection = new Section(null, "백엔드", "웹 어플리케이션 서버 개발 파트", List.of(backendNarrativeQuestion), + List.of(backendStackQuestion)); + + NarrativeQuestion frontendNarrativeQuestion = new NarrativeQuestion(null, "프론트엔드 파트와 관련된 활동 하나를 서술해주세요.", true, + 1, 600); + Section frontendSection = new Section(null, "프론트엔드", "웹 클라이언트 사이드 개발 파트", List.of(frontendNarrativeQuestion), + List.of()); + + Recruitment recruitment = new Recruitment(null, "test-code", "멋쟁이 사자처럼 99기 아기사자 모집", + "멋쟁이 사자처럼 서강대에서 99기 아기사자를 모집합니다!", LocalDateTime.of(2024, 9, 1, 2, 0), administrator, + List.of(commonSection, backendSection, frontendSection)); + recruitment.close(); + recruitmentRepository.save(recruitment); + + Applicant kh = new Applicant("kh@google.com", passwordEncoder.encode("test-password")); + Applicant lkh = new Applicant("lkh@google.com", passwordEncoder.encode("test-password")); + applicantRepository.saveAll(List.of(kh, lkh)); + + NarrativeAnswer skhIntroductionAnswer = new NarrativeAnswer(null, introductionQuestion, "안녕하세요"); + SelectiveAnswer skhPersonalityAnswer = new SelectiveAnswer(null, personalityChoices.get(0), + personalityQuestion); + NarrativeAnswer skhBackendNarrativeAnswer = new NarrativeAnswer(null, backendNarrativeQuestion, + "크루즈 프로젝트 서버 개발을 맡았습니다."); + SelectiveAnswer skhBackendStackAnswer = new SelectiveAnswer(null, backendStackChoices.get(1), + backendStackQuestion); + Application skhApplication = new Application(null, recruitment, kh, "202011414", "컴퓨터공학", "송경호", + List.of(skhIntroductionAnswer, skhBackendNarrativeAnswer), + List.of(skhPersonalityAnswer, skhBackendStackAnswer)); + applicationRepository.save(skhApplication); + + NarrativeAnswer lkhIntroductionAnswer = new NarrativeAnswer(null, introductionQuestion, "반갑습니다"); + SelectiveAnswer lkhPersonalityAnswer = new SelectiveAnswer(null, personalityChoices.get(1), + personalityQuestion); + NarrativeAnswer lkhFrontendNarrativeAnswer = new NarrativeAnswer(null, frontendNarrativeQuestion, + "크루즈 프로젝트 프론트 개발을 맡았습니다."); + Application lkhApplication = new Application(null, recruitment, lkh, "202013232", "컴퓨터공학", "이규호", + List.of(lkhIntroductionAnswer, lkhFrontendNarrativeAnswer), List.of(lkhPersonalityAnswer)); + applicationRepository.save(lkhApplication); + } +} diff --git a/src/test/java/com/server/crews/api/ApiTest.java b/src/test/java/com/server/crews/api/ApiTest.java index f675ec68..7ff1b887 100644 --- a/src/test/java/com/server/crews/api/ApiTest.java +++ b/src/test/java/com/server/crews/api/ApiTest.java @@ -18,6 +18,7 @@ import com.server.crews.auth.presentation.AuthorizationExtractor; import com.server.crews.environ.DatabaseCleaner; import com.server.crews.external.application.EmailService; +import com.server.crews.global.config.DatabaseInitializer; import com.server.crews.recruitment.dto.request.QuestionType; import com.server.crews.recruitment.dto.request.RecruitmentSaveRequest; import com.server.crews.recruitment.dto.response.RecruitmentDetailsResponse; @@ -51,6 +52,9 @@ public abstract class ApiTest { @MockBean private EmailService emailService; + @MockBean + private DatabaseInitializer databaseInitializer; + protected RequestSpecification spec; @BeforeEach diff --git a/src/test/java/com/server/crews/environ/service/ServiceTest.java b/src/test/java/com/server/crews/environ/service/ServiceTest.java index 95ff0cd7..be1edfda 100644 --- a/src/test/java/com/server/crews/environ/service/ServiceTest.java +++ b/src/test/java/com/server/crews/environ/service/ServiceTest.java @@ -9,6 +9,7 @@ import com.server.crews.environ.DatabaseCleaner; import com.server.crews.environ.repository.TestRepository; import com.server.crews.external.application.EmailService; +import com.server.crews.global.config.DatabaseInitializer; import com.server.crews.recruitment.domain.Recruitment; import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; @@ -34,6 +35,9 @@ public abstract class ServiceTest { @MockBean private EmailService emailService; + @MockBean + private DatabaseInitializer databaseInitializer; + @BeforeEach void setUp() { databaseCleaner.clear(); From 669bd2d1190c23741ac66950de998f75a2bcc0b1 Mon Sep 17 00:00:00 2001 From: jongmee Date: Sun, 1 Sep 2024 14:10:16 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20=EB=AA=A8=EC=A7=91=20=EB=A7=88?= =?UTF-8?q?=EA=B0=90=20=EA=B8=B0=ED=95=9C=20=EC=88=98=EC=A0=95=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EC=98=A4=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crews/global/exception/ErrorCode.java | 1 + .../application/RecruitmentService.java | 11 +++--- .../crews/recruitment/domain/Recruitment.java | 8 +++-- .../environ/service/TestRecruitment.java | 6 ++++ .../application/RecruitmentServiceTest.java | 36 ++++++++++++++++++- 5 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/server/crews/global/exception/ErrorCode.java b/src/main/java/com/server/crews/global/exception/ErrorCode.java index ce1cf74f..fa92e2b3 100644 --- a/src/main/java/com/server/crews/global/exception/ErrorCode.java +++ b/src/main/java/com/server/crews/global/exception/ErrorCode.java @@ -14,6 +14,7 @@ public enum ErrorCode { ALREADY_ANNOUNCED(HttpStatus.BAD_REQUEST, "모집 공고 결과 발표가 이미 완료되었습니다."), RECRUITMENT_ALREADY_STARTED(HttpStatus.BAD_REQUEST, "모집이 이미 시작되었습니다."), RECRUITMENT_NOT_STARTED(HttpStatus.BAD_REQUEST, "모집이 시작되지 않았습니다."), + INVALID_MODIFIED_DEADLINE(HttpStatus.BAD_REQUEST, "수정된 모집 마감 기한은 기존 기한 이후이며 모집 진행 중에만 수정할 수 있습니다."), NO_TOKEN(HttpStatus.UNAUTHORIZED, "토큰이 존재하지 않습니다."), INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "토큰 형식이 잘못 되었습니다."), diff --git a/src/main/java/com/server/crews/recruitment/application/RecruitmentService.java b/src/main/java/com/server/crews/recruitment/application/RecruitmentService.java index 8b40dc71..fb884e16 100644 --- a/src/main/java/com/server/crews/recruitment/application/RecruitmentService.java +++ b/src/main/java/com/server/crews/recruitment/application/RecruitmentService.java @@ -132,9 +132,12 @@ public RecruitmentProgressResponse findRecruitmentProgress(Long publisherId) { public void updateDeadline(Long publisherId, DeadlineUpdateRequest request) { Recruitment recruitment = recruitmentRepository.findByPublisher(publisherId) .orElseThrow(() -> new CrewsException(ErrorCode.RECRUITMENT_NOT_FOUND)); - LocalDateTime deadline = LocalDateTime.parse(request.deadline()); - validateDeadline(deadline); - recruitment.updateDeadline(deadline); // Todo: progress 검증, 이전 마감일 이후인지 검증 + + LocalDateTime modifiedDeadline = LocalDateTime.parse(request.deadline()); + if (!recruitment.hasOnOrAfterDeadline(modifiedDeadline) || !recruitment.isInProgress()) { + throw new CrewsException(ErrorCode.INVALID_MODIFIED_DEADLINE); + } + recruitment.updateDeadline(modifiedDeadline); } @Transactional @@ -143,7 +146,7 @@ public void closeRecruitments() { LocalDateTime now = LocalDateTime.now(clock); List recruitments = recruitmentRepository.findAll(); recruitments.stream() - .filter(recruitment -> recruitment.hasPassedDeadline(now)) + .filter(recruitment -> recruitment.hasOnOrAfterDeadline(now)) .forEach(Recruitment::close); } diff --git a/src/main/java/com/server/crews/recruitment/domain/Recruitment.java b/src/main/java/com/server/crews/recruitment/domain/Recruitment.java index feb5bb66..0f886413 100644 --- a/src/main/java/com/server/crews/recruitment/domain/Recruitment.java +++ b/src/main/java/com/server/crews/recruitment/domain/Recruitment.java @@ -112,8 +112,12 @@ public boolean isStarted() { return this.progress != RecruitmentProgress.READY; } - public boolean hasPassedDeadline(LocalDateTime now) { - return now.isAfter(deadline) || now.equals(deadline); + public boolean isInProgress() { + return this.progress == RecruitmentProgress.IN_PROGRESS; + } + + public boolean hasOnOrAfterDeadline(LocalDateTime other) { + return other.isAfter(deadline) || other.equals(deadline); } public void sortQuestions() { diff --git a/src/test/java/com/server/crews/environ/service/TestRecruitment.java b/src/test/java/com/server/crews/environ/service/TestRecruitment.java index 364b3873..b015075e 100644 --- a/src/test/java/com/server/crews/environ/service/TestRecruitment.java +++ b/src/test/java/com/server/crews/environ/service/TestRecruitment.java @@ -60,6 +60,12 @@ private List choicesInSelectiveQuestions(List selecti .toList(); } + public TestRecruitment start() { + this.recruitment.start(); + environ.recruitmentRepository().save(this.recruitment); + return this; + } + public TestRecruitment announce() { this.recruitment.announce(); environ.recruitmentRepository().save(this.recruitment); diff --git a/src/test/java/com/server/crews/recruitment/application/RecruitmentServiceTest.java b/src/test/java/com/server/crews/recruitment/application/RecruitmentServiceTest.java index 4172a9c5..8524b9b9 100644 --- a/src/test/java/com/server/crews/recruitment/application/RecruitmentServiceTest.java +++ b/src/test/java/com/server/crews/recruitment/application/RecruitmentServiceTest.java @@ -24,6 +24,7 @@ import com.server.crews.recruitment.domain.Recruitment; import com.server.crews.recruitment.domain.RecruitmentProgress; import com.server.crews.recruitment.dto.request.ChoiceSaveRequest; +import com.server.crews.recruitment.dto.request.DeadlineUpdateRequest; import com.server.crews.recruitment.dto.request.QuestionSaveRequest; import com.server.crews.recruitment.dto.request.QuestionType; import com.server.crews.recruitment.dto.request.RecruitmentSaveRequest; @@ -79,7 +80,8 @@ void validateDeadline() { LocalTime time = LocalTime.of(0, 0); LocalDate date = LocalDate.now(Clock.system(ZoneId.of("Asia/Seoul"))).minusDays(1); LocalDateTime invalidDeadline = LocalDateTime.of(date, time); - RecruitmentSaveRequest recruitmentSaveRequest = new RecruitmentSaveRequest(null, DEFAULT_TITLE, DEFAULT_DESCRIPTION, SECTION_REQUESTS, + RecruitmentSaveRequest recruitmentSaveRequest = new RecruitmentSaveRequest(null, DEFAULT_TITLE, + DEFAULT_DESCRIPTION, SECTION_REQUESTS, invalidDeadline.toString()); // when & then @@ -186,6 +188,38 @@ void findRecruitmentDetailsInReady() { ); } + @Test + @DisplayName("수정된 모집 마감 기한은 기존 기한 이후이다.") + void validateModifiedDeadline() { + // given + Administrator publisher = LIKE_LION_ADMIN().administrator(); + Recruitment recruitment = LIKE_LION_RECRUITMENT(publisher).start().recruitment(); + + LocalDateTime invalidDeadline = recruitment.getDeadline().minusDays(1); + DeadlineUpdateRequest request = new DeadlineUpdateRequest(invalidDeadline.toString()); + + // when & then + assertThatThrownBy(() -> recruitmentService.updateDeadline(publisher.getId(), request)) + .isInstanceOf(CrewsException.class) + .hasMessage(ErrorCode.INVALID_MODIFIED_DEADLINE.getMessage()); + } + + @Test + @DisplayName("모집 기한 수정은 모집 진행 중에만 할 수 있다.") + void validateRecruitmentProgressWhenUpdateDeadline() { + // given + Administrator publisher = LIKE_LION_ADMIN().administrator(); + Recruitment recruitment = LIKE_LION_RECRUITMENT(publisher).recruitment(); + + LocalDateTime invalidDeadline = recruitment.getDeadline().plusDays(1); + DeadlineUpdateRequest request = new DeadlineUpdateRequest(invalidDeadline.toString()); + + // when & then + assertThatThrownBy(() -> recruitmentService.updateDeadline(publisher.getId(), request)) + .isInstanceOf(CrewsException.class) + .hasMessage(ErrorCode.INVALID_MODIFIED_DEADLINE.getMessage()); + } + @Test @DisplayName("모집 공고 상태를 변경하고 지원 결과 이메일을 전송한다.") void announceRecruitmentOutcome() { From 7b5099bbbbf5f4afca1cf94c4f54e8ba48e4799c Mon Sep 17 00:00:00 2001 From: jongmee Date: Sun, 1 Sep 2024 14:13:38 +0900 Subject: [PATCH 5/5] =?UTF-8?q?test:=20=EC=B6=94=EA=B0=80=EB=90=9C=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EB=AA=A8?= =?UTF-8?q?=EC=A7=91=20=EA=B3=B5=EA=B3=A0=20=EB=A7=88=EA=B0=90=EA=B8=B0?= =?UTF-8?q?=ED=95=9C=20=EC=88=98=EC=A0=95=20api=20test=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/server/crews/api/RecruitmentApiTest.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/server/crews/api/RecruitmentApiTest.java b/src/test/java/com/server/crews/api/RecruitmentApiTest.java index d4ef8ba9..31d4c61b 100644 --- a/src/test/java/com/server/crews/api/RecruitmentApiTest.java +++ b/src/test/java/com/server/crews/api/RecruitmentApiTest.java @@ -34,7 +34,6 @@ import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.LocalTime; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -316,10 +315,15 @@ void updateDeadline() { // given AdminLoginResponse adminTokenResponse = signUpAdmin(TEST_CLUB_NAME, TEST_PASSWORD); String adminAccessToken = adminTokenResponse.accessToken(); - createRecruitment(adminAccessToken); + RecruitmentDetailsResponse recruitmentDetailsResponse = createRecruitment(adminAccessToken); + + RestAssured.given() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.AUTHORIZATION, AuthorizationExtractor.BEARER_TYPE + adminAccessToken) + .when().patch("/recruitments/in-progress"); DeadlineUpdateRequest deadlineUpdateRequest = new DeadlineUpdateRequest( - LocalDateTime.of(2030, 8, 5, 18, 0).toString()); + recruitmentDetailsResponse.deadline().plusDays(1).toString()); // when ExtractableResponse response = RestAssured.given(spec).log().all()