diff --git a/src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java b/src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java index bce69bd..7857283 100644 --- a/src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java +++ b/src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java @@ -21,7 +21,9 @@ public enum ErrorCode { EMPTY_RESULT(400, "C012", "조회 결과가 없습니다."), DUPLICATED_STUDENT_NO(400, "C013", "이미 등록된 학번입니다."), SEND_EMAIL_FAIL(400, "C014", "이메일 전송에 실패하였습니다."), - MAIL_TYPE_NOT_VALID(400, "C015", "메일 타입이 올바르지 않습니다.") + MAIL_TYPE_NOT_VALID(400, "C015", "메일 타입이 올바르지 않습니다."), + INVALID_DATETIME_FORMAT(400, "C016", "날짜 형식이 올바르지 않습니다."), + INVALID_TIME_FORMAT(400, "C017", "시간 형식이 올바르지 않습니다.") ; private final int status; diff --git a/src/main/java/dmu/dasom/api/domain/recruit/controller/RecruitController.java b/src/main/java/dmu/dasom/api/domain/recruit/controller/RecruitController.java index f7761c0..82555c8 100644 --- a/src/main/java/dmu/dasom/api/domain/recruit/controller/RecruitController.java +++ b/src/main/java/dmu/dasom/api/domain/recruit/controller/RecruitController.java @@ -3,6 +3,8 @@ import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto; import dmu.dasom.api.domain.applicant.service.ApplicantService; import dmu.dasom.api.domain.common.exception.ErrorResponse; +import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto; +import dmu.dasom.api.domain.recruit.service.RecruitService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; @@ -12,10 +14,9 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequestMapping("/api/recruit") @@ -23,6 +24,7 @@ public class RecruitController { private final ApplicantService applicantService; + private final RecruitService recruitService; // 지원하기 @Operation(summary = "부원 지원하기") @@ -42,4 +44,12 @@ public ResponseEntity apply(@Valid @RequestBody final ApplicantCreateReque return ResponseEntity.ok().build(); } + // 모집 일정 조회 + @Operation(summary = "모집 일정 조회") + @ApiResponse(responseCode = "200", description = "모집 일정 조회 성공") + @GetMapping + public ResponseEntity> getRecruitSchedule() { + return ResponseEntity.ok(recruitService.getRecruitSchedule()); + } + } diff --git a/src/main/java/dmu/dasom/api/domain/recruit/dto/RecruitConfigResponseDto.java b/src/main/java/dmu/dasom/api/domain/recruit/dto/RecruitConfigResponseDto.java new file mode 100644 index 0000000..f26ca97 --- /dev/null +++ b/src/main/java/dmu/dasom/api/domain/recruit/dto/RecruitConfigResponseDto.java @@ -0,0 +1,19 @@ +package dmu.dasom.api.domain.recruit.dto; + +import dmu.dasom.api.domain.recruit.enums.ConfigKey; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@Schema(name = "RecruitConfigResponseDto", description = "모집 설정 응답 DTO") +public class RecruitConfigResponseDto { + + @Schema(description = "모집 설정 키", example = "RECRUITMENT_PERIOD_START") + private ConfigKey key; + + @Schema(description = "모집 설정 값 (날짜 혹은 시간 값)", example = "2024-10-01T10:00:00 || 10:00:00") + private String value; + +} diff --git a/src/main/java/dmu/dasom/api/domain/recruit/dto/RecruitScheduleModifyRequestDto.java b/src/main/java/dmu/dasom/api/domain/recruit/dto/RecruitScheduleModifyRequestDto.java new file mode 100644 index 0000000..e64a7d7 --- /dev/null +++ b/src/main/java/dmu/dasom/api/domain/recruit/dto/RecruitScheduleModifyRequestDto.java @@ -0,0 +1,20 @@ +package dmu.dasom.api.domain.recruit.dto; + +import dmu.dasom.api.domain.recruit.enums.ConfigKey; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; + +@Getter +@Schema(name = "RecruitScheduleModifyRequestDto", description = "모집 일정 수정 요청 DTO") +public class RecruitScheduleModifyRequestDto { + + @NotNull + @Schema(description = "모집 설정 키", example = "key Enum 확인") + ConfigKey key; + + @NotNull + @Schema(description = "모집 설정 값 (날짜 혹은 시간 값)", example = "2024-10-01T10:00:00 || 10:00:00") + String value; + +} diff --git a/src/main/java/dmu/dasom/api/domain/recruit/entity/Recruit.java b/src/main/java/dmu/dasom/api/domain/recruit/entity/Recruit.java new file mode 100644 index 0000000..6d39414 --- /dev/null +++ b/src/main/java/dmu/dasom/api/domain/recruit/entity/Recruit.java @@ -0,0 +1,56 @@ +package dmu.dasom.api.domain.recruit.entity; + +import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto; +import dmu.dasom.api.domain.recruit.enums.ConfigKey; +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.DynamicUpdate; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +@AllArgsConstructor +@Builder +@DynamicUpdate +@Entity +@Getter +@NoArgsConstructor +public class Recruit { + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss"); + + @Enumerated(EnumType.STRING) + @Column(name = "key", length = 64, nullable = false, unique = true) + @Id + private ConfigKey key; + + @Column(name = "value") + private String value; + + public void updateDateTime(final LocalDateTime dateTime) { + this.value = dateTime.format(DATE_TIME_FORMATTER); + } + + public void updateTime(final LocalTime time) { + this.value = time.format(TIME_FORMATTER); + } + + public RecruitConfigResponseDto toResponse() { + LocalDateTime dateTime = LocalDateTime.parse(this.value, DATE_TIME_FORMATTER); + return RecruitConfigResponseDto.builder() + .key(key) + .value(dateTime.format(DATE_TIME_FORMATTER)) + .build(); + } + + public RecruitConfigResponseDto toTimeResponse() { + LocalTime time = LocalTime.parse(this.value, TIME_FORMATTER); + return RecruitConfigResponseDto.builder() + .key(key) + .value(time.format(TIME_FORMATTER)) + .build(); + } + +} diff --git a/src/main/java/dmu/dasom/api/domain/recruit/enums/ConfigKey.java b/src/main/java/dmu/dasom/api/domain/recruit/enums/ConfigKey.java new file mode 100644 index 0000000..5bc3a4e --- /dev/null +++ b/src/main/java/dmu/dasom/api/domain/recruit/enums/ConfigKey.java @@ -0,0 +1,23 @@ +package dmu.dasom.api.domain.recruit.enums; + +public enum ConfigKey { + + // 모집 기간(시작일, 종료일 - 시간 포함) + RECRUITMENT_PERIOD_START, // 모집 시작일 + RECRUITMENT_PERIOD_END, // 모집 종료일 + + // 1차 합격 발표일 (시간 포함) + DOCUMENT_PASS_ANNOUNCEMENT, // 1차 합격 발표일 + + // 면접 기간 (시작일, 종료일) + INTERVIEW_PERIOD_START, // 면접 기간 시작일 + INTERVIEW_PERIOD_END, // 면접 기간 종료일 + + // 면접 시간 (시작 시간, 종료 시간) + INTERVIEW_TIME_START, // 면접 시작 시간 + INTERVIEW_TIME_END, // 면접 종료 시간 + + // 2차 합격 발표일 (시간 포함) + INTERVIEW_PASS_ANNOUNCEMENT // 2차 합격 발표일 + +} diff --git a/src/main/java/dmu/dasom/api/domain/recruit/repository/RecruitRepository.java b/src/main/java/dmu/dasom/api/domain/recruit/repository/RecruitRepository.java new file mode 100644 index 0000000..823a692 --- /dev/null +++ b/src/main/java/dmu/dasom/api/domain/recruit/repository/RecruitRepository.java @@ -0,0 +1,13 @@ +package dmu.dasom.api.domain.recruit.repository; + +import dmu.dasom.api.domain.recruit.entity.Recruit; +import dmu.dasom.api.domain.recruit.enums.ConfigKey; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface RecruitRepository extends JpaRepository { + + Optional findByKey(final ConfigKey key); + +} diff --git a/src/main/java/dmu/dasom/api/domain/recruit/service/RecruitService.java b/src/main/java/dmu/dasom/api/domain/recruit/service/RecruitService.java new file mode 100644 index 0000000..faf7177 --- /dev/null +++ b/src/main/java/dmu/dasom/api/domain/recruit/service/RecruitService.java @@ -0,0 +1,14 @@ +package dmu.dasom.api.domain.recruit.service; + +import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto; +import dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto; + +import java.util.List; + +public interface RecruitService { + + List getRecruitSchedule(); + + void modifyRecruitSchedule(final RecruitScheduleModifyRequestDto requestDto); + +} diff --git a/src/main/java/dmu/dasom/api/domain/recruit/service/RecruitServiceImpl.java b/src/main/java/dmu/dasom/api/domain/recruit/service/RecruitServiceImpl.java new file mode 100644 index 0000000..cff3d4c --- /dev/null +++ b/src/main/java/dmu/dasom/api/domain/recruit/service/RecruitServiceImpl.java @@ -0,0 +1,82 @@ +package dmu.dasom.api.domain.recruit.service; + +import dmu.dasom.api.domain.common.exception.CustomException; +import dmu.dasom.api.domain.common.exception.ErrorCode; +import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto; +import dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto; +import dmu.dasom.api.domain.recruit.entity.Recruit; +import dmu.dasom.api.domain.recruit.enums.ConfigKey; +import dmu.dasom.api.domain.recruit.repository.RecruitRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.List; + +@RequiredArgsConstructor +@Service +@Transactional(readOnly = true) +public class RecruitServiceImpl implements RecruitService { + + private final RecruitRepository recruitRepository; + + // 모집 일정 설정 조회 + @Override + public List getRecruitSchedule() { + return findAll().stream() + .map(config -> config.getKey() == ConfigKey.INTERVIEW_TIME_START || config.getKey() == ConfigKey.INTERVIEW_TIME_END + ? config.toTimeResponse() : config.toResponse()) + .toList(); + } + + // 모집 일정 설정 수정 + @Override + @Transactional + public void modifyRecruitSchedule(final RecruitScheduleModifyRequestDto request) { + final Recruit config = findByKey(request.getKey()); + + if (request.getKey().equals(ConfigKey.INTERVIEW_TIME_START) || request.getKey().equals(ConfigKey.INTERVIEW_TIME_END)) { + final LocalTime time = parseTimeFormat(request.getValue()); + config.updateTime(time); + return; + } + + final LocalDateTime dateTime = parseDateTimeFormat(request.getValue()); + config.updateDateTime(dateTime); + } + + // DB에 저장된 모든 Recruit 객체를 찾아 반환 + private List findAll() { + return recruitRepository.findAll(); + } + + // DB에서 key에 해당하는 Recruit 객체를 찾아 반환 + private Recruit findByKey(final ConfigKey key) { + return recruitRepository.findByKey(key) + .orElseThrow(() -> new CustomException(ErrorCode.ARGUMENT_NOT_VALID)); + + } + + // 시간 형식 변환 및 검증 + private LocalTime parseTimeFormat(String value) { + try { + return LocalTime.parse(value, DateTimeFormatter.ofPattern("HH:mm:ss")); + } catch (DateTimeParseException e) { + throw new CustomException(ErrorCode.INVALID_TIME_FORMAT); + } + } + + // 날짜 및 시간 형식 변환 및 검증 + private LocalDateTime parseDateTimeFormat(String value) { + try { + return LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")); + } catch (DateTimeParseException e) { + throw new CustomException(ErrorCode.INVALID_DATETIME_FORMAT); + } + } + +} diff --git a/src/main/java/dmu/dasom/api/global/admin/controller/AdminController.java b/src/main/java/dmu/dasom/api/global/admin/controller/AdminController.java index c8b5536..d5de689 100644 --- a/src/main/java/dmu/dasom/api/global/admin/controller/AdminController.java +++ b/src/main/java/dmu/dasom/api/global/admin/controller/AdminController.java @@ -4,6 +4,8 @@ import dmu.dasom.api.domain.applicant.dto.ApplicantResponseDto; import dmu.dasom.api.domain.applicant.dto.ApplicantStatusUpdateRequestDto; import dmu.dasom.api.domain.applicant.service.ApplicantService; +import dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto; +import dmu.dasom.api.domain.recruit.service.RecruitService; import dmu.dasom.api.domain.email.enums.MailType; import dmu.dasom.api.global.dto.PageResponse; import io.swagger.v3.oas.annotations.Operation; @@ -26,27 +28,28 @@ public class AdminController { private final ApplicantService applicantService; + private final RecruitService recruitService; // 지원자 조회 @Operation(summary = "지원자 전체 조회") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "지원자 조회 성공"), - @ApiResponse(responseCode = "400", description = "잘못된 요청", - content = @Content( - mediaType = "application/json", - schema = @Schema(implementation = ErrorResponse.class), - examples = { - @ExampleObject( - name = "조회 결과 없음", - value = "{ \"code\": \"C012\", \"message\": \"조회 결과가 없습니다.\" }" - ) - } + @ApiResponse(responseCode = "200", description = "지원자 조회 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorResponse.class), + examples = { + @ExampleObject( + name = "조회 결과 없음", + value = "{ \"code\": \"C012\", \"message\": \"조회 결과가 없습니다.\" }" ) + } ) + ) }) @GetMapping("/applicants") public ResponseEntity> getApplicants( - @RequestParam(value = "page", defaultValue = "0") @Min(0) final int page + @RequestParam(value = "page", defaultValue = "0") @Min(0) final int page ) { return ResponseEntity.ok(applicantService.getApplicants(page)); } @@ -54,19 +57,19 @@ public ResponseEntity> getApplicants( // 지원자 상세 조회 @Operation(summary = "지원자 상세 조회") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "지원자 상세 조회 성공"), - @ApiResponse(responseCode = "400", description = "잘못된 요청", - content = @Content( - mediaType = "application/json", - schema = @Schema(implementation = ErrorResponse.class), - examples = { - @ExampleObject( - name = "조회 결과 없음", - value = "{ \"code\": \"C012\", \"message\": \"조회 결과가 없습니다.\" }" - ) - } + @ApiResponse(responseCode = "200", description = "지원자 상세 조회 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorResponse.class), + examples = { + @ExampleObject( + name = "조회 결과 없음", + value = "{ \"code\": \"C012\", \"message\": \"조회 결과가 없습니다.\" }" ) + } ) + ) }) @GetMapping("/applicants/{id}") public ResponseEntity getApplicant(@PathVariable("id") @Min(0) final Long id) { @@ -75,40 +78,87 @@ public ResponseEntity getApplicant(@PathVariable("i // 지원자 상태 변경 @Operation(summary = "지원자 상태 변경") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "지원자 상태 변경 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorResponse.class), + examples = { + @ExampleObject( + name = "id에 해당하는 지원자 정보 없음", + value = "{ \"code\": \"C012\", \"message\": \"조회 결과가 없습니다.\" }" + ) + } + ) + ) + }) @PatchMapping("/applicants/{id}/status") - public ResponseEntity updateApplicantStatus(@PathVariable("id") @Min(0) final Long id, - @Valid @RequestBody final ApplicantStatusUpdateRequestDto request) { + public ResponseEntity updateApplicantStatus( + @PathVariable("id") @Min(0) final Long id, + @Valid @RequestBody final ApplicantStatusUpdateRequestDto request + ) { return ResponseEntity.ok(applicantService.updateApplicantStatus(id, request)); } + // 모집 일정 수정 + @Operation(summary = "모집 일정 수정") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "모집 일정 수정 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorResponse.class), + examples = { + @ExampleObject( + name = "날짜 형식 오류", + value = "{ \"code\": \"C016\", \"message\": \"날짜 형식이 올바르지 않습니다.\" }" + ), + @ExampleObject( + name = "시간 형식 오류", + value = "{ \"code\": \"C017\", \"message\": \"시간 형식이 올바르지 않습니다.\" }" + ) + } + ) + ) + }) + @PatchMapping("/recruit/schedule") + public ResponseEntity modifyRecruitSchedule(@Valid @RequestBody final RecruitScheduleModifyRequestDto request) { + recruitService.modifyRecruitSchedule(request); + return ResponseEntity.ok().build(); + } + + // 메일 전송 @Operation( - summary = "메일 전송", - description = "지원자들에게 서류 결과 또는 최종 결과 이메일을 발송합니다." + summary = "메일 전송", + description = "지원자들에게 서류 결과 또는 최종 결과 이메일을 발송합니다." ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "메일 전송 성공"), - @ApiResponse(responseCode = "400", description = "잘못된 요청", - content = @Content( - mediaType = "application/json", - schema = @Schema(implementation = ErrorResponse.class), - examples = { - @ExampleObject( - name = "전송 실패", - value = "{ \"code\": \"C014\", \"message\": \"이메일 전송에 실패하였습니다.\" }" - ) - } + @ApiResponse(responseCode = "200", description = "메일 전송 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorResponse.class), + examples = { + @ExampleObject( + name = "전송 실패", + value = "{ \"code\": \"C014\", \"message\": \"이메일 전송에 실패하였습니다.\" }" ) + } ) + ) }) @PostMapping("/applicants/send-email") public ResponseEntity sendEmailsToApplicants( - @RequestParam - @Parameter(description = "메일 발송 타입", examples = { - @ExampleObject(name = "서류 합격 메일", value = "DOCUMENT_PASS"), - @ExampleObject(name = "최종 결과 메일", value = "FINAL_RESULT") - }) MailType mailType) { + @RequestParam + @Parameter(description = "메일 발송 타입", examples = { + @ExampleObject(name = "서류 합격 메일", value = "DOCUMENT_PASS"), + @ExampleObject(name = "최종 결과 메일", value = "FINAL_RESULT") + }) MailType mailType + ) { applicantService.sendEmailsToApplicants(mailType); - return ResponseEntity.ok().build(); + return ResponseEntity.ok() + .build(); } } diff --git a/src/test/java/dmu/dasom/api/domain/recruit/RecruitServiceTest.java b/src/test/java/dmu/dasom/api/domain/recruit/RecruitServiceTest.java new file mode 100644 index 0000000..a7a7326 --- /dev/null +++ b/src/test/java/dmu/dasom/api/domain/recruit/RecruitServiceTest.java @@ -0,0 +1,86 @@ +package dmu.dasom.api.domain.recruit; + +import dmu.dasom.api.domain.common.exception.CustomException; +import dmu.dasom.api.domain.common.exception.ErrorCode; +import dmu.dasom.api.domain.recruit.dto.RecruitConfigResponseDto; +import dmu.dasom.api.domain.recruit.dto.RecruitScheduleModifyRequestDto; +import dmu.dasom.api.domain.recruit.entity.Recruit; +import dmu.dasom.api.domain.recruit.enums.ConfigKey; +import dmu.dasom.api.domain.recruit.repository.RecruitRepository; +import dmu.dasom.api.domain.recruit.service.RecruitServiceImpl; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +class RecruitServiceTest { + + @Mock + private RecruitRepository recruitRepository; + + @InjectMocks + private RecruitServiceImpl recruitService; + + @Test + @DisplayName("모집 일정 조회") + void getRecruitSchedule() { + // given + Recruit recruit1 = mock(Recruit.class); + Recruit recruit2 = mock(Recruit.class); + when(recruitRepository.findAll()).thenReturn(List.of(recruit1, recruit2)); + + // when + List schedule = recruitService.getRecruitSchedule(); + + // then + assertNotNull(schedule); + assertEquals(2, schedule.size()); + verify(recruitRepository, times(1)).findAll(); + } + + @Test + @DisplayName("모집 일정 수정 - 성공") + void modifyRecruitSchedule_success() { + // given + Recruit recruit = mock(Recruit.class); + RecruitScheduleModifyRequestDto request = mock(RecruitScheduleModifyRequestDto.class); + when(request.getKey()).thenReturn(ConfigKey.RECRUITMENT_PERIOD_START); + when(request.getValue()).thenReturn("2025-01-02T12:00:00"); + when(recruitRepository.findByKey(ConfigKey.RECRUITMENT_PERIOD_START)).thenReturn(Optional.of(recruit)); + + // when + recruitService.modifyRecruitSchedule(request); + + // then + verify(recruit, times(1)).updateDateTime(LocalDateTime.of(2025, 1, 2, 12, 0, 0)); + } + + @Test + @DisplayName("모집 일정 수정 - 실패") + void modifyRecruitSchedule_fail() { + // given + RecruitScheduleModifyRequestDto request = mock(RecruitScheduleModifyRequestDto.class); + when(request.getKey()).thenReturn(ConfigKey.INTERVIEW_TIME_START); + when(request.getValue()).thenReturn("12:00"); + when(recruitRepository.findByKey(ConfigKey.INTERVIEW_TIME_START)).thenReturn(Optional.of(mock(Recruit.class))); + + // when + CustomException exception = assertThrows(CustomException.class, () -> { + recruitService.modifyRecruitSchedule(request); + }); + + // then + assertEquals(ErrorCode.INVALID_TIME_FORMAT, exception.getErrorCode()); + } +}