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 @@ -94,6 +94,14 @@ public void updateApply(boolean isApplied) {
this.isApplied = isApplied;
}

public boolean isValidOn(LocalDate date) {
return !date.isBefore(fromDate) && !date.isAfter(toDate);
}

public boolean hasCoopShops() {
return !coopShops.isEmpty();
}

public void replaceCoopShops(List<CoopShop> coopShops) {
this.coopShops.clear();
this.coopShops.addAll(coopShops);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package in.koreatech.koin.domain.coopshop.repository;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;

import in.koreatech.koin.domain.coopshop.exception.CoopSemesterNotFoundException;
import in.koreatech.koin.domain.coopshop.model.CoopSemester;
Expand All @@ -18,10 +22,10 @@ default CoopSemester getByIsApplied(boolean isApplied) {
.orElseThrow(() -> CoopSemesterNotFoundException.withDetail(""));
}

Optional<CoopSemester> findTopByOrderByToDateDesc();

default CoopSemester getTopByOrderByToDateDesc() {
return findTopByOrderByToDateDesc()
.orElseThrow(() -> CoopSemesterNotFoundException.withDetail(""));
}
@Query("""
SELECT coopSemester FROM CoopSemester coopSemester
WHERE coopSemester.fromDate <= :date AND coopSemester.toDate >= :date
ORDER BY coopSemester.fromDate DESC, coopSemester.id DESC
""")
List<CoopSemester> findAllValidOn(@Param("date") LocalDate date);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package in.koreatech.koin.domain.coopshop.service;

import static in.koreatech.koin.domain.dining.model.DiningType.*;
import static in.koreatech.koin.global.code.ApiResponseCode.NOT_FOUND_COOP_SEMESTER;

import java.time.Clock;
import java.time.LocalDate;
Expand All @@ -14,7 +15,6 @@

import in.koreatech.koin.domain.coopshop.dto.CoopShopResponse;
import in.koreatech.koin.domain.coopshop.dto.CoopShopsResponse;
import in.koreatech.koin.domain.coopshop.exception.CoopSemesterNotFoundException;
import in.koreatech.koin.domain.coopshop.exception.DiningTypeNotFoundException;
import in.koreatech.koin.domain.coopshop.model.CoopName;
import in.koreatech.koin.domain.coopshop.model.CoopOpen;
Expand All @@ -25,6 +25,7 @@
import in.koreatech.koin.domain.coopshop.repository.CoopSemesterRepository;
import in.koreatech.koin.domain.coopshop.repository.CoopShopRepository;
import in.koreatech.koin.domain.dining.model.DiningType;
import in.koreatech.koin.global.exception.CustomException;
import lombok.RequiredArgsConstructor;

@Service
Expand Down Expand Up @@ -98,20 +99,24 @@ public DiningType getDiningType() {
@Transactional
public void updateSemester() {
CoopSemester currentSemester = coopSemesterRepository.getByIsApplied(true);
if (validateSemester(currentSemester)) {
if (validateSemester(currentSemester) && currentSemester.hasCoopShops()) {
return;
}

CoopSemester nextSemester = findNextSemester();
currentSemester.updateApply(false);
CoopSemester nextSemester = coopSemesterRepository.getTopByOrderByToDateDesc();
if (!validateSemester(nextSemester)) {
throw CoopSemesterNotFoundException.withDetail("");
}
nextSemester.updateApply(true);
}

public boolean validateSemester(CoopSemester coopSemester) {
private CoopSemester findNextSemester() {
LocalDate today = LocalDate.now(clock);
return today.isAfter(coopSemester.getFromDate()) && today.isBefore(coopSemester.getToDate());
return coopSemesterRepository.findAllValidOn(today).stream()
.filter(CoopSemester::hasCoopShops)
.findFirst()
.orElseThrow(() -> CustomException.of(NOT_FOUND_COOP_SEMESTER, "date : " + today));
}

public boolean validateSemester(CoopSemester coopSemester) {
return coopSemester.isValidOn(LocalDate.now(clock));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,25 @@ void setUp() {
}
"""));
}

@Test
void 매장_데이터가_없는_미래_학기가_있어도_현재_유효한_학기로_전환된다() throws Exception {
coopShopFixture._23_겨울학기();
coopShopFixture._24_1학기_매장_없음();

coopShopService.updateSemester();

mockMvc.perform(
get("/coopshop")
.contentType(MediaType.ALL.APPLICATION_JSON)
)
.andExpect(status().isOk())
.andExpect(content().json("""
{
"semester": "23-겨울학기",
"from_date": "2023-12-21",
"to_date": "2024-02-28"
}
"""));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ public CoopShopAcceptanceFixture(
return coopSemesterRepository.save(coopSemester);
}

public CoopSemester _24_1학기_매장_없음() {
return coopSemesterRepository.save(
CoopSemester.builder()
.semester("24-1학기")
.fromDate(LocalDate.of(2024, 3, 1))
.toDate(LocalDate.of(2024, 6, 30))
.build()
);
}

public CoopSemester 현재학기() {
var coopSemester = coopSemesterRepository.save(
CoopSemester.builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package in.koreatech.koin.unit.domain.coopshop.service;

import static in.koreatech.koin.global.code.ApiResponseCode.NOT_FOUND_COOP_SEMESTER;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.time.Clock;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import in.koreatech.koin.domain.coopshop.model.CoopSemester;
import in.koreatech.koin.domain.coopshop.model.CoopShop;
import in.koreatech.koin.domain.coopshop.repository.CoopNameRepository;
import in.koreatech.koin.domain.coopshop.repository.CoopSemesterRepository;
import in.koreatech.koin.domain.coopshop.repository.CoopShopRepository;
import in.koreatech.koin.domain.coopshop.service.CoopShopService;
import in.koreatech.koin.global.exception.CustomException;

@ExtendWith(MockitoExtension.class)
@SuppressWarnings("NonAsciiCharacters")
class CoopShopServiceTest {

private static final ZoneId KST = ZoneId.of("Asia/Seoul");
private static final LocalDate TODAY = LocalDate.of(2024, 1, 15);

@Mock
private CoopShopRepository coopShopRepository;

@Mock
private CoopSemesterRepository coopSemesterRepository;

@Mock
private CoopNameRepository coopNameRepository;

private CoopShopService coopShopService;

@BeforeEach
void setUp() {
Clock clock = Clock.fixed(TODAY.atStartOfDay(KST).toInstant(), KST);
coopShopService = new CoopShopService(
clock,
coopShopRepository,
coopSemesterRepository,
coopNameRepository
);
}

@Test
void 현재_학기가_유효하면_학기를_전환하지_않는다() {
CoopSemester currentSemester = 매장이_있는_학기(
"23-겨울학기", LocalDate.of(2023, 12, 21), LocalDate.of(2024, 2, 28)
);
currentSemester.updateApply(true);
when(coopSemesterRepository.getByIsApplied(true)).thenReturn(currentSemester);

coopShopService.updateSemester();

assertThat(currentSemester.isApplied()).isTrue();
verify(coopSemesterRepository, never()).findAllValidOn(any());
}

@Test
void 현재_학기가_날짜는_유효해도_매장이_없으면_다른_학기를_찾는다() {
CoopSemester currentEmptySemester = CoopSemester.of(
"23-겨울학기", LocalDate.of(2023, 12, 21), LocalDate.of(2024, 2, 28)
);
currentEmptySemester.updateApply(true);
CoopSemester overlappingServingSemester = 매장이_있는_학기(
"24-1학기", LocalDate.of(2024, 1, 1), LocalDate.of(2024, 6, 30)
);
when(coopSemesterRepository.getByIsApplied(true)).thenReturn(currentEmptySemester);
when(coopSemesterRepository.findAllValidOn(TODAY))
.thenReturn(List.of(overlappingServingSemester, currentEmptySemester));

coopShopService.updateSemester();

assertThat(currentEmptySemester.isApplied()).isFalse();
assertThat(overlappingServingSemester.isApplied()).isTrue();
}

@Test
void 현재_학기가_날짜는_유효해도_매장이_없고_대체할_학기도_없으면_예외가_발생한다() {
CoopSemester currentEmptySemester = CoopSemester.of(
"23-겨울학기", LocalDate.of(2023, 12, 21), LocalDate.of(2024, 2, 28)
);
currentEmptySemester.updateApply(true);
when(coopSemesterRepository.getByIsApplied(true)).thenReturn(currentEmptySemester);
when(coopSemesterRepository.findAllValidOn(TODAY)).thenReturn(List.of(currentEmptySemester));

assertThatThrownBy(() -> coopShopService.updateSemester())
.isInstanceOf(CustomException.class)
.hasMessage(NOT_FOUND_COOP_SEMESTER.getMessage());
assertThat(currentEmptySemester.isApplied()).isTrue();
}

@Test
void 만료된_경우_오늘이_포함되고_매장이_있는_학기로_전환한다() {
CoopSemester expiredSemester = 매장이_있는_학기(
"23-2학기", LocalDate.of(2023, 9, 2), LocalDate.of(2023, 12, 20)
);
expiredSemester.updateApply(true);
CoopSemester emptySemester = CoopSemester.of(
"24-1학기", LocalDate.of(2024, 1, 1), LocalDate.of(2024, 6, 30)
);
CoopSemester servingSemester = 매장이_있는_학기(
"23-겨울학기", LocalDate.of(2023, 12, 21), LocalDate.of(2024, 2, 28)
);
when(coopSemesterRepository.getByIsApplied(true)).thenReturn(expiredSemester);
when(coopSemesterRepository.findAllValidOn(TODAY)).thenReturn(List.of(emptySemester, servingSemester));

coopShopService.updateSemester();

assertThat(expiredSemester.isApplied()).isFalse();
assertThat(emptySemester.isApplied()).isFalse();
assertThat(servingSemester.isApplied()).isTrue();
}

@Test
void 오늘이_포함되고_매장이_있는_학기가_없으면_예외가_발생한다() {
CoopSemester expiredSemester = 매장이_있는_학기(
"23-2학기", LocalDate.of(2023, 9, 2), LocalDate.of(2023, 12, 20)
);
expiredSemester.updateApply(true);
CoopSemester emptySemester = CoopSemester.of(
"24-1학기", LocalDate.of(2024, 1, 1), LocalDate.of(2024, 6, 30)
);
when(coopSemesterRepository.getByIsApplied(true)).thenReturn(expiredSemester);
when(coopSemesterRepository.findAllValidOn(TODAY)).thenReturn(List.of(emptySemester));

assertThatThrownBy(() -> coopShopService.updateSemester())
.isInstanceOf(CustomException.class)
.hasMessage(NOT_FOUND_COOP_SEMESTER.getMessage());
assertThat(expiredSemester.isApplied()).isTrue();
}

@Test
void 학기_시작일과_종료일_당일도_유효한_학기로_판단한다() {
CoopSemester startsToday = CoopSemester.of("24-1학기", TODAY, LocalDate.of(2024, 6, 30));
CoopSemester endsToday = CoopSemester.of("23-겨울학기", LocalDate.of(2023, 12, 21), TODAY);
CoopSemester endedYesterday = CoopSemester.of(
"23-2학기", LocalDate.of(2023, 9, 2), TODAY.minusDays(1)
);

assertThat(coopShopService.validateSemester(startsToday)).isTrue();
assertThat(coopShopService.validateSemester(endsToday)).isTrue();
assertThat(coopShopService.validateSemester(endedYesterday)).isFalse();
}

private CoopSemester 매장이_있는_학기(String semester, LocalDate fromDate, LocalDate toDate) {
CoopSemester coopSemester = CoopSemester.of(semester, fromDate, toDate);
coopSemester.replaceCoopShops(List.of(
CoopShop.builder()
.location("학생회관 1층")
.phone("041-000-0000")
.build()
));
return coopSemester;
}
}
Loading