Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ public interface RecruitmentRepository extends JpaRepository<Recruitment, Long>
@Query("SELECT r FROM Recruitment r WHERE r.startDate <= :now AND r.endDate >= :now")
Optional<Recruitment> findActiveRecruitment(LocalDateTime now);

// 진행 중이거나 아직 마감되지 않은 공고 중 가장 임박한 것 (예정 포함)
@Query("SELECT r FROM Recruitment r WHERE r.endDate >= :now ORDER BY r.startDate ASC LIMIT 1")
Optional<Recruitment> findCurrentOrUpcoming(LocalDateTime now);

// 기수로 공고 조회
Optional<Recruitment> findByTerm(Integer term);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,9 @@ private LocalDateTime now() {

// 모집 중 여부 조회
public RecruitmentStatusResponse getRecruitmentStatus() {
Optional<Recruitment> activeRecruitment = recruitmentRepository
.findActiveRecruitment(now());

return activeRecruitment
.map(r -> RecruitmentStatusResponse.of(true, r.getTerm()))
LocalDateTime now = now();
return recruitmentRepository.findCurrentOrUpcoming(now)
.map(r -> RecruitmentStatusResponse.of(r.isActive(now), r.getTerm()))
.orElse(RecruitmentStatusResponse.of(false, null));
}

Expand All @@ -113,9 +111,7 @@ public RecruitmentResponse getRecruitment(Integer term) {
Recruitment recruitment = recruitmentRepository.findByTerm(term)
.orElseThrow(() -> new CustomException(ErrorCode.RECRUITMENT_NOT_FOUND));

boolean isActive = recruitment.isActive(now());

return RecruitmentResponse.from(recruitment, isActive);
return RecruitmentResponse.from(recruitment, now());
}

// 지원서 질문 조회하기
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ private Recruitment createExpiredRecruitment() {
return r;
}

private Recruitment createUpcomingRecruitment() {
Recruitment r = Recruitment.create(28,
LocalDateTime.now().plusDays(14), LocalDateTime.now().plusDays(30),
"[]", null);
ReflectionTestUtils.setField(r, "id", 3L);
return r;
}

private User createUser(Long id) {
User u = User.builder()
.provider("kakao").providerId("test-" + id)
Expand Down Expand Up @@ -197,7 +205,7 @@ class GetRecruitmentStatus {
@DisplayName("TC-001 모집 기간 중 → isActive:true, term 반환")
void active() {
Recruitment r = createActiveRecruitment();
when(recruitmentRepository.findActiveRecruitment(any())).thenReturn(Optional.of(r));
when(recruitmentRepository.findCurrentOrUpcoming(any())).thenReturn(Optional.of(r));

RecruitmentStatusResponse res = recruitmentService.getRecruitmentStatus();

Expand All @@ -206,9 +214,9 @@ void active() {
}

@Test
@DisplayName("TC-002 모집 기간 외 공고만 있을 때 → isActive:false")
@DisplayName("TC-002 마감된 공고만 존재 (endDate 경과) → isActive:false, term:null")
void expiredRecruitment() {
when(recruitmentRepository.findActiveRecruitment(any())).thenReturn(Optional.empty());
when(recruitmentRepository.findCurrentOrUpcoming(any())).thenReturn(Optional.empty());

RecruitmentStatusResponse res = recruitmentService.getRecruitmentStatus();

Expand All @@ -219,7 +227,7 @@ void expiredRecruitment() {
@Test
@DisplayName("TC-003 공고 데이터 없음 → isActive:false (예외 없음)")
void noData() {
when(recruitmentRepository.findActiveRecruitment(any())).thenReturn(Optional.empty());
when(recruitmentRepository.findCurrentOrUpcoming(any())).thenReturn(Optional.empty());

RecruitmentStatusResponse res = recruitmentService.getRecruitmentStatus();

Expand All @@ -229,21 +237,36 @@ void noData() {
@Test
@DisplayName("TC-004 start_date 정각 → isActive:true")
void startDateBoundary() {
LocalDateTime now = LocalDateTime.now();
Recruitment r = Recruitment.create(27, now, now.plusDays(7), "[]", null);
LocalDateTime fixedNow = LocalDateTime.of(2026, 7, 1, 10, 0, 0);
Clock fixed = Clock.fixed(fixedNow.atZone(ZoneId.of("Asia/Seoul")).toInstant(), ZoneId.of("Asia/Seoul"));
ReflectionTestUtils.setField(recruitmentService, "clock", fixed);

Recruitment r = Recruitment.create(27, fixedNow, fixedNow.plusDays(7), "[]", null);
ReflectionTestUtils.setField(r, "id", 1L);
when(recruitmentRepository.findActiveRecruitment(any())).thenReturn(Optional.of(r));
when(recruitmentRepository.findCurrentOrUpcoming(any())).thenReturn(Optional.of(r));

assertThat(recruitmentService.getRecruitmentStatus().getIsActive()).isTrue();
}

@Test
@DisplayName("TC-005 end_date 1초 초과 → isActive:false")
void endDatePast() {
when(recruitmentRepository.findActiveRecruitment(any())).thenReturn(Optional.empty());
when(recruitmentRepository.findCurrentOrUpcoming(any())).thenReturn(Optional.empty());

assertThat(recruitmentService.getRecruitmentStatus().getIsActive()).isFalse();
}

@Test
@DisplayName("TC-006 예정 공고 존재 (start_date 미래) → isActive:false, term 반환")
void upcomingRecruitment() {
Recruitment r = createUpcomingRecruitment();
when(recruitmentRepository.findCurrentOrUpcoming(any())).thenReturn(Optional.of(r));

RecruitmentStatusResponse res = recruitmentService.getRecruitmentStatus();

assertThat(res.getIsActive()).isFalse();
assertThat(res.getTerm()).isEqualTo(28);
}
}

// ══════════════════════════════════════════════
Expand Down Expand Up @@ -286,6 +309,21 @@ void notFound() {
.extracting(e -> ((CustomException) e).getErrorCode())
.isEqualTo(ErrorCode.RECRUITMENT_NOT_FOUND);
}

@Test
@DisplayName("TC-004 예정 공고 조회 → isActive:false + 날짜/일정 필드 반환 (null 아님)")
void upcomingRecruitment() {
Recruitment r = createUpcomingRecruitment();
when(recruitmentRepository.findByTerm(28)).thenReturn(Optional.of(r));

RecruitmentResponse res = recruitmentService.getRecruitment(28);

assertThat(res.getIsActive()).isFalse();
assertThat(res.getTerm()).isEqualTo(28);
assertThat(res.getStartDate()).isNotNull();
assertThat(res.getEndDate()).isNotNull();
assertThat(res.getSchedule()).isNotNull();
}
}

// ══════════════════════════════════════════════
Expand Down
Loading