From 21d2659375de0b8da0976116c71d482d9b0de1c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=B0=EC=A4=80=EC=9D=BC=28Bae=20Junil=29?= <70627982+bjo6300@users.noreply.github.com> Date: Wed, 22 Nov 2023 01:16:16 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EB=B4=89=EC=82=AC=20=EB=AA=A8?= =?UTF-8?q?=EC=A7=91=EA=B8=80=20=EA=B2=80=EC=83=89(=EB=B4=89=EC=82=AC?= =?UTF-8?q?=EC=9E=90)=20=EB=A1=9C=EC=A7=81=EC=9D=98=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=EC=9D=84=20=EC=BB=A4?= =?UTF-8?q?=EC=84=9C=20=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=ED=95=9C=EB=8B=A4.=20(#293)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 봉사 모집글 조회(봉사자) 로직을 no offset으로 구현한다. Co-Authored-By: hseong3243 <48748265+hseong3243@users.noreply.github.com> Co-Authored-By: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Co-Authored-By: minjungkim <97938489+pushedrumex@users.noreply.github.com> * test: 봉사 모집글 조회(봉사자) no offset 로직을 테스트한다. Co-Authored-By: hseong3243 <48748265+hseong3243@users.noreply.github.com> Co-Authored-By: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Co-Authored-By: minjungkim <97938489+pushedrumex@users.noreply.github.com> Co-Authored-By: hseong3243 <48748265+hseong3243@users.noreply.github.com> Co-Authored-By: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Co-Authored-By: minjungkim <97938489+pushedrumex@users.noreply.github.com> * fix: feat/#267 브랜치에 cd-dev를 적용하지 않는다. * feat: count가 null인 경우 0으로 변경한다. * test: 봉사 모집글 조회(봉사자) no offset 서비스 로직을 테스트한다. * test: 봉사 모집글 조회(봉사자) no offset 컨트롤러 테스트 코드를 작성한다. * fix: 봉사 모집글 조회(봉사자) 테스트 코드에서 봉사자 액세스 토큰을 제거한다. * test: countFindRecruitmentsV2 레포지토리 테스트 코드를 작성한다. --------- Co-authored-by: hseong3243 <48748265+hseong3243@users.noreply.github.com> Co-authored-by: Seonheui Jeon <88873302+funnysunny08@users.noreply.github.com> Co-authored-by: minjungkim <97938489+pushedrumex@users.noreply.github.com> --- .../controller/RecruitmentController.java | 24 +++++- .../request/FindRecruitmentsRequestV2.java | 35 ++++++++ .../response/FindRecruitmentsResponse.java | 10 +++ .../RecruitmentRepositoryCustom.java | 11 +++ .../repository/RecruitmentRepositoryImpl.java | 65 +++++++++++++++ .../service/RecruitmentService.java | 39 +++++++++ .../controller/RecruitmentControllerTest.java | 81 ++++++++++++++++++- .../repository/RecruitmentRepositoryTest.java | 52 ++++++++++++ .../service/RecruitmentServiceTest.java | 58 ++++++++++++- 9 files changed, 370 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/clova/anifriends/domain/recruitment/dto/request/FindRecruitmentsRequestV2.java diff --git a/src/main/java/com/clova/anifriends/domain/recruitment/controller/RecruitmentController.java b/src/main/java/com/clova/anifriends/domain/recruitment/controller/RecruitmentController.java index e389fe2aa..3738d28f4 100644 --- a/src/main/java/com/clova/anifriends/domain/recruitment/controller/RecruitmentController.java +++ b/src/main/java/com/clova/anifriends/domain/recruitment/controller/RecruitmentController.java @@ -1,8 +1,10 @@ package com.clova.anifriends.domain.recruitment.controller; import com.clova.anifriends.domain.auth.LoginUser; +import com.clova.anifriends.domain.auth.authorization.ShelterOnly; import com.clova.anifriends.domain.recruitment.dto.request.FindRecruitmentsByShelterRequest; import com.clova.anifriends.domain.recruitment.dto.request.FindRecruitmentsRequest; +import com.clova.anifriends.domain.recruitment.dto.request.FindRecruitmentsRequestV2; import com.clova.anifriends.domain.recruitment.dto.request.RegisterRecruitmentRequest; import com.clova.anifriends.domain.recruitment.dto.request.UpdateRecruitmentRequest; import com.clova.anifriends.domain.recruitment.dto.response.FindCompletedRecruitmentsResponse; @@ -12,7 +14,6 @@ import com.clova.anifriends.domain.recruitment.dto.response.FindRecruitmentsResponse; import com.clova.anifriends.domain.recruitment.dto.response.RegisterRecruitmentResponse; import com.clova.anifriends.domain.recruitment.service.RecruitmentService; -import com.clova.anifriends.domain.auth.authorization.ShelterOnly; import jakarta.validation.Valid; import java.net.URI; import lombok.RequiredArgsConstructor; @@ -88,6 +89,27 @@ public ResponseEntity findRecruitments( )); } + @GetMapping("/v2/recruitments") + public ResponseEntity findRecruitmentsV2( + @ModelAttribute @Valid FindRecruitmentsRequestV2 findRecruitmentsRequestV2, + Pageable pageable) { + KeywordCondition keywordCondition = findRecruitmentsRequestV2.keywordFilter() + .getKeywordCondition(); + + return ResponseEntity.ok(recruitmentService.findRecruitmentsV2( + findRecruitmentsRequestV2.keyword(), + findRecruitmentsRequestV2.startDate(), + findRecruitmentsRequestV2.endDate(), + findRecruitmentsRequestV2.closedFilter().getIsClosed(), + keywordCondition.titleFilter(), + keywordCondition.contentFilter(), + keywordCondition.shelterNameFilter(), + findRecruitmentsRequestV2.createdAt(), + findRecruitmentsRequestV2.recruitmentId(), + pageable + )); + } + @ShelterOnly @GetMapping("/shelters/recruitments") public ResponseEntity findRecruitmentsByShelter( diff --git a/src/main/java/com/clova/anifriends/domain/recruitment/dto/request/FindRecruitmentsRequestV2.java b/src/main/java/com/clova/anifriends/domain/recruitment/dto/request/FindRecruitmentsRequestV2.java new file mode 100644 index 000000000..5de47b297 --- /dev/null +++ b/src/main/java/com/clova/anifriends/domain/recruitment/dto/request/FindRecruitmentsRequestV2.java @@ -0,0 +1,35 @@ +package com.clova.anifriends.domain.recruitment.dto.request; + +import com.clova.anifriends.domain.recruitment.controller.KeywordFilter; +import com.clova.anifriends.domain.recruitment.controller.RecruitmentStatusFilter; +import java.time.LocalDate; +import java.time.LocalDateTime; + +public record FindRecruitmentsRequestV2( + String keyword, + LocalDate startDate, + LocalDate endDate, + RecruitmentStatusFilter closedFilter, + KeywordFilter keywordFilter, + Long recruitmentId, + LocalDateTime createdAt +) { + + public FindRecruitmentsRequestV2( + String keyword, + LocalDate startDate, + LocalDate endDate, + RecruitmentStatusFilter closedFilter, + KeywordFilter keywordFilter, + Long recruitmentId, + LocalDateTime createdAt + ) { + this.keyword = keyword; + this.startDate = startDate; + this.endDate = endDate; + this.closedFilter = closedFilter == null ? RecruitmentStatusFilter.ALL : closedFilter; + this.keywordFilter = keywordFilter == null ? KeywordFilter.ALL : keywordFilter; + this.recruitmentId = recruitmentId; + this.createdAt = createdAt; + } +} diff --git a/src/main/java/com/clova/anifriends/domain/recruitment/dto/response/FindRecruitmentsResponse.java b/src/main/java/com/clova/anifriends/domain/recruitment/dto/response/FindRecruitmentsResponse.java index ccaa2cd8c..4d52d8b47 100644 --- a/src/main/java/com/clova/anifriends/domain/recruitment/dto/response/FindRecruitmentsResponse.java +++ b/src/main/java/com/clova/anifriends/domain/recruitment/dto/response/FindRecruitmentsResponse.java @@ -5,6 +5,7 @@ import java.time.LocalDateTime; import java.util.List; import org.springframework.data.domain.Page; +import org.springframework.data.domain.Slice; public record FindRecruitmentsResponse( List recruitments, @@ -44,4 +45,13 @@ public static FindRecruitmentsResponse from(Page recruitments) { .toList(); return new FindRecruitmentsResponse(content, pageInfo); } + + public static FindRecruitmentsResponse fromV2(Slice recruitments, Long count) { + PageInfo pageInfo = PageInfo.of(count, recruitments.hasNext()); + List content = recruitments.getContent() + .stream() + .map(FindRecruitmentResponse::from) + .toList(); + return new FindRecruitmentsResponse(content, pageInfo); + } } diff --git a/src/main/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryCustom.java b/src/main/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryCustom.java index 27573cc23..69d6571af 100644 --- a/src/main/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryCustom.java +++ b/src/main/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryCustom.java @@ -2,8 +2,10 @@ import com.clova.anifriends.domain.recruitment.Recruitment; import java.time.LocalDate; +import java.time.LocalDateTime; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; public interface RecruitmentRepositoryCustom { @@ -11,6 +13,15 @@ Page findRecruitments(String keyword, LocalDate startDate, LocalDate endDate, Boolean isClosed, boolean titleContains, boolean contentContains, boolean shelterNameContains, Pageable pageable); + Slice findRecruitmentsV2(String keyword, LocalDate startDate, + LocalDate endDate, Boolean isClosed, boolean titleContains, boolean contentContains, + boolean shelterNameContains, LocalDateTime createdAt, Long recruitmentId, + Pageable pageable); + + Long countFindRecruitmentsV2(String keyword, LocalDate startDate, + LocalDate endDate, Boolean isClosed, boolean titleContains, boolean contentContains, + boolean shelterNameContains); + Page findRecruitmentsByShelterOrderByCreatedAt(long shelterId, String keyword, LocalDate startDate, LocalDate endDate, Boolean isClosed, Boolean content, Boolean title, Pageable pageable); diff --git a/src/main/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryImpl.java b/src/main/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryImpl.java index 8a3534f25..0113cce71 100644 --- a/src/main/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryImpl.java +++ b/src/main/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryImpl.java @@ -8,6 +8,7 @@ import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; import java.util.Objects; import java.util.function.Supplier; @@ -15,6 +16,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Repository; @Repository @@ -55,6 +58,68 @@ public Page findRecruitments(String keyword, LocalDate startDate, return new PageImpl<>(content, pageable, count); } + @Override + public Slice findRecruitmentsV2(String keyword, LocalDate startDate, + LocalDate endDate, Boolean isClosed, boolean titleContains, boolean contentContains, + boolean shelterNameContains, LocalDateTime createdAt, Long recruitmentId, + Pageable pageable) { + List content = query.select(recruitment) + .from(recruitment) + .join(recruitment.shelter) + .leftJoin(recruitment.applicants) + .where( + keywordSearch(keyword, titleContains, contentContains, shelterNameContains), + recruitmentIsClosed(isClosed), + recruitmentStartTimeGoe(startDate), + recruitmentStartTimeLoe(endDate), + cursorId(recruitmentId, createdAt) + ) + .orderBy(recruitment.createdAt.desc()) + .limit(pageable.getPageSize() + 1L) + .offset(pageable.getOffset()) + .fetch(); + + boolean hasNext = false; + + if (content.size() > pageable.getPageSize()) { + content.remove(pageable.getPageSize()); + hasNext = true; + } + + return new SliceImpl<>(content, pageable, hasNext); + } + + @Override + public Long countFindRecruitmentsV2(String keyword, LocalDate startDate, + LocalDate endDate, Boolean isClosed, boolean titleContains, boolean contentContains, + boolean shelterNameContains) { + + Long count = query.select(recruitment.count()) + .from(recruitment) + .join(recruitment.shelter) + .where( + keywordSearch(keyword, titleContains, contentContains, shelterNameContains), + recruitmentIsClosed(isClosed), + recruitmentStartTimeGoe(startDate), + recruitmentStartTimeLoe(endDate) + ).fetchOne(); + + return count != null ? count : 0; + } + + private BooleanExpression cursorId(Long recruitmentId, LocalDateTime createdAt) { + if (recruitmentId == null || createdAt == null) { + return null; + } + + return recruitment.createdAt.lt(createdAt) + .or( + recruitment.recruitmentId.lt(recruitmentId) + .and(recruitment.createdAt.eq(createdAt) + ) + ); + } + private BooleanBuilder keywordSearch(String keyword, boolean titleFilter, boolean contentFilter, boolean shelterNameFilter) { return nullSafeBuilder(() -> recruitmentTitleContains(keyword, titleFilter)) diff --git a/src/main/java/com/clova/anifriends/domain/recruitment/service/RecruitmentService.java b/src/main/java/com/clova/anifriends/domain/recruitment/service/RecruitmentService.java index 58722c55a..02c9e5b82 100644 --- a/src/main/java/com/clova/anifriends/domain/recruitment/service/RecruitmentService.java +++ b/src/main/java/com/clova/anifriends/domain/recruitment/service/RecruitmentService.java @@ -20,6 +20,7 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -127,6 +128,44 @@ public FindRecruitmentsResponse findRecruitments( return FindRecruitmentsResponse.from(recruitments); } + @Transactional(readOnly = true) + public FindRecruitmentsResponse findRecruitmentsV2( + String keyword, + LocalDate startDate, + LocalDate endDate, + Boolean isClosed, + Boolean titleContains, + Boolean contentContains, + Boolean shelterNameContains, + LocalDateTime createdAt, + Long recruitmentId, + Pageable pageable + ) { + Slice recruitments = recruitmentRepository.findRecruitmentsV2( + keyword, + startDate, + endDate, + isClosed, + titleContains, + contentContains, + shelterNameContains, + createdAt, + recruitmentId, + pageable); + + Long count = recruitmentRepository.countFindRecruitmentsV2( + keyword, + startDate, + endDate, + isClosed, + titleContains, + contentContains, + shelterNameContains + ); + + return FindRecruitmentsResponse.fromV2(recruitments, count); + } + @Transactional public void closeRecruitment(Long shelterId, Long recruitmentId) { Recruitment recruitment = getRecruitmentByShelter(shelterId, recruitmentId); diff --git a/src/test/java/com/clova/anifriends/domain/recruitment/controller/RecruitmentControllerTest.java b/src/test/java/com/clova/anifriends/domain/recruitment/controller/RecruitmentControllerTest.java index 7286ab523..5618ff526 100644 --- a/src/test/java/com/clova/anifriends/domain/recruitment/controller/RecruitmentControllerTest.java +++ b/src/test/java/com/clova/anifriends/domain/recruitment/controller/RecruitmentControllerTest.java @@ -199,15 +199,88 @@ void findRecruitments() throws Exception { //when ResultActions resultActions = mockMvc.perform(get("/api/recruitments") - .header(AUTHORIZATION, volunteerAccessToken) .params(params)); //then resultActions.andExpect(status().isOk()) .andDo(restDocs.document( - requestHeaders( - headerWithName(AUTHORIZATION).description("봉사자 액세스 토큰") + queryParameters( + parameterWithName("keyword").description("검색어").optional(), + parameterWithName("startDate").description("검색 시작일").optional() + .attributes(DocumentationFormatGenerator.getDateConstraint()), + parameterWithName("endDate").description("검색 종료일").optional() + .attributes(DocumentationFormatGenerator.getDateConstraint()), + parameterWithName("closedFilter").description("마감 여부").optional() + .attributes( + DocumentationFormatGenerator.getConstraint("IS_OPENED, IS_CLOSED")), + parameterWithName("keywordFilter").description("검색 필터").optional() + .attributes(DocumentationFormatGenerator.getConstraint( + String.join(", ", Arrays.stream(KeywordFilter.values()) + .map(KeywordFilter::name) + .toArray(String[]::new)))), + parameterWithName("pageNumber").description("페이지 번호"), + parameterWithName("pageSize").description("페이지 사이즈") ), + responseFields( + fieldWithPath("recruitments").type(ARRAY).description("봉사 모집글 리스트"), + fieldWithPath("recruitments[].recruitmentId").type(NUMBER) + .description("봉사 모집글 ID"), + fieldWithPath("recruitments[].recruitmentTitle").type(STRING) + .description("봉사 모집글 제목"), + fieldWithPath("recruitments[].recruitmentStartTime").type(STRING) + .description("봉사 시작 시간"), + fieldWithPath("recruitments[].recruitmentEndTime").type(STRING) + .description("봉사 종료 시간"), + fieldWithPath("recruitments[].recruitmentIsClosed").type(BOOLEAN) + .description("봉사 모집 마감 여부"), + fieldWithPath("recruitments[].recruitmentApplicantCount").type(NUMBER) + .description("봉사 신청 인원"), + fieldWithPath("recruitments[].recruitmentCapacity").type(NUMBER) + .description("봉사 정원"), + fieldWithPath("recruitments[].shelterName").type(STRING).description("보호소 이름"), + fieldWithPath("recruitments[].shelterImageUrl").type(STRING) + .description("보호소 이미지 url").optional(), + fieldWithPath("pageInfo").type(OBJECT).description("페이지 정보"), + fieldWithPath("pageInfo.totalElements").type(NUMBER).description("총 요소 개수"), + fieldWithPath("pageInfo.hasNext").type(BOOLEAN).description("다음 페이지 여부") + ) + )); + } + + @Test + @DisplayName("성공: 봉사 모집글 조회, 검색 V2 API 호출") + void findRecruitmentsV2() throws Exception { + //given + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("keyword", "겅색어"); + params.add("startDate", LocalDate.now().toString()); + params.add("endDate", LocalDate.now().toString()); + params.add("closedFilter", "IS_OPENED"); + params.add("keywordFilter", KeywordFilter.IS_CONTENT.getName()); + params.add("recruitmentId", "1"); + params.add("createdAt", String.valueOf(LocalDateTime.now())); + params.add("pageNumber", "0"); + params.add("pageSize", "10"); + Shelter shelter = shelter(); + Recruitment recruitment = recruitment(shelter); + ReflectionTestUtils.setField(recruitment, "recruitmentId", 1L); + FindRecruitmentResponse findRecruitmentResponse + = FindRecruitmentResponse.from(recruitment); + PageInfo pageInfo = new PageInfo(1, false); + FindRecruitmentsResponse response = new FindRecruitmentsResponse( + List.of(findRecruitmentResponse), pageInfo); + + given(recruitmentService.findRecruitmentsV2(anyString(), any(), any(), + any(), anyBoolean(), anyBoolean(), anyBoolean(), any(), anyLong(), any())) + .willReturn(response); + + //when + ResultActions resultActions = mockMvc.perform(get("/api/v2/recruitments") + .params(params)); + + //then + resultActions.andExpect(status().isOk()) + .andDo(restDocs.document( queryParameters( parameterWithName("keyword").description("검색어").optional(), parameterWithName("startDate").description("검색 시작일").optional() @@ -222,6 +295,8 @@ void findRecruitments() throws Exception { String.join(", ", Arrays.stream(KeywordFilter.values()) .map(KeywordFilter::name) .toArray(String[]::new)))), + parameterWithName("recruitmentId").description("보호소 ID"), + parameterWithName("createdAt").description("보호소 생성 날짜"), parameterWithName("pageNumber").description("페이지 번호"), parameterWithName("pageSize").description("페이지 사이즈") ), diff --git a/src/test/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryTest.java b/src/test/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryTest.java index 1798edf50..255d864e7 100644 --- a/src/test/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryTest.java +++ b/src/test/java/com/clova/anifriends/domain/recruitment/repository/RecruitmentRepositoryTest.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; import org.springframework.test.util.ReflectionTestUtils; class RecruitmentRepositoryTest extends BaseRepositoryTest { @@ -116,6 +117,57 @@ void findRecruitmentsWhenArgsAreNotNull() { } } + @Nested + @DisplayName("findRecruitmentsV2 메서드 실행 시") + class FindRecruitmentsV2Test { + + //todo: 다양한 케이스에 대한 테스트를 작성할 것 + @Test + @DisplayName("성공: 모든 인자가 null") + void findRecruitmentsV2WhenArgsAreNull() { + //given + Shelter shelter = ShelterFixture.shelter(); + Recruitment recruitment = RecruitmentFixture.recruitment(shelter); + PageRequest pageRequest = PageRequest.of(0, 10); + shelterRepository.save(shelter); + recruitmentRepository.save(recruitment); + + //when + Slice recruitments = recruitmentRepository.findRecruitmentsV2(null, null, + null, null, false, false, false, LocalDateTime.now(), + recruitment.getRecruitmentId(), + pageRequest); + + //then + assertThat(recruitments.getContent().size()).isEqualTo(1); + } + } + + @Nested + @DisplayName("countFindRecruitmentsV2 메서드 실행 시") + class CountFindRecruitmentsV2Test { + + //todo: 다양한 케이스에 대한 테스트를 작성할 것 + @Test + @DisplayName("성공: 모든 인자가 null") + void countFindRecruitmentsV2WhenArgsAreNull() { + //given + Shelter shelter = ShelterFixture.shelter(); + Recruitment recruitment = RecruitmentFixture.recruitment(shelter); + PageRequest pageRequest = PageRequest.of(0, 10); + shelterRepository.save(shelter); + recruitmentRepository.save(recruitment); + + //when + Long count = recruitmentRepository.countFindRecruitmentsV2(null, null, + null, null, false, false, false + ); + + //then + assertThat(count).isEqualTo(1); + } + } + @Nested @DisplayName("findRecruitmentsByShelterOrderByCreatedAt 메서드 실행 시") class FindRecruitmentsByShelterOrderByCreatedAtTest { diff --git a/src/test/java/com/clova/anifriends/domain/recruitment/service/RecruitmentServiceTest.java b/src/test/java/com/clova/anifriends/domain/recruitment/service/RecruitmentServiceTest.java index 03856091b..fba2f4ca5 100644 --- a/src/test/java/com/clova/anifriends/domain/recruitment/service/RecruitmentServiceTest.java +++ b/src/test/java/com/clova/anifriends/domain/recruitment/service/RecruitmentServiceTest.java @@ -21,6 +21,7 @@ import com.clova.anifriends.domain.common.PageInfo; import com.clova.anifriends.domain.common.event.ImageDeletionEvent; import com.clova.anifriends.domain.recruitment.Recruitment; +import com.clova.anifriends.domain.recruitment.controller.RecruitmentStatusFilter; import com.clova.anifriends.domain.recruitment.dto.response.FindCompletedRecruitmentsResponse; import com.clova.anifriends.domain.recruitment.dto.response.FindRecruitmentDetailResponse; import com.clova.anifriends.domain.recruitment.dto.response.FindRecruitmentsByShelterIdResponse; @@ -30,7 +31,6 @@ import com.clova.anifriends.domain.recruitment.exception.RecruitmentNotFoundException; import com.clova.anifriends.domain.recruitment.repository.RecruitmentRepository; import com.clova.anifriends.domain.recruitment.support.fixture.RecruitmentFixture; -import com.clova.anifriends.domain.recruitment.controller.RecruitmentStatusFilter; import com.clova.anifriends.domain.shelter.Shelter; import com.clova.anifriends.domain.shelter.exception.ShelterNotFoundException; import com.clova.anifriends.domain.shelter.repository.ShelterRepository; @@ -51,6 +51,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.SliceImpl; import org.springframework.test.util.ReflectionTestUtils; @ExtendWith(MockitoExtension.class) @@ -190,6 +191,61 @@ void findRecruitments() { } } + @Nested + @DisplayName("findRecruitmentsV2 실행 시") + class FindRecruitmentsV2Test { + + @Test + @DisplayName("성공") + void findRecruitments() { + //give + String keyword = "keyword"; + LocalDate startDate = LocalDate.now(); + LocalDate endDate = LocalDate.now(); + String isClosed = "IS_CLOSED"; + boolean title = false; + boolean content = false; + boolean shelterName = false; + LocalDateTime createdAt = LocalDateTime.now(); + Long recruitmentId = 1L; + PageRequest pageRequest = PageRequest.of(0, 10); + Shelter shelter = shelter(); + Recruitment recruitment = recruitment(shelter); + ReflectionTestUtils.setField(recruitment, "recruitmentId", recruitmentId); + SliceImpl recruitments = new SliceImpl<>(List.of(recruitment)); + + given(recruitmentRepository.findRecruitmentsV2(keyword, startDate, endDate, + RecruitmentStatusFilter.valueOf(isClosed).getIsClosed(), + title, content, shelterName, createdAt, recruitmentId, pageRequest)).willReturn( + recruitments); + given(recruitmentRepository.countFindRecruitmentsV2(keyword, startDate, endDate, + RecruitmentStatusFilter.valueOf(isClosed).getIsClosed(), + title, content, shelterName)).willReturn(Long.valueOf(recruitments.getSize())); + + //when + FindRecruitmentsResponse recruitmentsByVolunteer + = recruitmentService.findRecruitmentsV2(keyword, startDate, endDate, + RecruitmentStatusFilter.valueOf(isClosed).getIsClosed(), title, content, + shelterName, createdAt, recruitmentId, pageRequest); + + //then + PageInfo pageInfo = recruitmentsByVolunteer.pageInfo(); + assertThat(pageInfo.totalElements()).isEqualTo(recruitments.getSize()); + FindRecruitmentResponse findRecruitment = recruitmentsByVolunteer.recruitments() + .get(0); + assertThat(findRecruitment.recruitmentTitle()).isEqualTo(recruitment.getTitle()); + assertThat(findRecruitment.recruitmentStartTime()).isEqualTo( + recruitment.getStartTime()); + assertThat(findRecruitment.recruitmentEndTime()).isEqualTo(recruitment.getEndTime()); + assertThat(findRecruitment.recruitmentApplicantCount()).isEqualTo( + recruitment.getApplicantCount()); + assertThat(findRecruitment.recruitmentCapacity()).isEqualTo(recruitment.getCapacity()); + assertThat(findRecruitment.shelterName()).isEqualTo(recruitment.getShelter().getName()); + assertThat(findRecruitment.shelterImageUrl()) + .isEqualTo(recruitment.getShelter().getImage()); + } + } + @Nested @DisplayName("findRecruitmentsByShelter 메서드 실행 시") class FindRecruitmentsByShelterTest {