From 65cd018389d2b563c41abdf8ba84901fbc1fefe0 Mon Sep 17 00:00:00 2001 From: BGuga Date: Thu, 9 Nov 2023 16:55:50 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20specification=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/FestivalSpecification.java | 30 +++++++++++++++++++ .../repository/FestivalRepository.java | 3 +- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/com/festago/festival/domain/FestivalSpecification.java diff --git a/backend/src/main/java/com/festago/festival/domain/FestivalSpecification.java b/backend/src/main/java/com/festago/festival/domain/FestivalSpecification.java new file mode 100644 index 000000000..1b26d30ff --- /dev/null +++ b/backend/src/main/java/com/festago/festival/domain/FestivalSpecification.java @@ -0,0 +1,30 @@ +package com.festago.festival.domain; + +import java.time.LocalDate; +import org.springframework.data.jpa.domain.Specification; + +public class FestivalSpecification { + + private FestivalSpecification() { + } + + public static Specification all() { + return (root, query, criteriaBuilder) -> criteriaBuilder.conjunction(); + } + + public static Specification afterStartDate(LocalDate currentTime) { + return (root, query, criteriaBuilder) -> criteriaBuilder.lessThan(root.get("startDate"), currentTime); + } + + public static Specification beforeStartDate(LocalDate currentTime) { + return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get("startDate"), currentTime); + } + + public static Specification afterEndDate(LocalDate currentTime) { + return (root, query, criteriaBuilder) -> criteriaBuilder.lessThan(root.get("endDate"), currentTime); + } + + public static Specification beforeEndDate(LocalDate currentTime) { + return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get("endDate"), currentTime); + } +} diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalRepository.java b/backend/src/main/java/com/festago/festival/repository/FestivalRepository.java index 10b3c02c6..032b89a68 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalRepository.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalRepository.java @@ -2,7 +2,8 @@ import com.festago.festival.domain.Festival; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -public interface FestivalRepository extends JpaRepository { +public interface FestivalRepository extends JpaRepository, JpaSpecificationExecutor { } From a6d4d8e6972b22a92399ef40a09cf8358ac6b269 Mon Sep 17 00:00:00 2001 From: BGuga Date: Thu, 9 Nov 2023 17:00:56 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20FestivalFilter=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../festago/common/exception/ErrorCode.java | 1 + .../festival/domain/FestivalFilter.java | 39 +++++++++++++++++++ .../festival/domain/FestivalFilterTest.java | 20 ++++++++++ 3 files changed, 60 insertions(+) create mode 100644 backend/src/main/java/com/festago/festival/domain/FestivalFilter.java create mode 100644 backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java diff --git a/backend/src/main/java/com/festago/common/exception/ErrorCode.java b/backend/src/main/java/com/festago/common/exception/ErrorCode.java index 9c57b4cab..7e47f1bd8 100644 --- a/backend/src/main/java/com/festago/common/exception/ErrorCode.java +++ b/backend/src/main/java/com/festago/common/exception/ErrorCode.java @@ -29,6 +29,7 @@ public enum ErrorCode { DELETE_CONSTRAINT_STAGE("티켓이 등록된 공연은 삭제할 수 없습니다."), DELETE_CONSTRAINT_SCHOOL("학생 또는 축제에 등록된 학교는 삭제할 수 없습니다."), DUPLICATE_SCHOOL("이미 존재하는 학교 정보입니다."), + INVALID_FESTIVAL_FILTER("유효하지 않은 필터 값입니다."), // 401 diff --git a/backend/src/main/java/com/festago/festival/domain/FestivalFilter.java b/backend/src/main/java/com/festago/festival/domain/FestivalFilter.java new file mode 100644 index 000000000..8eabacc5c --- /dev/null +++ b/backend/src/main/java/com/festago/festival/domain/FestivalFilter.java @@ -0,0 +1,39 @@ +package com.festago.festival.domain; + +import static com.festago.festival.domain.FestivalSpecification.afterEndDate; +import static com.festago.festival.domain.FestivalSpecification.afterStartDate; +import static com.festago.festival.domain.FestivalSpecification.all; +import static com.festago.festival.domain.FestivalSpecification.beforeEndDate; +import static com.festago.festival.domain.FestivalSpecification.beforeStartDate; + +import com.festago.common.exception.BadRequestException; +import com.festago.common.exception.ErrorCode; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.function.Function; +import org.springframework.data.jpa.domain.Specification; + +public enum FestivalFilter { + ALL((currentTime) -> all()), + PROGRESS((currentTime) -> afterStartDate(currentTime) + .and(beforeEndDate(currentTime))), + PLANNED((currentTime) -> beforeStartDate(currentTime)), + END((currentTime) -> afterEndDate(currentTime)); + + private final Function> filter; + + FestivalFilter(Function> filter) { + this.filter = filter; + } + + public static FestivalFilter from(String filterName) { + return Arrays.stream(FestivalFilter.values()) + .filter(festivalFilter -> festivalFilter.name().equalsIgnoreCase(filterName)) + .findAny() + .orElseThrow(() -> new BadRequestException(ErrorCode.INVALID_FESTIVAL_FILTER)); + } + + public Specification getSpecification() { + return filter.apply(LocalDate.now()); + } +} diff --git a/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java b/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java new file mode 100644 index 000000000..2521bf085 --- /dev/null +++ b/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java @@ -0,0 +1,20 @@ +package com.festago.festival.domain; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.festago.common.exception.BadRequestException; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class FestivalFilterTest { + + @Test + void 유요하지_않은_name_이면_예외() { + // given && when && then + assertThatThrownBy(() -> FestivalFilter.from("unvalid")) + .isInstanceOf(BadRequestException.class); + } +} From c8fe2ba8ab45fd0512c8142f06a0169200fc0924 Mon Sep 17 00:00:00 2001 From: BGuga Date: Thu, 9 Nov 2023 17:05:47 +0900 Subject: [PATCH 03/14] =?UTF-8?q?feat:=20=EC=B6=95=EC=A0=9C=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=20=EC=83=81=ED=83=9C=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?Controller,=20Service=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../festival/application/FestivalService.java | 7 +- .../FestivalFilter.java | 13 +-- .../FestivalSpecification.java | 3 +- .../presentation/FestivalController.java | 9 +- .../application/FestivalServiceTest.java | 6 +- .../festival/domain/FestivalFilterTest.java | 1 + .../repository/FestivalRepositoryTest.java | 98 +++++++++++++++++++ .../presentation/FestivalControllerTest.java | 3 +- 8 files changed, 124 insertions(+), 16 deletions(-) rename backend/src/main/java/com/festago/festival/{domain => repository}/FestivalFilter.java (69%) rename backend/src/main/java/com/festago/festival/{domain => repository}/FestivalSpecification.java (92%) create mode 100644 backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java diff --git a/backend/src/main/java/com/festago/festival/application/FestivalService.java b/backend/src/main/java/com/festago/festival/application/FestivalService.java index 4fe7c285f..03f132fb0 100644 --- a/backend/src/main/java/com/festago/festival/application/FestivalService.java +++ b/backend/src/main/java/com/festago/festival/application/FestivalService.java @@ -11,6 +11,7 @@ import com.festago.festival.dto.FestivalResponse; import com.festago.festival.dto.FestivalUpdateRequest; import com.festago.festival.dto.FestivalsResponse; +import com.festago.festival.repository.FestivalFilter; import com.festago.festival.repository.FestivalRepository; import com.festago.school.domain.School; import com.festago.school.repository.SchoolRepository; @@ -19,8 +20,8 @@ import java.time.Clock; import java.time.LocalDate; import java.util.List; -import org.springframework.dao.DataIntegrityViolationException; import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -49,8 +50,8 @@ private void validate(Festival festival) { } @Transactional(readOnly = true) - public FestivalsResponse findAll() { - List festivals = festivalRepository.findAll(); + public FestivalsResponse findFestivals(FestivalFilter festivalFilter) { + List festivals = festivalRepository.findAll(festivalFilter.getSpecification()); return FestivalsResponse.from(festivals); } diff --git a/backend/src/main/java/com/festago/festival/domain/FestivalFilter.java b/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java similarity index 69% rename from backend/src/main/java/com/festago/festival/domain/FestivalFilter.java rename to backend/src/main/java/com/festago/festival/repository/FestivalFilter.java index 8eabacc5c..757bce469 100644 --- a/backend/src/main/java/com/festago/festival/domain/FestivalFilter.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java @@ -1,13 +1,14 @@ -package com.festago.festival.domain; +package com.festago.festival.repository; -import static com.festago.festival.domain.FestivalSpecification.afterEndDate; -import static com.festago.festival.domain.FestivalSpecification.afterStartDate; -import static com.festago.festival.domain.FestivalSpecification.all; -import static com.festago.festival.domain.FestivalSpecification.beforeEndDate; -import static com.festago.festival.domain.FestivalSpecification.beforeStartDate; +import static com.festago.festival.repository.FestivalSpecification.afterEndDate; +import static com.festago.festival.repository.FestivalSpecification.afterStartDate; +import static com.festago.festival.repository.FestivalSpecification.all; +import static com.festago.festival.repository.FestivalSpecification.beforeEndDate; +import static com.festago.festival.repository.FestivalSpecification.beforeStartDate; import com.festago.common.exception.BadRequestException; import com.festago.common.exception.ErrorCode; +import com.festago.festival.domain.Festival; import java.time.LocalDate; import java.util.Arrays; import java.util.function.Function; diff --git a/backend/src/main/java/com/festago/festival/domain/FestivalSpecification.java b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java similarity index 92% rename from backend/src/main/java/com/festago/festival/domain/FestivalSpecification.java rename to backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java index 1b26d30ff..43bd389c8 100644 --- a/backend/src/main/java/com/festago/festival/domain/FestivalSpecification.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java @@ -1,5 +1,6 @@ -package com.festago.festival.domain; +package com.festago.festival.repository; +import com.festago.festival.domain.Festival; import java.time.LocalDate; import org.springframework.data.jpa.domain.Specification; diff --git a/backend/src/main/java/com/festago/presentation/FestivalController.java b/backend/src/main/java/com/festago/presentation/FestivalController.java index 67b9c94fd..f4cb705b5 100644 --- a/backend/src/main/java/com/festago/presentation/FestivalController.java +++ b/backend/src/main/java/com/festago/presentation/FestivalController.java @@ -3,6 +3,7 @@ import com.festago.festival.application.FestivalService; import com.festago.festival.dto.FestivalDetailResponse; import com.festago.festival.dto.FestivalsResponse; +import com.festago.festival.repository.FestivalFilter; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -10,6 +11,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -21,9 +23,10 @@ public class FestivalController { private final FestivalService festivalService; @GetMapping - @Operation(description = "모든 축제들을 조회한다.", summary = "축제 목록 조회") - public ResponseEntity findAll() { - FestivalsResponse response = festivalService.findAll(); + @Operation(description = "축제를 조건별로 조회한다. ALL: 전체, PROGRESS: 진행 중, PLANNED: 진행 예정, END: 종료", summary = "축제 목록 조회") + public ResponseEntity findFestivals( + @RequestParam(defaultValue = "ALL") String festivalFilter) { + FestivalsResponse response = festivalService.findFestivals(FestivalFilter.from(festivalFilter)); return ResponseEntity.ok() .body(response); } diff --git a/backend/src/test/java/com/festago/application/FestivalServiceTest.java b/backend/src/test/java/com/festago/application/FestivalServiceTest.java index c8c5c3387..cb6fd9b26 100644 --- a/backend/src/test/java/com/festago/application/FestivalServiceTest.java +++ b/backend/src/test/java/com/festago/application/FestivalServiceTest.java @@ -18,6 +18,7 @@ import com.festago.festival.dto.FestivalDetailStageResponse; import com.festago.festival.dto.FestivalResponse; import com.festago.festival.dto.FestivalsResponse; +import com.festago.festival.repository.FestivalFilter; import com.festago.festival.repository.FestivalRepository; import com.festago.school.domain.School; import com.festago.school.repository.SchoolRepository; @@ -40,6 +41,7 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.jpa.domain.Specification; @ExtendWith(MockitoExtension.class) @DisplayNameGeneration(ReplaceUnderscores.class) @@ -66,10 +68,10 @@ class FestivalServiceTest { // given Festival festival1 = FestivalFixture.festival().id(1L).build(); Festival festival2 = FestivalFixture.festival().id(2L).build(); - given(festivalRepository.findAll()).willReturn(List.of(festival1, festival2)); + given(festivalRepository.findAll(any(Specification.class))).willReturn(List.of(festival1, festival2)); // when - FestivalsResponse response = festivalService.findAll(); + FestivalsResponse response = festivalService.findFestivals(FestivalFilter.ALL); // then List festivalIds = response.festivals().stream().map(FestivalResponse::id).toList(); diff --git a/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java b/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java index 2521bf085..2592f54dd 100644 --- a/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java +++ b/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.festago.common.exception.BadRequestException; +import com.festago.festival.repository.FestivalFilter; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; diff --git a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java new file mode 100644 index 000000000..e3dddd865 --- /dev/null +++ b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java @@ -0,0 +1,98 @@ +package com.festago.festival.repository; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +import com.festago.festival.domain.Festival; +import com.festago.school.domain.School; +import com.festago.school.repository.SchoolRepository; +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +@DisplayNameGeneration(ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +@DataJpaTest +class FestivalRepositoryTest { + + private static final String CURRENT_FESTIVAL = "현재 축제"; + private static final String PAST_FESTIVAL = "과거 축제"; + private static final String FUTURE_FESTIVAL = "미래 축제"; + + @Autowired + FestivalRepository festivalRepository; + + @Autowired + SchoolRepository schoolRepository; + + @BeforeEach + public void setting() { + School school = schoolRepository.save(new School("domain", "name")); + LocalDate now = LocalDate.now(); + festivalRepository.save(new Festival(PAST_FESTIVAL, now.minusDays(10L), now.minusDays(8L), school)); + festivalRepository.save(new Festival(CURRENT_FESTIVAL, now.minusDays(1L), now.plusDays(1L), school)); + festivalRepository.save(new Festival(FUTURE_FESTIVAL, now.plusDays(1L), now.plusDays(3L), school)); + } + + @Test + void 모든_축제_반환() { + // given + FestivalFilter filter = FestivalFilter.ALL; + + // when + List actual = festivalRepository.findAll(filter.getSpecification()); + + // then + assertThat(actual).hasSize(3); + } + + @Test + void 진행_예정_축제_반환() { + // given + FestivalFilter filter = FestivalFilter.PLANNED; + + // when + List actual = festivalRepository.findAll(filter.getSpecification()); + + // then + assertSoftly(softAssertions -> { + assertThat(actual).hasSize(1); + assertThat(actual.get(0)).matches(festival -> festival.getName().equals(FUTURE_FESTIVAL)); + }); + } + + @Test + void 진행_중_축제_반환() { + // given + FestivalFilter filter = FestivalFilter.PROGRESS; + + // when + List actual = festivalRepository.findAll(filter.getSpecification()); + + // then + assertSoftly(softAssertions -> { + assertThat(actual).hasSize(1); + assertThat(actual.get(0)).matches(festival -> festival.getName().equals(CURRENT_FESTIVAL)); + }); + } + + @Test + void 종료_축제_반환() { + // given + FestivalFilter filter = FestivalFilter.END; + + // when + List actual = festivalRepository.findAll(filter.getSpecification()); + + // then + assertSoftly(softAssertions -> { + assertThat(actual).hasSize(1); + assertThat(actual.get(0)).matches(festival -> festival.getName().equals(PAST_FESTIVAL)); + }); + } +} diff --git a/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java b/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java index 12f038314..777f2c37e 100644 --- a/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java +++ b/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java @@ -1,6 +1,7 @@ package com.festago.presentation; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -47,7 +48,7 @@ class FestivalControllerTest { FestivalResponse festivalResponse2 = new FestivalResponse(2L, 2L, "우테대학교", LocalDate.now().minusDays(3), LocalDate.now(), "https://image2.png"); FestivalsResponse expected = new FestivalsResponse(List.of(festivalResponse1, festivalResponse2)); - given(festivalService.findAll()) + given(festivalService.findFestivals(any())) .willReturn(expected); // when & then From de59bb008c67bf94f9bb15b3318f0b1d4910b669 Mon Sep 17 00:00:00 2001 From: BGuga Date: Thu, 9 Nov 2023 17:52:16 +0900 Subject: [PATCH 04/14] =?UTF-8?q?refactor:=20private=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=9E=90=EB=A5=BC=20lombok=20=EC=9D=84=20=ED=86=B5=ED=95=B4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../festago/festival/repository/FestivalSpecification.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java index 43bd389c8..05b413c36 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java @@ -2,13 +2,13 @@ import com.festago.festival.domain.Festival; import java.time.LocalDate; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.springframework.data.jpa.domain.Specification; +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class FestivalSpecification { - private FestivalSpecification() { - } - public static Specification all() { return (root, query, criteriaBuilder) -> criteriaBuilder.conjunction(); } From 415a1b730809569a0361c29a4cf4ced748dc8056 Mon Sep 17 00:00:00 2001 From: BGuga Date: Thu, 9 Nov 2023 17:54:38 +0900 Subject: [PATCH 05/14] =?UTF-8?q?chore:=20=EA=B4=84=ED=98=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/festago/festival/repository/FestivalFilter.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java b/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java index 757bce469..ceecd131a 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java @@ -15,11 +15,11 @@ import org.springframework.data.jpa.domain.Specification; public enum FestivalFilter { - ALL((currentTime) -> all()), - PROGRESS((currentTime) -> afterStartDate(currentTime) + ALL(currentTime -> all()), + PROGRESS(currentTime -> afterStartDate(currentTime) .and(beforeEndDate(currentTime))), - PLANNED((currentTime) -> beforeStartDate(currentTime)), - END((currentTime) -> afterEndDate(currentTime)); + PLANNED(currentTime -> beforeStartDate(currentTime)), + END(currentTime -> afterEndDate(currentTime)); private final Function> filter; From 62e399857c7d5016a238fc32cec4ede1a39fea95 Mon Sep 17 00:00:00 2001 From: BGuga Date: Thu, 9 Nov 2023 17:55:38 +0900 Subject: [PATCH 06/14] =?UTF-8?q?chore:=20=EB=B3=80=EC=88=98=20=EC=83=81?= =?UTF-8?q?=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../festival/repository/FestivalSpecification.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java index 05b413c36..2af2a900a 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java @@ -9,23 +9,26 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class FestivalSpecification { + private static final String START_DATE = "startDate"; + private static final String END_DATE = "endDate"; + public static Specification all() { return (root, query, criteriaBuilder) -> criteriaBuilder.conjunction(); } public static Specification afterStartDate(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> criteriaBuilder.lessThan(root.get("startDate"), currentTime); + return (root, query, criteriaBuilder) -> criteriaBuilder.lessThan(root.get(START_DATE), currentTime); } public static Specification beforeStartDate(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get("startDate"), currentTime); + return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get(START_DATE), currentTime); } public static Specification afterEndDate(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> criteriaBuilder.lessThan(root.get("endDate"), currentTime); + return (root, query, criteriaBuilder) -> criteriaBuilder.lessThan(root.get(END_DATE), currentTime); } public static Specification beforeEndDate(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get("endDate"), currentTime); + return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get(END_DATE), currentTime); } } From 7596ca08f5848e0a29fcd87f497371cbce981645 Mon Sep 17 00:00:00 2001 From: BGuga Date: Thu, 9 Nov 2023 17:56:35 +0900 Subject: [PATCH 07/14] =?UTF-8?q?chore:=20given=20=EC=A0=88=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/festago/presentation/FestivalControllerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java b/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java index 777f2c37e..df4645d6c 100644 --- a/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java +++ b/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java @@ -13,6 +13,7 @@ import com.festago.festival.dto.FestivalDetailResponse; import com.festago.festival.dto.FestivalResponse; import com.festago.festival.dto.FestivalsResponse; +import com.festago.festival.repository.FestivalFilter; import com.festago.support.CustomWebMvcTest; import java.nio.charset.StandardCharsets; import java.time.LocalDate; @@ -48,7 +49,7 @@ class FestivalControllerTest { FestivalResponse festivalResponse2 = new FestivalResponse(2L, 2L, "우테대학교", LocalDate.now().minusDays(3), LocalDate.now(), "https://image2.png"); FestivalsResponse expected = new FestivalsResponse(List.of(festivalResponse1, festivalResponse2)); - given(festivalService.findFestivals(any())) + given(festivalService.findFestivals(any(FestivalFilter.class))) .willReturn(expected); // when & then From 0bead6af3cf5eda541cf1ed7d6da0c016da63c95 Mon Sep 17 00:00:00 2001 From: BGuga Date: Mon, 13 Nov 2023 15:39:22 +0900 Subject: [PATCH 08/14] =?UTF-8?q?feat:=20=EC=B6=95=EC=A0=9C=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20ALL=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=EA=B0=92=EC=9D=84=20=EC=A7=84=ED=96=89=20=EC=A4=91?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../festival/repository/FestivalFilter.java | 13 ++++--- .../repository/FestivalSpecification.java | 4 --- .../presentation/FestivalController.java | 4 +-- .../application/FestivalServiceTest.java | 19 ---------- .../festival/domain/FestivalFilterTest.java | 35 ++++++++++++++++++- .../repository/FestivalRepositoryTest.java | 12 ------- .../presentation/FestivalControllerTest.java | 17 +++++++-- 7 files changed, 57 insertions(+), 47 deletions(-) diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java b/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java index ceecd131a..73897c667 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java @@ -2,7 +2,6 @@ import static com.festago.festival.repository.FestivalSpecification.afterEndDate; import static com.festago.festival.repository.FestivalSpecification.afterStartDate; -import static com.festago.festival.repository.FestivalSpecification.all; import static com.festago.festival.repository.FestivalSpecification.beforeEndDate; import static com.festago.festival.repository.FestivalSpecification.beforeStartDate; @@ -10,12 +9,10 @@ import com.festago.common.exception.ErrorCode; import com.festago.festival.domain.Festival; import java.time.LocalDate; -import java.util.Arrays; import java.util.function.Function; import org.springframework.data.jpa.domain.Specification; public enum FestivalFilter { - ALL(currentTime -> all()), PROGRESS(currentTime -> afterStartDate(currentTime) .and(beforeEndDate(currentTime))), PLANNED(currentTime -> beforeStartDate(currentTime)), @@ -28,10 +25,12 @@ public enum FestivalFilter { } public static FestivalFilter from(String filterName) { - return Arrays.stream(FestivalFilter.values()) - .filter(festivalFilter -> festivalFilter.name().equalsIgnoreCase(filterName)) - .findAny() - .orElseThrow(() -> new BadRequestException(ErrorCode.INVALID_FESTIVAL_FILTER)); + return switch (filterName.toUpperCase()) { + case "PROGRESS" -> PROGRESS; + case "PLANNED" -> PLANNED; + case "END" -> END; + default -> throw new BadRequestException(ErrorCode.INVALID_FESTIVAL_FILTER); + }; } public Specification getSpecification() { diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java index 2af2a900a..50f90e67c 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java @@ -12,10 +12,6 @@ public class FestivalSpecification { private static final String START_DATE = "startDate"; private static final String END_DATE = "endDate"; - public static Specification all() { - return (root, query, criteriaBuilder) -> criteriaBuilder.conjunction(); - } - public static Specification afterStartDate(LocalDate currentTime) { return (root, query, criteriaBuilder) -> criteriaBuilder.lessThan(root.get(START_DATE), currentTime); } diff --git a/backend/src/main/java/com/festago/presentation/FestivalController.java b/backend/src/main/java/com/festago/presentation/FestivalController.java index f4cb705b5..197914043 100644 --- a/backend/src/main/java/com/festago/presentation/FestivalController.java +++ b/backend/src/main/java/com/festago/presentation/FestivalController.java @@ -23,9 +23,9 @@ public class FestivalController { private final FestivalService festivalService; @GetMapping - @Operation(description = "축제를 조건별로 조회한다. ALL: 전체, PROGRESS: 진행 중, PLANNED: 진행 예정, END: 종료", summary = "축제 목록 조회") + @Operation(description = "축제를 조건별로 조회한다. PROGRESS: 진행 중, PLANNED: 진행 예정, END: 종료, 기본값 -> 진행 중", summary = "축제 목록 조회") public ResponseEntity findFestivals( - @RequestParam(defaultValue = "ALL") String festivalFilter) { + @RequestParam(defaultValue = "PROGRESS") String festivalFilter) { FestivalsResponse response = festivalService.findFestivals(FestivalFilter.from(festivalFilter)); return ResponseEntity.ok() .body(response); diff --git a/backend/src/test/java/com/festago/application/FestivalServiceTest.java b/backend/src/test/java/com/festago/application/FestivalServiceTest.java index cb6fd9b26..e7c190ef1 100644 --- a/backend/src/test/java/com/festago/application/FestivalServiceTest.java +++ b/backend/src/test/java/com/festago/application/FestivalServiceTest.java @@ -17,8 +17,6 @@ import com.festago.festival.dto.FestivalDetailResponse; import com.festago.festival.dto.FestivalDetailStageResponse; import com.festago.festival.dto.FestivalResponse; -import com.festago.festival.dto.FestivalsResponse; -import com.festago.festival.repository.FestivalFilter; import com.festago.festival.repository.FestivalRepository; import com.festago.school.domain.School; import com.festago.school.repository.SchoolRepository; @@ -41,7 +39,6 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.jpa.domain.Specification; @ExtendWith(MockitoExtension.class) @DisplayNameGeneration(ReplaceUnderscores.class) @@ -63,22 +60,6 @@ class FestivalServiceTest { @InjectMocks FestivalService festivalService; - @Test - void 모든_축제_조회() { - // given - Festival festival1 = FestivalFixture.festival().id(1L).build(); - Festival festival2 = FestivalFixture.festival().id(2L).build(); - given(festivalRepository.findAll(any(Specification.class))).willReturn(List.of(festival1, festival2)); - - // when - FestivalsResponse response = festivalService.findFestivals(FestivalFilter.ALL); - - // then - List festivalIds = response.festivals().stream().map(FestivalResponse::id).toList(); - - assertThat(festivalIds).containsExactly(1L, 2L); - } - @Nested class 축제_생성 { diff --git a/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java b/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java index 2592f54dd..8c7ea210f 100644 --- a/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java +++ b/backend/src/test/java/com/festago/festival/domain/FestivalFilterTest.java @@ -1,5 +1,6 @@ package com.festago.festival.domain; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.festago.common.exception.BadRequestException; @@ -7,15 +8,47 @@ import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; @DisplayNameGeneration(ReplaceUnderscores.class) @SuppressWarnings("NonAsciiCharacters") class FestivalFilterTest { @Test - void 유요하지_않은_name_이면_예외() { + void 유효하지_않은_name_이면_예외() { // given && when && then assertThatThrownBy(() -> FestivalFilter.from("unvalid")) .isInstanceOf(BadRequestException.class); } + + @ValueSource(strings = {"progress", "Progress", "PROGRESS"}) + @ParameterizedTest + void PROGRESS_반환(String value) { + // given && when + FestivalFilter filter = FestivalFilter.from(value); + + // then + assertThat(filter).isEqualTo(FestivalFilter.PROGRESS); + } + + @ValueSource(strings = {"planned", "Planned", "PLANNED"}) + @ParameterizedTest + void PLANNED_반환(String value) { + // given && when + FestivalFilter filter = FestivalFilter.from(value); + + // then + assertThat(filter).isEqualTo(FestivalFilter.PLANNED); + } + + @ValueSource(strings = {"end", "End", "END"}) + @ParameterizedTest + void END_반환(String value) { + // given && when + FestivalFilter filter = FestivalFilter.from(value); + + // then + assertThat(filter).isEqualTo(FestivalFilter.END); + } } diff --git a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java index e3dddd865..d7ecd302c 100644 --- a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java +++ b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java @@ -39,18 +39,6 @@ public void setting() { festivalRepository.save(new Festival(FUTURE_FESTIVAL, now.plusDays(1L), now.plusDays(3L), school)); } - @Test - void 모든_축제_반환() { - // given - FestivalFilter filter = FestivalFilter.ALL; - - // when - List actual = festivalRepository.findAll(filter.getSpecification()); - - // then - assertThat(actual).hasSize(3); - } - @Test void 진행_예정_축제_반환() { // given diff --git a/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java b/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java index df4645d6c..8fef4e0a3 100644 --- a/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java +++ b/backend/src/test/java/com/festago/presentation/FestivalControllerTest.java @@ -1,9 +1,12 @@ package com.festago.presentation; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -22,6 +25,8 @@ import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; @@ -41,8 +46,11 @@ class FestivalControllerTest { @MockBean FestivalService festivalService; + @Captor + ArgumentCaptor festivalFilterCaptor; + @Test - void 모든_축제를_조회한다() throws Exception { + void 축제를_조회한다() throws Exception { // given FestivalResponse festivalResponse1 = new FestivalResponse(1L, 1L, "테코대학교", LocalDate.now(), LocalDate.now().plusDays(3), "https://image1.png"); @@ -61,7 +69,12 @@ class FestivalControllerTest { .getResponse() .getContentAsString(StandardCharsets.UTF_8); FestivalsResponse actual = objectMapper.readValue(content, FestivalsResponse.class); - assertThat(actual).isEqualTo(expected); + assertSoftly(softAssertions -> { + verify(festivalService, times(1)).findFestivals(festivalFilterCaptor.capture()); + assertThat(festivalFilterCaptor.getValue()).isEqualTo(FestivalFilter.PROGRESS); + assertThat(actual).isEqualTo(expected); + } + ); } @Test From 5840f26bb702912ababf83b3fda75f92c0999010 Mon Sep 17 00:00:00 2001 From: BGuga Date: Mon, 13 Nov 2023 15:56:52 +0900 Subject: [PATCH 09/14] =?UTF-8?q?feat:=20=EC=B6=95=EC=A0=9C=20=EB=8B=B9?= =?UTF-8?q?=EC=9D=BC=EC=9D=B4=20Progress=EC=97=90=20=ED=8F=AC=ED=95=A8?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20?= =?UTF-8?q?Spec=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../festival/application/FestivalService.java | 2 +- .../festival/repository/FestivalFilter.java | 16 +++++----------- .../repository/FestivalSpecification.java | 14 +++++++------- .../repository/FestivalRepositoryTest.java | 13 +++++++------ 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/backend/src/main/java/com/festago/festival/application/FestivalService.java b/backend/src/main/java/com/festago/festival/application/FestivalService.java index 03f132fb0..5b8185718 100644 --- a/backend/src/main/java/com/festago/festival/application/FestivalService.java +++ b/backend/src/main/java/com/festago/festival/application/FestivalService.java @@ -51,7 +51,7 @@ private void validate(Festival festival) { @Transactional(readOnly = true) public FestivalsResponse findFestivals(FestivalFilter festivalFilter) { - List festivals = festivalRepository.findAll(festivalFilter.getSpecification()); + List festivals = festivalRepository.findAll(festivalFilter.getSpecification(LocalDate.now(clock))); return FestivalsResponse.from(festivals); } diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java b/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java index 73897c667..81f60d250 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalFilter.java @@ -1,10 +1,5 @@ package com.festago.festival.repository; -import static com.festago.festival.repository.FestivalSpecification.afterEndDate; -import static com.festago.festival.repository.FestivalSpecification.afterStartDate; -import static com.festago.festival.repository.FestivalSpecification.beforeEndDate; -import static com.festago.festival.repository.FestivalSpecification.beforeStartDate; - import com.festago.common.exception.BadRequestException; import com.festago.common.exception.ErrorCode; import com.festago.festival.domain.Festival; @@ -13,10 +8,9 @@ import org.springframework.data.jpa.domain.Specification; public enum FestivalFilter { - PROGRESS(currentTime -> afterStartDate(currentTime) - .and(beforeEndDate(currentTime))), - PLANNED(currentTime -> beforeStartDate(currentTime)), - END(currentTime -> afterEndDate(currentTime)); + PROGRESS(FestivalSpecification::progress), + PLANNED(FestivalSpecification::planned), + END(FestivalSpecification::end); private final Function> filter; @@ -33,7 +27,7 @@ public static FestivalFilter from(String filterName) { }; } - public Specification getSpecification() { - return filter.apply(LocalDate.now()); + public Specification getSpecification(LocalDate currentTime) { + return filter.apply(currentTime); } } diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java index 50f90e67c..9102772bc 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java @@ -12,19 +12,19 @@ public class FestivalSpecification { private static final String START_DATE = "startDate"; private static final String END_DATE = "endDate"; - public static Specification afterStartDate(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> criteriaBuilder.lessThan(root.get(START_DATE), currentTime); + public static Specification progress(LocalDate currentTime) { + return (root, query, criteriaBuilder) -> criteriaBuilder.and( + criteriaBuilder.lessThanOrEqualTo(root.get(START_DATE), currentTime), + criteriaBuilder.greaterThanOrEqualTo(root.get(END_DATE), currentTime) + ); } - public static Specification beforeStartDate(LocalDate currentTime) { + public static Specification planned(LocalDate currentTime) { return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get(START_DATE), currentTime); } - public static Specification afterEndDate(LocalDate currentTime) { + public static Specification end(LocalDate currentTime) { return (root, query, criteriaBuilder) -> criteriaBuilder.lessThan(root.get(END_DATE), currentTime); } - public static Specification beforeEndDate(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get(END_DATE), currentTime); - } } diff --git a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java index d7ecd302c..f6540b466 100644 --- a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java +++ b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java @@ -30,12 +30,13 @@ class FestivalRepositoryTest { @Autowired SchoolRepository schoolRepository; + LocalDate now = LocalDate.parse("2023-06-30"); + @BeforeEach public void setting() { School school = schoolRepository.save(new School("domain", "name")); - LocalDate now = LocalDate.now(); - festivalRepository.save(new Festival(PAST_FESTIVAL, now.minusDays(10L), now.minusDays(8L), school)); - festivalRepository.save(new Festival(CURRENT_FESTIVAL, now.minusDays(1L), now.plusDays(1L), school)); + festivalRepository.save(new Festival(PAST_FESTIVAL, now.minusDays(3L), now.minusDays(1L), school)); + festivalRepository.save(new Festival(CURRENT_FESTIVAL, now, now, school)); festivalRepository.save(new Festival(FUTURE_FESTIVAL, now.plusDays(1L), now.plusDays(3L), school)); } @@ -45,7 +46,7 @@ public void setting() { FestivalFilter filter = FestivalFilter.PLANNED; // when - List actual = festivalRepository.findAll(filter.getSpecification()); + List actual = festivalRepository.findAll(filter.getSpecification(now)); // then assertSoftly(softAssertions -> { @@ -60,7 +61,7 @@ public void setting() { FestivalFilter filter = FestivalFilter.PROGRESS; // when - List actual = festivalRepository.findAll(filter.getSpecification()); + List actual = festivalRepository.findAll(filter.getSpecification(now)); // then assertSoftly(softAssertions -> { @@ -75,7 +76,7 @@ public void setting() { FestivalFilter filter = FestivalFilter.END; // when - List actual = festivalRepository.findAll(filter.getSpecification()); + List actual = festivalRepository.findAll(filter.getSpecification(now)); // then assertSoftly(softAssertions -> { From 9bb33fc0d9f698774d0437f0cbecbfc047ebc950 Mon Sep 17 00:00:00 2001 From: BGuga Date: Mon, 13 Nov 2023 16:49:40 +0900 Subject: [PATCH 10/14] =?UTF-8?q?feat:=20=EC=B6=95=EC=A0=9C=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=20=EC=83=81=ED=99=A9=EB=B3=84=20=EC=A0=95=EB=A0=AC=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/FestivalSpecification.java | 22 ++- .../repository/FestivalRepositoryTest.java | 168 ++++++++++++++---- 2 files changed, 148 insertions(+), 42 deletions(-) diff --git a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java index 9102772bc..50e7efb5c 100644 --- a/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java +++ b/backend/src/main/java/com/festago/festival/repository/FestivalSpecification.java @@ -13,18 +13,26 @@ public class FestivalSpecification { private static final String END_DATE = "endDate"; public static Specification progress(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> criteriaBuilder.and( - criteriaBuilder.lessThanOrEqualTo(root.get(START_DATE), currentTime), - criteriaBuilder.greaterThanOrEqualTo(root.get(END_DATE), currentTime) - ); + return (root, query, criteriaBuilder) -> { + query.orderBy(criteriaBuilder.asc(root.get(START_DATE))); + return criteriaBuilder.and( + criteriaBuilder.lessThanOrEqualTo(root.get(START_DATE), currentTime), + criteriaBuilder.greaterThanOrEqualTo(root.get(END_DATE), currentTime) + ); + }; } public static Specification planned(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get(START_DATE), currentTime); + return (root, query, criteriaBuilder) -> { + query.orderBy(criteriaBuilder.asc(root.get(START_DATE))); + return criteriaBuilder.greaterThan(root.get(START_DATE), currentTime); + }; } public static Specification end(LocalDate currentTime) { - return (root, query, criteriaBuilder) -> criteriaBuilder.lessThan(root.get(END_DATE), currentTime); + return (root, query, criteriaBuilder) -> { + query.orderBy(criteriaBuilder.desc(root.get(END_DATE))); + return criteriaBuilder.lessThan(root.get(END_DATE), currentTime); + }; } - } diff --git a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java index f6540b466..f587ae7ea 100644 --- a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java +++ b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java @@ -11,6 +11,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @@ -32,56 +33,153 @@ class FestivalRepositoryTest { LocalDate now = LocalDate.parse("2023-06-30"); + School school; + @BeforeEach public void setting() { - School school = schoolRepository.save(new School("domain", "name")); - festivalRepository.save(new Festival(PAST_FESTIVAL, now.minusDays(3L), now.minusDays(1L), school)); - festivalRepository.save(new Festival(CURRENT_FESTIVAL, now, now, school)); - festivalRepository.save(new Festival(FUTURE_FESTIVAL, now.plusDays(1L), now.plusDays(3L), school)); + this.school = schoolRepository.save(new School("domain", "name")); } @Test - void 진행_예정_축제_반환() { + void 종료_축제는_종료_시점이_최근인_순서로_반환된다() { // given - FestivalFilter filter = FestivalFilter.PLANNED; + FestivalFilter filter = FestivalFilter.END; + Festival festival1 = festivalRepository.save( + new Festival(PAST_FESTIVAL, now.minusDays(3L), now.minusDays(1L), school)); + Festival festival2 = festivalRepository.save( + new Festival(CURRENT_FESTIVAL, now, now, school)); + Festival festival3 = festivalRepository.save( + new Festival(FUTURE_FESTIVAL, now.plusDays(1L), now.plusDays(3L), school)); + + LocalDate currentTime = now.plusDays(4L); // when - List actual = festivalRepository.findAll(filter.getSpecification(now)); + List actual = festivalRepository.findAll(filter.getSpecification(currentTime)); // then - assertSoftly(softAssertions -> { - assertThat(actual).hasSize(1); - assertThat(actual.get(0)).matches(festival -> festival.getName().equals(FUTURE_FESTIVAL)); - }); + assertThat(actual).isEqualTo(List.of(festival3, festival2, festival1)); } - @Test - void 진행_중_축제_반환() { - // given - FestivalFilter filter = FestivalFilter.PROGRESS; - - // when - List actual = festivalRepository.findAll(filter.getSpecification(now)); - - // then - assertSoftly(softAssertions -> { - assertThat(actual).hasSize(1); - assertThat(actual.get(0)).matches(festival -> festival.getName().equals(CURRENT_FESTIVAL)); - }); + private void prepareNotOrderedFestivals() { + festivalRepository.save(new Festival(FUTURE_FESTIVAL, now.plusDays(1L), now.plusDays(3L), school)); + festivalRepository.save(new Festival(CURRENT_FESTIVAL, now, now, school)); + festivalRepository.save(new Festival(PAST_FESTIVAL, now.minusDays(3L), now.minusDays(1L), school)); } - @Test - void 종료_축제_반환() { - // given - FestivalFilter filter = FestivalFilter.END; + @Nested + class 진행_에정_축제_반환 { + + @Test + void 성공() { + // given + FestivalFilter filter = FestivalFilter.PLANNED; + prepareNotOrderedFestivals(); + + // when + List actual = festivalRepository.findAll(filter.getSpecification(now)); + + // then + assertSoftly(softAssertions -> { + assertThat(actual).hasSize(1); + assertThat(actual.get(0)).matches(festival -> festival.getName().equals(FUTURE_FESTIVAL)); + }); + } + + @Test + void 은_시작_시점이_빠른_순서로_반환된다() { + // given + FestivalFilter filter = FestivalFilter.PLANNED; + LocalDate currentTime = LocalDate.parse("2023-10-01"); + Festival festival2 = festivalRepository.save( + new Festival("festival2", currentTime.plusDays(2), currentTime.plusDays(10), school)); + Festival festival3 = festivalRepository.save( + new Festival("festival3", currentTime.plusDays(3), currentTime.plusDays(10), school)); + Festival festival1 = festivalRepository.save( + new Festival("festival1", currentTime.plusDays(1), currentTime.plusDays(10), school)); + + // when + List actual = festivalRepository.findAll(filter.getSpecification(currentTime)); + + // then + assertThat(actual).isEqualTo(List.of(festival1, festival2, festival3)); + } + } - // when - List actual = festivalRepository.findAll(filter.getSpecification(now)); + @Nested + class 진행_축제_반환 { + + @Test + void 성공() { + // given + FestivalFilter filter = FestivalFilter.PROGRESS; + prepareNotOrderedFestivals(); + + // when + List actual = festivalRepository.findAll(filter.getSpecification(now)); + + // then + assertSoftly(softAssertions -> { + assertThat(actual).hasSize(1); + assertThat(actual.get(0)).matches(festival -> festival.getName().equals(CURRENT_FESTIVAL)); + }); + } + + @Test + void 은_시작_시점이_빠른_순서로_반환된다() { + // given + FestivalFilter filter = FestivalFilter.PROGRESS; + LocalDate currentTime = LocalDate.parse("2023-10-01"); + Festival festival2 = festivalRepository.save( + new Festival("festival2", currentTime.minusDays(2), currentTime.plusDays(10), school)); + Festival festival3 = festivalRepository.save( + new Festival("festival3", currentTime.minusDays(1), currentTime.plusDays(10), school)); + Festival festival1 = festivalRepository.save( + new Festival("festival1", currentTime.minusDays(3), currentTime.plusDays(10), school)); + + // when + List actual = festivalRepository.findAll(filter.getSpecification(currentTime)); + + // then + assertThat(actual).isEqualTo(List.of(festival1, festival2, festival3)); + } + } - // then - assertSoftly(softAssertions -> { - assertThat(actual).hasSize(1); - assertThat(actual.get(0)).matches(festival -> festival.getName().equals(PAST_FESTIVAL)); - }); + @Nested + class 종료_축제_반환 { + + @Test + void 성공() { + // given + FestivalFilter filter = FestivalFilter.END; + prepareNotOrderedFestivals(); + + // when + List actual = festivalRepository.findAll(filter.getSpecification(now)); + + // then + assertSoftly(softAssertions -> { + assertThat(actual).hasSize(1); + assertThat(actual.get(0)).matches(festival -> festival.getName().equals(PAST_FESTIVAL)); + }); + } + + @Test + void 은_시작_시점이_빠른_순서로_반환된다() { + // given + FestivalFilter filter = FestivalFilter.END; + LocalDate currentTime = LocalDate.parse("2023-10-01"); + Festival festival2 = festivalRepository.save( + new Festival("festival2", currentTime.minusDays(10), currentTime.minusDays(2), school)); + Festival festival3 = festivalRepository.save( + new Festival("festival3", currentTime.minusDays(10), currentTime.minusDays(1), school)); + Festival festival1 = festivalRepository.save( + new Festival("festival1", currentTime.minusDays(10), currentTime.minusDays(3), school)); + + // when + List actual = festivalRepository.findAll(filter.getSpecification(currentTime)); + + // then + assertThat(actual).isEqualTo(List.of(festival3, festival2, festival1)); + } } } From 9ff03dded5126a877d54dffc964e941f7bc9c764 Mon Sep 17 00:00:00 2001 From: BGuga Date: Mon, 13 Nov 2023 17:07:18 +0900 Subject: [PATCH 11/14] =?UTF-8?q?chore:=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../festago/festival/repository/FestivalRepositoryTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java index f587ae7ea..4de19f371 100644 --- a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java +++ b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java @@ -103,6 +103,7 @@ class 진행_에정_축제_반환 { // then assertThat(actual).isEqualTo(List.of(festival1, festival2, festival3)); } + } @Nested @@ -142,6 +143,7 @@ class 진행_축제_반환 { // then assertThat(actual).isEqualTo(List.of(festival1, festival2, festival3)); } + } @Nested @@ -181,5 +183,6 @@ class 종료_축제_반환 { // then assertThat(actual).isEqualTo(List.of(festival3, festival2, festival1)); } + } } From 3b3aa776b8851bc1485cc1694b471108274fee5a Mon Sep 17 00:00:00 2001 From: BGuga Date: Mon, 13 Nov 2023 17:07:29 +0900 Subject: [PATCH 12/14] =?UTF-8?q?chore:=20index=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/resources/db/migration/V9__festival_date_index.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 backend/src/main/resources/db/migration/V9__festival_date_index.sql diff --git a/backend/src/main/resources/db/migration/V9__festival_date_index.sql b/backend/src/main/resources/db/migration/V9__festival_date_index.sql new file mode 100644 index 000000000..e52df8ad8 --- /dev/null +++ b/backend/src/main/resources/db/migration/V9__festival_date_index.sql @@ -0,0 +1,4 @@ +create index festival_start_date_index + on festival (start_date); +create index festival_end_date_index + on festival (end_date desc); From 91ae2469d290b5b8b4e54925ef14abd6528bb2ae Mon Sep 17 00:00:00 2001 From: BGuga Date: Mon, 13 Nov 2023 17:09:36 +0900 Subject: [PATCH 13/14] =?UTF-8?q?chore:=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/festago/common/exception/ErrorCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/festago/common/exception/ErrorCode.java b/backend/src/main/java/com/festago/common/exception/ErrorCode.java index 7e47f1bd8..890c4a099 100644 --- a/backend/src/main/java/com/festago/common/exception/ErrorCode.java +++ b/backend/src/main/java/com/festago/common/exception/ErrorCode.java @@ -29,7 +29,7 @@ public enum ErrorCode { DELETE_CONSTRAINT_STAGE("티켓이 등록된 공연은 삭제할 수 없습니다."), DELETE_CONSTRAINT_SCHOOL("학생 또는 축제에 등록된 학교는 삭제할 수 없습니다."), DUPLICATE_SCHOOL("이미 존재하는 학교 정보입니다."), - INVALID_FESTIVAL_FILTER("유효하지 않은 필터 값입니다."), + INVALID_FESTIVAL_FILTER("유효하지 않은 축제의 필터 값입니다."), // 401 From 858672aec6a2754a35f71c014b27b0dedaac1269 Mon Sep 17 00:00:00 2001 From: BGuga Date: Mon, 13 Nov 2023 17:30:57 +0900 Subject: [PATCH 14/14] =?UTF-8?q?chore:=20test=20=EA=B0=9C=ED=96=89=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EB=B3=80=EC=88=98=20=EC=9E=AC?= =?UTF-8?q?=ED=99=9C=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/FestivalRepositoryTest.java | 52 +++++-------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java index 4de19f371..81e13a050 100644 --- a/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java +++ b/backend/src/test/java/com/festago/festival/repository/FestivalRepositoryTest.java @@ -40,26 +40,6 @@ public void setting() { this.school = schoolRepository.save(new School("domain", "name")); } - @Test - void 종료_축제는_종료_시점이_최근인_순서로_반환된다() { - // given - FestivalFilter filter = FestivalFilter.END; - Festival festival1 = festivalRepository.save( - new Festival(PAST_FESTIVAL, now.minusDays(3L), now.minusDays(1L), school)); - Festival festival2 = festivalRepository.save( - new Festival(CURRENT_FESTIVAL, now, now, school)); - Festival festival3 = festivalRepository.save( - new Festival(FUTURE_FESTIVAL, now.plusDays(1L), now.plusDays(3L), school)); - - LocalDate currentTime = now.plusDays(4L); - - // when - List actual = festivalRepository.findAll(filter.getSpecification(currentTime)); - - // then - assertThat(actual).isEqualTo(List.of(festival3, festival2, festival1)); - } - private void prepareNotOrderedFestivals() { festivalRepository.save(new Festival(FUTURE_FESTIVAL, now.plusDays(1L), now.plusDays(3L), school)); festivalRepository.save(new Festival(CURRENT_FESTIVAL, now, now, school)); @@ -89,21 +69,19 @@ class 진행_에정_축제_반환 { void 은_시작_시점이_빠른_순서로_반환된다() { // given FestivalFilter filter = FestivalFilter.PLANNED; - LocalDate currentTime = LocalDate.parse("2023-10-01"); Festival festival2 = festivalRepository.save( - new Festival("festival2", currentTime.plusDays(2), currentTime.plusDays(10), school)); + new Festival("festival2", now.plusDays(2), now.plusDays(10), school)); Festival festival3 = festivalRepository.save( - new Festival("festival3", currentTime.plusDays(3), currentTime.plusDays(10), school)); + new Festival("festival3", now.plusDays(3), now.plusDays(10), school)); Festival festival1 = festivalRepository.save( - new Festival("festival1", currentTime.plusDays(1), currentTime.plusDays(10), school)); + new Festival("festival1", now.plusDays(1), now.plusDays(10), school)); // when - List actual = festivalRepository.findAll(filter.getSpecification(currentTime)); + List actual = festivalRepository.findAll(filter.getSpecification(now)); // then assertThat(actual).isEqualTo(List.of(festival1, festival2, festival3)); } - } @Nested @@ -129,21 +107,19 @@ class 진행_축제_반환 { void 은_시작_시점이_빠른_순서로_반환된다() { // given FestivalFilter filter = FestivalFilter.PROGRESS; - LocalDate currentTime = LocalDate.parse("2023-10-01"); Festival festival2 = festivalRepository.save( - new Festival("festival2", currentTime.minusDays(2), currentTime.plusDays(10), school)); + new Festival("festival2", now.minusDays(2), now.plusDays(10), school)); Festival festival3 = festivalRepository.save( - new Festival("festival3", currentTime.minusDays(1), currentTime.plusDays(10), school)); + new Festival("festival3", now.minusDays(1), now.plusDays(10), school)); Festival festival1 = festivalRepository.save( - new Festival("festival1", currentTime.minusDays(3), currentTime.plusDays(10), school)); + new Festival("festival1", now.minusDays(3), now.plusDays(10), school)); // when - List actual = festivalRepository.findAll(filter.getSpecification(currentTime)); + List actual = festivalRepository.findAll(filter.getSpecification(now)); // then assertThat(actual).isEqualTo(List.of(festival1, festival2, festival3)); } - } @Nested @@ -166,23 +142,21 @@ class 종료_축제_반환 { } @Test - void 은_시작_시점이_빠른_순서로_반환된다() { + void 은_종료_시점이_느린_순서로_반환된다() { // given FestivalFilter filter = FestivalFilter.END; - LocalDate currentTime = LocalDate.parse("2023-10-01"); Festival festival2 = festivalRepository.save( - new Festival("festival2", currentTime.minusDays(10), currentTime.minusDays(2), school)); + new Festival("festival2", now.minusDays(10), now.minusDays(2), school)); Festival festival3 = festivalRepository.save( - new Festival("festival3", currentTime.minusDays(10), currentTime.minusDays(1), school)); + new Festival("festival3", now.minusDays(10), now.minusDays(1), school)); Festival festival1 = festivalRepository.save( - new Festival("festival1", currentTime.minusDays(10), currentTime.minusDays(3), school)); + new Festival("festival1", now.minusDays(10), now.minusDays(3), school)); // when - List actual = festivalRepository.findAll(filter.getSpecification(currentTime)); + List actual = festivalRepository.findAll(filter.getSpecification(now)); // then assertThat(actual).isEqualTo(List.of(festival3, festival2, festival1)); } - } }