From bddb5a9b6a426b4f449aa9b37de675fdb2731a1c Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 20 Feb 2024 17:29:54 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EB=B3=84=EB=A1=9C=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EA=B0=9C=EC=88=98=EB=A5=BC=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RunnerPostQueryController.java | 5 ++ .../response/RunnerPostResponse.java | 8 ++ .../repository/RunnerPostQueryRepository.java | 8 ++ .../query/service/RunnerPostQueryService.java | 9 +++ .../baton/assure/common/AssuredSupport.java | 9 +++ .../TestRunnerPostCommandRepository.java | 21 +++++ .../assure/runnerpost/RunnerPostSteps.java | 80 +++++++++++++++++++ .../RunnerPostStatusCountAssruedTest.java | 56 +++++++++++++ .../RunnerPostStatusCountAssuredSupport.java | 61 ++++++++++++++ .../touch/baton/config/AssuredTestConfig.java | 4 + .../baton/config/RepositoryTestConfig.java | 5 ++ .../RunnerPostQueryRepositoryTest.java | 46 +++++++++++ .../service/RunnerPostQueryServiceTest.java | 32 ++++++++ .../fixture/domain/RunnerPostFixture.java | 17 ++++ 14 files changed, 361 insertions(+) create mode 100644 backend/baton/src/test/java/touch/baton/assure/repository/TestRunnerPostCommandRepository.java create mode 100644 backend/baton/src/test/java/touch/baton/assure/runnerpost/RunnerPostSteps.java create mode 100644 backend/baton/src/test/java/touch/baton/assure/runnerpost/query/count/RunnerPostStatusCountAssruedTest.java create mode 100644 backend/baton/src/test/java/touch/baton/assure/runnerpost/support/query/count/RunnerPostStatusCountAssuredSupport.java diff --git a/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/controller/RunnerPostQueryController.java b/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/controller/RunnerPostQueryController.java index 8b2e376cd..27d60f5fe 100644 --- a/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/controller/RunnerPostQueryController.java +++ b/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/controller/RunnerPostQueryController.java @@ -125,4 +125,9 @@ public ResponseEntity countRunnerPostByLoginedRunnerAn final long runnerPostCount = runnerPostQueryService.countRunnerPostByRunnerIdAndReviewStatus(runner.getId(), reviewStatus); return ResponseEntity.ok(RunnerPostResponse.Count.from(runnerPostCount)); } + + @GetMapping("/count") + public ResponseEntity countAllRunnerPostByReviewStatus() { + return ResponseEntity.ok(runnerPostQueryService.countAllRunnerPostByReviewStatus()); + } } diff --git a/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/controller/response/RunnerPostResponse.java b/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/controller/response/RunnerPostResponse.java index b6be711c4..a9af47226 100644 --- a/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/controller/response/RunnerPostResponse.java +++ b/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/controller/response/RunnerPostResponse.java @@ -147,4 +147,12 @@ private static List convertToTags(final RunnerPost runnerPost, final Lis .map(runnerPostTag -> runnerPostTag.getTag().getTagName().getValue()) .toList(); } + + public record StatusCount( + long notStarted, + long inProgress, + long done, + long overdue + ) { + } } diff --git a/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/repository/RunnerPostQueryRepository.java b/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/repository/RunnerPostQueryRepository.java index 962577ca5..1210500e1 100644 --- a/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/repository/RunnerPostQueryRepository.java +++ b/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/repository/RunnerPostQueryRepository.java @@ -54,4 +54,12 @@ select count(1) where rp.supporter.id = :supporterId and rp.reviewStatus = :reviewStatus """) long countBySupporterIdAndReviewStatus(@Param("supporterId") final Long supporterId, @Param("reviewStatus") final ReviewStatus reviewStatus); + + + @Query(""" + select count(1) + from RunnerPost rp + where rp.reviewStatus = :reviewStatus + """) + long countByReviewStatus(@Param("reviewStatus") ReviewStatus reviewStatus); } diff --git a/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/service/RunnerPostQueryService.java b/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/service/RunnerPostQueryService.java index 3210e499b..0f8a035b7 100644 --- a/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/service/RunnerPostQueryService.java +++ b/backend/baton/src/main/java/touch/baton/domain/runnerpost/query/service/RunnerPostQueryService.java @@ -130,6 +130,15 @@ public long countRunnerPostBySupporterIdAndReviewStatus(final Long supporterId, return runnerPostQueryRepository.countBySupporterIdAndReviewStatus(supporterId, reviewStatus); } + public RunnerPostResponse.StatusCount countAllRunnerPostByReviewStatus() { + long notStartedCount = runnerPostQueryRepository.countByReviewStatus(ReviewStatus.NOT_STARTED); + long inProgressCount = runnerPostQueryRepository.countByReviewStatus(ReviewStatus.IN_PROGRESS); + long doneCount = runnerPostQueryRepository.countByReviewStatus(ReviewStatus.DONE); + long overdueCount = runnerPostQueryRepository.countByReviewStatus(ReviewStatus.OVERDUE); + + return new RunnerPostResponse.StatusCount(notStartedCount, inProgressCount, doneCount, overdueCount); + } + @Transactional public void increaseWatchedCount(final RunnerPost runnerPost) { runnerPost.increaseWatchedCount(); diff --git a/backend/baton/src/test/java/touch/baton/assure/common/AssuredSupport.java b/backend/baton/src/test/java/touch/baton/assure/common/AssuredSupport.java index edcfe5ec3..cb2698edb 100644 --- a/backend/baton/src/test/java/touch/baton/assure/common/AssuredSupport.java +++ b/backend/baton/src/test/java/touch/baton/assure/common/AssuredSupport.java @@ -78,6 +78,15 @@ public static ExtractableResponse post(final String uri, .extract(); } + public static ExtractableResponse get(final String uri) { + return RestAssured + .given().log().ifValidationFails() + .when().log().ifValidationFails() + .get(uri) + .then().log().ifError() + .extract(); + } + public static ExtractableResponse get(final String uri, final PathParams pathParams) { return RestAssured .given().log().ifValidationFails() diff --git a/backend/baton/src/test/java/touch/baton/assure/repository/TestRunnerPostCommandRepository.java b/backend/baton/src/test/java/touch/baton/assure/repository/TestRunnerPostCommandRepository.java new file mode 100644 index 000000000..3e908d7ff --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/assure/repository/TestRunnerPostCommandRepository.java @@ -0,0 +1,21 @@ +package touch.baton.assure.repository; + +import org.springframework.context.annotation.Profile; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import touch.baton.domain.runnerpost.command.repository.RunnerPostCommandRepository; + +@Profile("test") +public interface TestRunnerPostCommandRepository extends RunnerPostCommandRepository { + + @Transactional + @Modifying(flushAutomatically = true, clearAutomatically = true) + @Query(""" + update RunnerPost rp + set rp.reviewStatus = 'OVERDUE' + where rp.id = :id + """) + void expireRunnerPost(@Param("id") Long runnerPostId); +} diff --git a/backend/baton/src/test/java/touch/baton/assure/runnerpost/RunnerPostSteps.java b/backend/baton/src/test/java/touch/baton/assure/runnerpost/RunnerPostSteps.java new file mode 100644 index 000000000..72e76771c --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/assure/runnerpost/RunnerPostSteps.java @@ -0,0 +1,80 @@ +package touch.baton.assure.runnerpost; + +import org.springframework.http.HttpStatus; +import touch.baton.assure.common.HttpStatusAndLocationHeader; +import touch.baton.assure.runnerpost.support.command.RunnerPostCreateSupport; +import touch.baton.assure.runnerpost.support.command.RunnerPostUpdateSupport; +import touch.baton.assure.runnerpost.support.command.applicant.RunnerPostApplicantCreateSupport; +import touch.baton.domain.member.command.Supporter; + +import java.time.LocalDateTime; +import java.util.List; + +import static touch.baton.assure.runnerpost.support.command.RunnerPostCreateSupport.러너_게시글_생성_요청; +import static touch.baton.assure.runnerpost.support.command.applicant.RunnerPostApplicantCreateSupport.러너의_서포터_선택_요청; + +public abstract class RunnerPostSteps { + + public static void 러너가_게시글을_작성하고_리뷰를_받은_뒤_리뷰완료로_변경한다(final String 러너_액세스_토큰, final String 서포터_액세스_토큰, final Supporter 서포터) { + final Long 게시글_식별자 = 러너_게시글을_생성하고_게시을글_식별자를_반환한다(러너_액세스_토큰); + 서포터가_러너_게시글에_리뷰_신청을_성공한다(서포터_액세스_토큰, 게시글_식별자); + 러너가_서포터의_리뷰_신청_선택에_성공한다(서포터, 러너_액세스_토큰, 게시글_식별자); + 서포터가_러너_게시글의_리뷰를_완료로_변경하는_것을_성공한다(서포터_액세스_토큰, 게시글_식별자); + } + + public static void 러너가_게시글을_작성하고_서포터가_선택된다(final String 러너_액세스_토큰, final String 서포터_액세스_토큰, final Supporter 서포터) { + final Long 게시글_식별자 = 러너_게시글을_생성하고_게시을글_식별자를_반환한다(러너_액세스_토큰); + 서포터가_러너_게시글에_리뷰_신청을_성공한다(서포터_액세스_토큰, 게시글_식별자); + 러너가_서포터의_리뷰_신청_선택에_성공한다(서포터, 러너_액세스_토큰, 게시글_식별자); + } + + public static Long 러너_게시글을_생성하고_게시을글_식별자를_반환한다(final String 러너_엑세스_토큰) { + return RunnerPostCreateSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(러너_엑세스_토큰) + .러너_게시글_등록_요청한다( + 러너_게시글_생성_요청( + "테스트용_러너_게시글_제목", + List.of("자바", "스프링"), + "https://test-pull-request.com", + LocalDateTime.now().plusHours(100), + "테스트용_러너_게시글_구현_내용", + "테스트용_러너_게시글_궁금한_내용", + "테스트용_러너_게시글_참고_사항" + )) + + .서버_응답() + .러너_게시글_생성_성공을_검증한다() + .생성한_러너_게시글의_식별자값을_반환한다(); + } + + public static void 서포터가_러너_게시글에_리뷰_신청을_성공한다(final String 서포터_액세스_토큰, final Long 러너_게시글_식별자값) { + RunnerPostApplicantCreateSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(서포터_액세스_토큰) + .서포터가_러너_게시글에_리뷰를_신청한다(러너_게시글_식별자값, "안녕하세요. 서포터입니다.") + + .서버_응답() + .서포터가_러너_게시글에_리뷰_신청_성공을_검증한다(러너_게시글_식별자값); + } + + public static void 러너가_서포터의_리뷰_신청_선택에_성공한다(final Supporter 서포터, final String 러너_액세스_토큰, final Long 러너_게시글_식별자값) { + RunnerPostUpdateSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(러너_액세스_토큰) + .러너가_서포터를_선택한다(러너_게시글_식별자값, 러너의_서포터_선택_요청(서포터.getId())) + + .서버_응답() + .러너_게시글에_서포터가_성공적으로_선택되었는지_확인한다(new HttpStatusAndLocationHeader(HttpStatus.NO_CONTENT, "/api/v1/posts/runner")); + } + + public static void 서포터가_러너_게시글의_리뷰를_완료로_변경하는_것을_성공한다(final String 서포터_액세스_토큰, final Long 러너_게시글_식별자값) { + RunnerPostUpdateSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(서포터_액세스_토큰) + .서포터가_리뷰를_완료하고_리뷰완료_버튼을_누른다(러너_게시글_식별자값) + + .서버_응답() + .러너_게시글이_성공적으로_리뷰_완료_상태인지_확인한다(new HttpStatusAndLocationHeader(HttpStatus.NO_CONTENT, "/api/v1/posts/runner")); + } +} diff --git a/backend/baton/src/test/java/touch/baton/assure/runnerpost/query/count/RunnerPostStatusCountAssruedTest.java b/backend/baton/src/test/java/touch/baton/assure/runnerpost/query/count/RunnerPostStatusCountAssruedTest.java new file mode 100644 index 000000000..8770d8429 --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/assure/runnerpost/query/count/RunnerPostStatusCountAssruedTest.java @@ -0,0 +1,56 @@ +package touch.baton.assure.runnerpost.query.count; + +import org.junit.jupiter.api.Test; +import touch.baton.assure.runnerpost.support.query.count.RunnerPostStatusCountAssuredSupport; +import touch.baton.config.AssuredTestConfig; +import touch.baton.config.infra.auth.oauth.authcode.FakeAuthCodes; +import touch.baton.domain.member.command.Supporter; +import touch.baton.domain.member.command.vo.SocialId; + +import static touch.baton.assure.runnerpost.RunnerPostSteps.러너_게시글을_생성하고_게시을글_식별자를_반환한다; +import static touch.baton.assure.runnerpost.RunnerPostSteps.러너가_게시글을_작성하고_리뷰를_받은_뒤_리뷰완료로_변경한다; +import static touch.baton.assure.runnerpost.RunnerPostSteps.러너가_게시글을_작성하고_서포터가_선택된다; + +@SuppressWarnings("NonAsciiCharacters") +class RunnerPostStatusCountAssruedTest extends AssuredTestConfig { + + @Test + void 리뷰_상태_별_게시글의_총_개수를_조회한다() { + // given + final String 러너_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.hyenaAuthCode()); + final String 서포터_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.ethanAuthCode()); + final SocialId 서포터_소셜_아이디 = jwtTestManager.parseToSocialId(서포터_액세스_토큰); + final Supporter 서포터 = supporterRepository.getBySocialId(서포터_소셜_아이디); + + final Long 리뷰_진행중_글_개수 = 1L; + 러너가_게시글을_작성하고_서포터가_선택된다(러너_액세스_토큰, 서포터_액세스_토큰, 서포터); + + final Long 리뷰_완료_글_개수 = 2L; + 러너가_게시글을_작성하고_리뷰를_받은_뒤_리뷰완료로_변경한다(러너_액세스_토큰, 서포터_액세스_토큰, 서포터); + 러너가_게시글을_작성하고_리뷰를_받은_뒤_리뷰완료로_변경한다(러너_액세스_토큰, 서포터_액세스_토큰, 서포터); + + final Long 리뷰_대기중_글_개수 = 3L; + 러너_게시글을_생성하고_게시을글_식별자를_반환한다(러너_액세스_토큰); + 러너_게시글을_생성하고_게시을글_식별자를_반환한다(러너_액세스_토큰); + 러너_게시글을_생성하고_게시을글_식별자를_반환한다(러너_액세스_토큰); + + final Long 기간_만료_글_개수 = 4L; + 러너_게시글을_생성하고_마감기한이_지나_마감된다(러너_액세스_토큰); + 러너_게시글을_생성하고_마감기한이_지나_마감된다(러너_액세스_토큰); + 러너_게시글을_생성하고_마감기한이_지나_마감된다(러너_액세스_토큰); + 러너_게시글을_생성하고_마감기한이_지나_마감된다(러너_액세스_토큰); + + // when, then + RunnerPostStatusCountAssuredSupport + .클라이언트_요청() + .게시글의_상태별로_게시글_총_수를_반환한다() + + .서버_응답() + .게시글_상태별_게시글_총_수_반환_성공을_확인한다(리뷰_대기중_글_개수, 리뷰_진행중_글_개수, 리뷰_완료_글_개수, 기간_만료_글_개수); + } + + private void 러너_게시글을_생성하고_마감기한이_지나_마감된다(final String 러너_액세스_토큰) { + final Long 만료된_게시글 = 러너_게시글을_생성하고_게시을글_식별자를_반환한다(러너_액세스_토큰); + runnerPostCommandRepository.expireRunnerPost(만료된_게시글); + } +} diff --git a/backend/baton/src/test/java/touch/baton/assure/runnerpost/support/query/count/RunnerPostStatusCountAssuredSupport.java b/backend/baton/src/test/java/touch/baton/assure/runnerpost/support/query/count/RunnerPostStatusCountAssuredSupport.java new file mode 100644 index 000000000..ab744592e --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/assure/runnerpost/support/query/count/RunnerPostStatusCountAssuredSupport.java @@ -0,0 +1,61 @@ +package touch.baton.assure.runnerpost.support.query.count; + +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.HttpStatus; +import touch.baton.assure.common.AssuredSupport; +import touch.baton.domain.runnerpost.query.controller.response.RunnerPostResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +@SuppressWarnings("NonAsciiCharacters") +public class RunnerPostStatusCountAssuredSupport { + + private RunnerPostStatusCountAssuredSupport() { + } + + public static RunnerPostStatusCountBuilder 클라이언트_요청() { + return new RunnerPostStatusCountBuilder(); + } + + public static class RunnerPostStatusCountBuilder { + + private ExtractableResponse response; + + public RunnerPostStatusCountBuilder 게시글의_상태별로_게시글_총_수를_반환한다() { + response = AssuredSupport.get("/api/v1/posts/runner/count"); + return this; + } + + public RunnerPostStatusCountResponseBuilder 서버_응답() { + return new RunnerPostStatusCountResponseBuilder(response); + } + + public class RunnerPostStatusCountResponseBuilder { + + private final ExtractableResponse response; + + public RunnerPostStatusCountResponseBuilder(final ExtractableResponse 응답) { + this.response = 응답; + } + + public void 게시글_상태별_게시글_총_수_반환_성공을_확인한다(final Long 리뷰_대기중_글_개수, + final Long 리뷰_진행중_글_개수, + final Long 리뷰_완료_글_개수, + final Long 기간_만료_글_개수 + ) { + final RunnerPostResponse.StatusCount actual = response.as(RunnerPostResponse.StatusCount.class); + + assertAll( + () -> assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()), + () -> assertThat(actual.notStarted()).isEqualTo(리뷰_대기중_글_개수), + () -> assertThat(actual.inProgress()).isEqualTo(리뷰_진행중_글_개수), + () -> assertThat(actual.done()).isEqualTo(리뷰_완료_글_개수), + () -> assertThat(actual.overdue()).isEqualTo(기간_만료_글_개수) + ); + } + } + } + +} diff --git a/backend/baton/src/test/java/touch/baton/config/AssuredTestConfig.java b/backend/baton/src/test/java/touch/baton/config/AssuredTestConfig.java index 03f26b996..8c1be51d6 100644 --- a/backend/baton/src/test/java/touch/baton/config/AssuredTestConfig.java +++ b/backend/baton/src/test/java/touch/baton/config/AssuredTestConfig.java @@ -17,6 +17,7 @@ import touch.baton.assure.repository.TestNotificationCommandRepository; import touch.baton.assure.repository.TestRankQueryRepository; import touch.baton.assure.repository.TestRefreshTokenRepository; +import touch.baton.assure.repository.TestRunnerPostCommandRepository; import touch.baton.assure.repository.TestRunnerPostQueryRepository; import touch.baton.assure.repository.TestRunnerQueryRepository; import touch.baton.assure.repository.TestSupporterQueryRepository; @@ -41,6 +42,9 @@ public abstract class AssuredTestConfig { @Autowired protected TestRunnerQueryRepository runnerRepository; + @Autowired + protected TestRunnerPostCommandRepository runnerPostCommandRepository; + @Autowired protected TestSupporterQueryRepository supporterRepository; diff --git a/backend/baton/src/test/java/touch/baton/config/RepositoryTestConfig.java b/backend/baton/src/test/java/touch/baton/config/RepositoryTestConfig.java index acf8a239d..34991e020 100644 --- a/backend/baton/src/test/java/touch/baton/config/RepositoryTestConfig.java +++ b/backend/baton/src/test/java/touch/baton/config/RepositoryTestConfig.java @@ -69,6 +69,11 @@ protected Supporter persistSupporter(final ReviewCount reviewCount, final Member return supporter; } + protected RunnerPost persistRunnerPost(final RunnerPost runnerPost) { + em.persist(runnerPost); + return runnerPost; + } + protected RunnerPost persistRunnerPost(final Runner runner) { final RunnerPost runnerPost = RunnerPostFixture.create(runner, deadline(LocalDateTime.now().plusHours(100))); em.persist(runnerPost); diff --git a/backend/baton/src/test/java/touch/baton/domain/runnerpost/query/repository/RunnerPostQueryRepositoryTest.java b/backend/baton/src/test/java/touch/baton/domain/runnerpost/query/repository/RunnerPostQueryRepositoryTest.java index f645ad6ba..b394b4aa7 100644 --- a/backend/baton/src/test/java/touch/baton/domain/runnerpost/query/repository/RunnerPostQueryRepositoryTest.java +++ b/backend/baton/src/test/java/touch/baton/domain/runnerpost/query/repository/RunnerPostQueryRepositoryTest.java @@ -2,18 +2,24 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import touch.baton.config.RepositoryTestConfig; +import touch.baton.domain.member.command.Member; import touch.baton.domain.member.command.Runner; import touch.baton.domain.member.command.Supporter; import touch.baton.domain.runnerpost.command.RunnerPost; import touch.baton.domain.runnerpost.command.repository.dto.RunnerPostApplicantCountDto; import touch.baton.domain.runnerpost.command.vo.ReviewStatus; import touch.baton.fixture.domain.MemberFixture; +import touch.baton.fixture.domain.RunnerPostFixture; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.SoftAssertions.assertSoftly; @@ -194,4 +200,44 @@ void countBySupporterIdAndReviewStatus() { // then assertThat(actual.intValue()).isEqualTo(expected); } + + @DisplayName("전체 글에서 리뷰 상태별로 게시글 개수를 조회할 수 있다.") + @MethodSource("provideCountByReviewStatus") + @ParameterizedTest + void countByReviewStatus(final Long notStartedCount, + final Long inProgressCount, + final Long doneCount, + final Long overdueCount, + final ReviewStatus targetReviewStatus, + final Long expected + ) { + // given + final Member member = persistMember(MemberFixture.createEthan()); + final Runner runner = persistRunner(member); + + for (int i = 0; i < notStartedCount; i++) { + persistRunnerPost(RunnerPostFixture.create(runner, ReviewStatus.NOT_STARTED)); + } + for (int i = 0; i < inProgressCount; i++) { + persistRunnerPost(RunnerPostFixture.create(runner, ReviewStatus.IN_PROGRESS)); + } + for (int i = 0; i < doneCount; i++) { + persistRunnerPost(RunnerPostFixture.create(runner, ReviewStatus.DONE)); + } + for (int i = 0; i < overdueCount; i++) { + persistRunnerPost(RunnerPostFixture.create(runner, ReviewStatus.OVERDUE)); + } + + // when, then + assertThat(runnerPostQueryRepository.countByReviewStatus(targetReviewStatus)).isEqualTo(expected); + } + + private static Stream provideCountByReviewStatus() { + return Stream.of( + Arguments.of(4L, 3L, 2L, 1L, ReviewStatus.NOT_STARTED, 4L), + Arguments.of(4L, 3L, 2L, 1L, ReviewStatus.IN_PROGRESS, 3L), + Arguments.of(4L, 3L, 2L, 1L, ReviewStatus.DONE, 2L), + Arguments.of(4L, 3L, 2L, 1L, ReviewStatus.OVERDUE, 1L) + ); + } } diff --git a/backend/baton/src/test/java/touch/baton/domain/runnerpost/query/service/RunnerPostQueryServiceTest.java b/backend/baton/src/test/java/touch/baton/domain/runnerpost/query/service/RunnerPostQueryServiceTest.java index a848a283a..eed943261 100644 --- a/backend/baton/src/test/java/touch/baton/domain/runnerpost/query/service/RunnerPostQueryServiceTest.java +++ b/backend/baton/src/test/java/touch/baton/domain/runnerpost/query/service/RunnerPostQueryServiceTest.java @@ -871,4 +871,36 @@ void countRunnerPostBySupporterIdAndReviewStatus_except_NOT_STARTED() { // then assertThat(actual).isEqualTo(expected); } + + @DisplayName("전체 글에서 리뷰 상태별로 게시글 개수를 조회할 수 있다.") + @Test + void countByReviewStatus( + ) { + // given + final Member member = persistMember(MemberFixture.createEthan()); + final Runner runner = persistRunner(member); + + final int notStartedCount = 4; + for (int i = 0; i < notStartedCount; i++) { + persistRunnerPost(RunnerPostFixture.create(runner, ReviewStatus.NOT_STARTED)); + } + final int inProgressCount = 3; + for (int i = 0; i < inProgressCount; i++) { + persistRunnerPost(RunnerPostFixture.create(runner, ReviewStatus.IN_PROGRESS)); + } + final int doneCount = 2; + for (int i = 0; i < doneCount; i++) { + persistRunnerPost(RunnerPostFixture.create(runner, ReviewStatus.DONE)); + } + final int overdueCount = 1; + for (int i = 0; i < overdueCount; i++) { + persistRunnerPost(RunnerPostFixture.create(runner, ReviewStatus.OVERDUE)); + } + + // when + final RunnerPostResponse.StatusCount actual = runnerPostQueryService.countAllRunnerPostByReviewStatus(); + + // then + assertThat(actual).isEqualTo(new RunnerPostResponse.StatusCount(notStartedCount, inProgressCount, doneCount, overdueCount)); + } } diff --git a/backend/baton/src/test/java/touch/baton/fixture/domain/RunnerPostFixture.java b/backend/baton/src/test/java/touch/baton/fixture/domain/RunnerPostFixture.java index 59d4cd25f..800625bad 100644 --- a/backend/baton/src/test/java/touch/baton/fixture/domain/RunnerPostFixture.java +++ b/backend/baton/src/test/java/touch/baton/fixture/domain/RunnerPostFixture.java @@ -72,6 +72,23 @@ public static RunnerPost create(final Runner runner, final Deadline deadline) { .build(); } + public static RunnerPost create(final Runner runner, final ReviewStatus reviewStatus) { + return RunnerPost.builder() + .title(new Title("테스트 제목")) + .implementedContents(new ImplementedContents("테스트 내용")) + .curiousContents(new CuriousContents("테스트 궁금 점")) + .postscriptContents(new PostscriptContents("테스트 참고 사항")) + .pullRequestUrl(new PullRequestUrl("https://테스트")) + .deadline(new Deadline(LocalDateTime.now().plusHours(100))) + .watchedCount(new WatchedCount(0)) + .reviewStatus(reviewStatus) + .isReviewed(IsReviewed.notReviewed()) + .runner(runner) + .supporter(null) + .runnerPostTags(new RunnerPostTags(new ArrayList<>())) + .build(); + } + public static RunnerPost create(final Runner runner, final Deadline deadline, final ReviewStatus reviewStatus, From c841f6663280e7dba4a2dc50838c440c8e171f67 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 20 Feb 2024 17:49:49 +0900 Subject: [PATCH 2/2] =?UTF-8?q?test:=20Restdocs=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/docs/asciidoc/RunnerPostReadApi.adoc | 14 ++++++++ .../read/RunnerPostStatusCountApiTest.java | 36 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostStatusCountApiTest.java diff --git a/backend/baton/src/docs/asciidoc/RunnerPostReadApi.adoc b/backend/baton/src/docs/asciidoc/RunnerPostReadApi.adoc index 6a427755c..ccafaddd9 100644 --- a/backend/baton/src/docs/asciidoc/RunnerPostReadApi.adoc +++ b/backend/baton/src/docs/asciidoc/RunnerPostReadApi.adoc @@ -209,3 +209,17 @@ include::{snippets}/../../build/generated-snippets/tag-read-api-test/read-tags-b ===== *Http Response Fields* include::{snippets}/../../build/generated-snippets/tag-read-api-test/read-tags-by-reduced-name/response-fields.adoc[] + +==== *러너 게시글 상태별 총 개수 조회 API* + +===== *Http Request* + +include::{snippets}/../../build/generated-snippets/runner-post-status-count-api-test/count-all-runner-post-by-review-status/http-request.adoc[] + +===== *Http Response* + +include::{snippets}/../../build/generated-snippets/runner-post-status-count-api-test/count-all-runner-post-by-review-status/http-response.adoc[] + +===== *Http Response Fields* + +include::{snippets}/../../build/generated-snippets/runner-post-status-count-api-test/count-all-runner-post-by-review-status/response-fields.adoc[] diff --git a/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostStatusCountApiTest.java b/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostStatusCountApiTest.java new file mode 100644 index 000000000..0a4ba7ccd --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostStatusCountApiTest.java @@ -0,0 +1,36 @@ +package touch.baton.document.runnerpost.read; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.restdocs.payload.JsonFieldType; +import touch.baton.config.RestdocsConfig; +import touch.baton.domain.runnerpost.query.controller.response.RunnerPostResponse; + +import static org.mockito.BDDMockito.when; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class RunnerPostStatusCountApiTest extends RestdocsConfig { + + @DisplayName("게시글 상태 별 총 개수 반환 API 구현") + @Test + void countAllRunnerPostByReviewStatus() throws Exception { + // when + when(runnerPostQueryService.countAllRunnerPostByReviewStatus()) + .thenReturn(new RunnerPostResponse.StatusCount(1L, 2L, 3L, 4L)); + + // then + mockMvc.perform(get("/api/v1/posts/runner/count")) + .andExpect(status().isOk()) + .andDo(restDocs.document( + responseFields( + fieldWithPath("notStarted").type(JsonFieldType.NUMBER).description("리뷰_대기중_게시글_총_수"), + fieldWithPath("inProgress").type(JsonFieldType.NUMBER).description("리뷰_진행중_게시글_총_수"), + fieldWithPath("done").type(JsonFieldType.NUMBER).description("리뷰_완료_게시글_총_수"), + fieldWithPath("overdue").type(JsonFieldType.NUMBER).description("기간_만료_게시글_총_수") + )) + ); + } +}