diff --git a/src/main/java/ai/softeer/caecae/findinggame/service/FindingGameService.java b/src/main/java/ai/softeer/caecae/findinggame/service/FindingGameService.java index 9bad899..f0f0650 100644 --- a/src/main/java/ai/softeer/caecae/findinggame/service/FindingGameService.java +++ b/src/main/java/ai/softeer/caecae/findinggame/service/FindingGameService.java @@ -1,17 +1,14 @@ package ai.softeer.caecae.findinggame.service; import ai.softeer.caecae.findinggame.domain.dto.FindingGameDailyInfo; +import ai.softeer.caecae.findinggame.domain.dto.response.FindingGameInfoResponseDto; import ai.softeer.caecae.findinggame.domain.entity.FindingGame; -import ai.softeer.caecae.findinggame.domain.enums.AnswerType; import ai.softeer.caecae.findinggame.repository.FindGameDbRepository; -import ai.softeer.caecae.racinggame.domain.dto.request.RegisterFindingGamePeriodRequestDto; -import ai.softeer.caecae.findinggame.domain.dto.response.FindingGameInfoResponseDto; -import ai.softeer.caecae.racinggame.domain.dto.response.RegisterFindingGamePeriodResponseDto; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDate; +import java.time.Clock; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -20,54 +17,8 @@ @RequiredArgsConstructor public class FindingGameService { private final FindGameDbRepository findGameDbRepository; - - /** - * 어드민이 숨은캐스퍼찾기 게임 기간을 등록하는 로직 - * - * @param req 게임 시작 날짜 - * @return 게임 시작 날짜, 종료 날짜(+6일) - */ - @Transactional - public RegisterFindingGamePeriodResponseDto registerFindingGamePeriod(RegisterFindingGamePeriodRequestDto req) { - List findingGames = findGameDbRepository.findAll(); - // 등록된 게임 정보가 없으면 생성하기 - if (findingGames.isEmpty()) { - findingGames = initFindingGames(); - } - - // 게임 정보 기간 업데이트 - LocalDate date = req.startDate(); - for (FindingGame findingGame : findingGames) { - findingGame.updateFindingGamePeriod( - date.atTime(15, 15), - date.plusDays(1).atTime(14, 15) - ); - date = date.plusDays(1); - } - - - findGameDbRepository.saveAll(findingGames); - - return RegisterFindingGamePeriodResponseDto.builder() - .startDate(req.startDate()) - .endDate(req.startDate().plusDays(6)) - .build(); - } - - // 7개의 숨은캐스퍼찾기 게임 정보 객체 초기화 - private List initFindingGames() { - List findingGames = new ArrayList<>(); - for (int day = 0; day < 7; day++) { - findingGames.add( - FindingGame.builder() - .questionImageUrl("no-image") - .numberOfWinners(315) - .answerType(AnswerType.UNSELECTED) - .build()); - } - return findingGames; - } - + private final Clock clock; // 테스트코드 의존성 주입을 위한 빈 + /** * 전체 게임 정보와, 최근/다음 게임의 인덱스를 반환하는 로직 * @@ -76,12 +27,11 @@ private List initFindingGames() { @Transactional(readOnly = true) public FindingGameInfoResponseDto getFindingGameInfo() { List findingGames = findGameDbRepository.findAllByOrderByStartTime(); - int recentGameIndex = -1; int nextGameIndex = -1; for (int i = 0; i < findingGames.size(); i++) { FindingGame findingGame = findingGames.get(i); - if (findingGame.getStartTime().isBefore(LocalDateTime.now())) { + if (findingGame.getStartTime().isBefore(LocalDateTime.now(clock))) { recentGameIndex = i; } } @@ -108,4 +58,12 @@ public FindingGameInfoResponseDto getFindingGameInfo() { .nextGameIndex(nextGameIndex) .build(); } + + // 테스트용 + public LocalDateTime testUtcClockInstant() { + findGameDbRepository.findAllByOrderByStartTime(); + return LocalDateTime.now(clock); + } + } + diff --git a/src/main/java/ai/softeer/caecae/global/config/TimeConfig.java b/src/main/java/ai/softeer/caecae/global/config/TimeConfig.java new file mode 100644 index 0000000..5ffbb50 --- /dev/null +++ b/src/main/java/ai/softeer/caecae/global/config/TimeConfig.java @@ -0,0 +1,22 @@ +package ai.softeer.caecae.global.config; + +import jakarta.annotation.PostConstruct; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Clock; +import java.util.TimeZone; + +@Configuration +public class TimeConfig { + + @Bean + public Clock clock() { + return Clock.systemDefaultZone(); + } + + @PostConstruct + void started() { + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); + } +} \ No newline at end of file diff --git a/src/test/java/ai/softeer/caecae/findinggame/service/FindingGameServiceTest.java b/src/test/java/ai/softeer/caecae/findinggame/service/FindingGameServiceTest.java new file mode 100644 index 0000000..c4e6dc6 --- /dev/null +++ b/src/test/java/ai/softeer/caecae/findinggame/service/FindingGameServiceTest.java @@ -0,0 +1,162 @@ +package ai.softeer.caecae.findinggame.service; + +import ai.softeer.caecae.findinggame.domain.dto.response.FindingGameInfoResponseDto; +import ai.softeer.caecae.findinggame.domain.entity.FindingGame; +import ai.softeer.caecae.findinggame.domain.enums.AnswerType; +import ai.softeer.caecae.findinggame.repository.FindGameDbRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Clock; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.List; +import java.util.TimeZone; + +import static org.mockito.BDDMockito.given; + +@ExtendWith(MockitoExtension.class) +class FindingGameServiceTest { + + @InjectMocks + private FindingGameService findingGameService; + + @Mock + private FindGameDbRepository findGameDbRepository; + + @Mock + private Clock clock; + + // 7일치의 숨은그림찾기 게임 정보 + private final List findingGames = new ArrayList<>(); + private static LocalDateTime integrityDateTime; + + + @BeforeAll + static void timeSetUp() { + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); + integrityDateTime = LocalDateTime.of(2024, 5, 5, 10, 10); + } + + @BeforeEach + void setUp() { + // 8/1 ~ 8/7 게임 + LocalDateTime baseTime = LocalDateTime.of(2024, 8, 1, 15, 15); + for (int day = 0; day < 7; day++) { + findingGames.add( + FindingGame.builder() + .id(day + 1) + .questionImageUrl("url") + .startTime(baseTime.plusDays(day)) + .endTime(baseTime.plusDays(day + 1).minusHours(1)) + .numberOfWinners(315) + .answerType(AnswerType.PIXEL) + .build() + ); + } + + Mockito.when(findGameDbRepository.findAllByOrderByStartTime()).thenReturn(findingGames); + + given(clock.getZone()).willReturn(ZoneId.systemDefault()); + } + + @Test + @DisplayName("LocalDateTime과 비교하여, Clock.instant의 시간에 TimeZone의 시차가 적용되는지 테스트 하기 - 같은 시간") + void testUtcClockInstant1() { + // Given + given(clock.instant()).willReturn(Instant.parse("2024-05-05T10:10:00Z")); + + // When + // Clock을 사용하여 LocalDateTime 생성 + LocalDateTime time = findingGameService.testUtcClockInstant(); + + // Then + Assertions.assertThat(time).isNotEqualTo(integrityDateTime); + } + + @Test + @DisplayName("LocalDateTime과 Clock.instant의 시간에 UTC+9의 시차가 적용되는지 테스트 하기 - UTC+9") + void testUtcClockInstant2() { + // Given + given(clock.instant()).willReturn(Instant.parse("2024-05-05T10:10:00Z").minusSeconds(9 * 3600)); + + // When + // Clock을 사용하여 LocalDateTime 생성 + LocalDateTime time = findingGameService.testUtcClockInstant(); + + // Then + Assertions.assertThat(time).isEqualTo(integrityDateTime); + } + + + @Test + @DisplayName("Given_일주일치 게임 정보 입력 When_게임 시작 전 Then_최근게임 인덱스가 -1") + void getFindingGameInfo_1() { + // Given + // LocalDateTime.now()에서 사용할 clock 객체 모킹 + given(clock.instant()).willReturn(Instant.parse("2024-08-01T09:00:00Z").minusSeconds(9 * 3600)); + + // When + FindingGameInfoResponseDto findingGameInfo = findingGameService.getFindingGameInfo(); + + // Then + Assertions.assertThat(findingGameInfo).isNotNull(); + Assertions.assertThat(findingGameInfo.recentGameIndex()).isEqualTo(-1); + Assertions.assertThat(findingGameInfo.nextGameIndex()).isEqualTo(0); + } + + @Test + @DisplayName("Given_일주일치 게임 정보 입력 When_게임 진행 중(첫날) Then_최근게임 인덱스가 0") + void getFindingGameInfo_2() { + // Given + // LocalDateTime.now()에서 사용할 clock 객체 모킹 + given(clock.instant()).willReturn(Instant.parse("2024-08-01T17:00:00Z").minusSeconds(9 * 3600)); + + // When + FindingGameInfoResponseDto findingGameInfo = findingGameService.getFindingGameInfo(); + + // Then + Assertions.assertThat(findingGameInfo).isNotNull(); + Assertions.assertThat(findingGameInfo.recentGameIndex()).isEqualTo(0); + Assertions.assertThat(findingGameInfo.nextGameIndex()).isEqualTo(1); + } + + @Test + @DisplayName("Given_일주일치 게임 정보 입력 When_게임 진행 중(마지막날) Then_최근게임 인덱스가 6") + void getFindingGameInfo_3() { + // Given + // LocalDateTime.now()에서 사용할 clock 객체 모킹 + given(clock.instant()).willReturn(Instant.parse("2024-08-08T10:00:00Z").minusSeconds(9 * 3600)); + + // When + FindingGameInfoResponseDto findingGameInfo = findingGameService.getFindingGameInfo(); + + // Then + Assertions.assertThat(findingGameInfo).isNotNull(); + Assertions.assertThat(findingGameInfo.recentGameIndex()).isEqualTo(6); + Assertions.assertThat(findingGameInfo.nextGameIndex()).isEqualTo(-1); + } + + @Test + @DisplayName("Given_일주일치 게임 정보 입력 When_게임 진행 종료 Then_다음게임 인덱스가 -1") + void getFindingGameInfo_4() { + // Given + // LocalDateTime.now()에서 사용할 clock 객체 모킹 + given(clock.instant()).willReturn(Instant.parse("2032-08-08T16:00:00Z").minusSeconds(9 * 3600)); + + // When + FindingGameInfoResponseDto findingGameInfo = findingGameService.getFindingGameInfo(); + + // Then + Assertions.assertThat(findingGameInfo).isNotNull(); + Assertions.assertThat(findingGameInfo.recentGameIndex()).isEqualTo(6); + Assertions.assertThat(findingGameInfo.nextGameIndex()).isEqualTo(-1); + } +} \ No newline at end of file