diff --git a/backend/baton/src/docs/asciidoc/RunnerPostReadApi.adoc b/backend/baton/src/docs/asciidoc/RunnerPostReadApi.adoc index 301113d89..6a427755c 100644 --- a/backend/baton/src/docs/asciidoc/RunnerPostReadApi.adoc +++ b/backend/baton/src/docs/asciidoc/RunnerPostReadApi.adoc @@ -54,6 +54,28 @@ include::{snippets}/../../build/generated-snippets/runner-post-read-with-logined include::{snippets}/../../build/generated-snippets/runner-post-read-with-logined-runner-api-test/read-runner-post-by-logined-runner-and-review-status/response-fields.adoc[] +==== *러너 마이페이지 게시글 개수 조회 API* + +===== *Http Request* + +include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-runner-api-test/count-runner-post-by-logined-runner-and-review-status/http-request.adoc[] + +===== *Http Request Headers* + +include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-runner-api-test/count-runner-post-by-logined-runner-and-review-status/request-headers.adoc[] + +===== *Http Request Query Parameters* + +include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-runner-api-test/count-runner-post-by-logined-runner-and-review-status/query-parameters.adoc[] + +===== *Http Response* + +include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-runner-api-test/count-runner-post-by-logined-runner-and-review-status/http-response.adoc[] + +===== *Http Response Fields* + +include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-runner-api-test/count-runner-post-by-logined-runner-and-review-status/response-fields.adoc[] + ==== *리뷰 지원한 서포터 조회 API* ===== *Http Request* @@ -94,7 +116,29 @@ include::{snippets}/../../build/generated-snippets/runner-post-read-with-logined include::{snippets}/../../build/generated-snippets/runner-post-read-with-logined-supporter-api-test/read-runner-post-by-logined-supporter-and-review-status/response-fields.adoc[] -==== *서포터 리뷰 완료한 게시글 조회 API* +==== *서포터 마이페이지 게시글 개수 조회 API* + +===== *Http Request* + +include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-supporter-api-test/count-runner-post-by-logined-supporter-and-review-status/http-request.adoc[] + +===== *Http Request Headers* + +include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-supporter-api-test/count-runner-post-by-logined-supporter-and-review-status/request-headers.adoc[] + +===== *Http Request Query Parameters* + +include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-supporter-api-test/count-runner-post-by-logined-supporter-and-review-status/query-parameters.adoc[] + +===== *Http Response* + +include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-supporter-api-test/count-runner-post-by-logined-supporter-and-review-status/http-response.adoc[] + +===== *Http Response Fields* + +include::{snippets}/../../build/generated-snippets/runner-post-count-with-logined-supporter-api-test/count-runner-post-by-logined-supporter-and-review-status/response-fields.adoc[] + +==== *서포터의 리뷰 완료한 게시글 조회 API* ===== *Http Request* @@ -112,6 +156,24 @@ include::{snippets}/../../build/generated-snippets/runner-post-read-of-supporter include::{snippets}/../../build/generated-snippets/runner-post-read-of-supporter-by-guest-api-test/read-runner-post-by-supporter-id-and-review-status/response-fields.adoc[] +==== *서포터의 리뷰 완료한 게시글 개수 조회 API* + +===== *Http Request* + +include::{snippets}/../../build/generated-snippets/runner-post-count-of-supporter-by-guest-api-test/count-runner-post-by-supporter-id-and-review-status/http-request.adoc[] + +===== *Http Request Query Parameters* + +include::{snippets}/../../build/generated-snippets/runner-post-count-of-supporter-by-guest-api-test/count-runner-post-by-supporter-id-and-review-status/query-parameters.adoc[] + +===== *Http Response* + +include::{snippets}/../../build/generated-snippets/runner-post-count-of-supporter-by-guest-api-test/count-runner-post-by-supporter-id-and-review-status/http-response.adoc[] + +===== *Http Response Fields* + +include::{snippets}/../../build/generated-snippets/runner-post-count-of-supporter-by-guest-api-test/count-runner-post-by-supporter-id-and-review-status/response-fields.adoc[] + ==== *태그 이름과 리뷰 상태를 조건으로 러너 게시글 페이징 조회 API* ===== *Http Request* diff --git a/backend/baton/src/main/java/touch/baton/domain/member/command/repository/SupporterRunnerPostCommandRepository.java b/backend/baton/src/main/java/touch/baton/domain/member/command/repository/SupporterRunnerPostCommandRepository.java index 1306d5a21..5400c2d9b 100644 --- a/backend/baton/src/main/java/touch/baton/domain/member/command/repository/SupporterRunnerPostCommandRepository.java +++ b/backend/baton/src/main/java/touch/baton/domain/member/command/repository/SupporterRunnerPostCommandRepository.java @@ -1,22 +1,10 @@ package touch.baton.domain.member.command.repository; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; import touch.baton.domain.member.command.SupporterRunnerPost; -import java.util.Optional; - public interface SupporterRunnerPostCommandRepository extends JpaRepository { - @Query(""" - select count(1) - from SupporterRunnerPost srp - group by srp.runnerPost.id - having srp.runnerPost.id = :runnerPostId - """) - Optional countByRunnerPostId(@Param("runnerPostId") final Long runnerPostId); - boolean existsByRunnerPostId(final Long runnerPostId); boolean existsByRunnerPostIdAndSupporterId(final Long runnerPostId, final Long supporterId); diff --git a/backend/baton/src/main/java/touch/baton/domain/member/query/repository/SupporterRunnerPostQueryRepository.java b/backend/baton/src/main/java/touch/baton/domain/member/query/repository/SupporterRunnerPostQueryRepository.java index 8daa47299..171f19605 100644 --- a/backend/baton/src/main/java/touch/baton/domain/member/query/repository/SupporterRunnerPostQueryRepository.java +++ b/backend/baton/src/main/java/touch/baton/domain/member/query/repository/SupporterRunnerPostQueryRepository.java @@ -10,14 +10,6 @@ public interface SupporterRunnerPostQueryRepository extends JpaRepository { - @Query(""" - select count(1) - from SupporterRunnerPost srp - group by srp.runnerPost.id - having srp.runnerPost.id in (:runnerPostIds) - """) - List countByRunnerPostIdIn(@Param("runnerPostIds") final List runnerPostIds); - @Query(""" select count(1) from SupporterRunnerPost srp @@ -26,20 +18,6 @@ select count(1) """) Optional countByRunnerPostId(@Param("runnerPostId") final Long runnerPostId); - @Query(""" - select case when exists ( - select 1 from SupporterRunnerPost srp - where srp.runnerPost.id = rp.id) - then ( - select count(srp.id) from SupporterRunnerPost srp - where srp.runnerPost.id = rp.id - ) else 0 end - from RunnerPost rp - where rp.id in :runnerPostIds - order by rp.id desc - """) - List countByRunnerPostIds(@Param("runnerPostIds") final List runnerPostIds); - @Query(""" select (count(1) >= 1) from SupporterRunnerPost srp @@ -49,11 +27,13 @@ select count(srp.id) from SupporterRunnerPost srp """) boolean existsByRunnerPostIdAndMemberId(@Param("runnerPostId") final Long runnerPostId, @Param("memberId") final Long memberId); - boolean existsByRunnerPostId(final Long runnerPostId); - - void deleteBySupporterIdAndRunnerPostId(final Long supporterId, final Long runnerPostId); - - boolean existsByRunnerPostIdAndSupporterId(final Long runnerPostId, final Long supporterId); - List readByRunnerPostId(final Long runnerPostId); + + @Query(""" + select count(1) + from SupporterRunnerPost srp + join fetch RunnerPost rp on rp.id = srp.runnerPost.id + where rp.reviewStatus = 'NOT_STARTED' and srp.supporter.id = :supporterId + """) + long countRunnerPostBySupporterIdByReviewStatusNotStarted(@Param("supporterId") final Long supporterId); } diff --git a/backend/baton/src/main/java/touch/baton/domain/runnerpost/command/service/RunnerPostCommandService.java b/backend/baton/src/main/java/touch/baton/domain/runnerpost/command/service/RunnerPostCommandService.java index 10a7c7873..6d6d2a292 100644 --- a/backend/baton/src/main/java/touch/baton/domain/runnerpost/command/service/RunnerPostCommandService.java +++ b/backend/baton/src/main/java/touch/baton/domain/runnerpost/command/service/RunnerPostCommandService.java @@ -107,8 +107,7 @@ public Long createRunnerPostApplicant(final Supporter supporter, final Long runnerPostId ) { final RunnerPost foundRunnerPost = getRunnerPostOrThrowException(runnerPostId); - final boolean isApplicantHistoryExist = supporterRunnerPostCommandRepository.existsByRunnerPostIdAndSupporterId(runnerPostId, supporter.getId()); - if (isApplicantHistoryExist) { + if (isApplySupporter(foundRunnerPost, supporter)) { throw new RunnerPostBusinessException("Supporter 는 이미 해당 RunnerPost 에 리뷰 신청을 한 이력이 있습니다."); } @@ -160,7 +159,7 @@ public void updateRunnerPostAppliedSupporter(final Runner runner, final RunnerPost foundRunnerPost = runnerPostCommandRepository.findById(runnerPostId) .orElseThrow(() -> new RunnerPostBusinessException("RunnerPost 의 식별자값으로 러너 게시글을 조회할 수 없습니다.")); - if (isApplySupporter(runnerPostId, foundApplySupporter)) { + if (!isApplySupporter(foundRunnerPost, foundApplySupporter)) { throw new RunnerPostBusinessException("게시글에 리뷰를 제안한 서포터가 아닙니다."); } if (foundRunnerPost.isNotOwner(runner)) { @@ -172,7 +171,7 @@ public void updateRunnerPostAppliedSupporter(final Runner runner, eventPublisher.publishEvent(new RunnerPostAssignSupporterEvent(foundRunnerPost.getId())); } - private boolean isApplySupporter(final Long runnerPostId, final Supporter foundSupporter) { - return !supporterRunnerPostCommandRepository.existsByRunnerPostIdAndSupporterId(runnerPostId, foundSupporter.getId()); + private boolean isApplySupporter(final RunnerPost runnerPost, final Supporter supporter) { + return supporterRunnerPostCommandRepository.existsByRunnerPostIdAndSupporterId(runnerPost.getId(), supporter.getId()); } } 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 de7021365..8b2e376cd 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 @@ -48,7 +48,7 @@ public ResponseEntity readByRunnerPostId( @PathVariable final Long runnerPostId ) { final RunnerPost foundRunnerPost = runnerPostQueryService.readByRunnerPostId(runnerPostId); - final long applicantCount = runnerPostQueryService.readCountByRunnerPostId(foundRunnerPost.getId()); + final long applicantCount = runnerPostQueryService.countApplicantsByRunnerPostId(foundRunnerPost.getId()); final boolean isApplicantHistoryExist = runnerPostQueryService.existsRunnerPostApplicantByRunnerPostIdAndMemberId(runnerPostId, member.getId()); runnerPostQueryService.increaseWatchedCount(foundRunnerPost); @@ -70,6 +70,14 @@ public ResponseEntity> readRunnerPostByS return ResponseEntity.ok(runnerPostQueryService.pageRunnerPostBySupporterIdAndReviewStatus(pageParams, supporterId, ReviewStatus.DONE)); } + @GetMapping("/search/count") + public ResponseEntity countRunnerPostBySupporterIdAndReviewStatusDone( + @RequestParam final Long supporterId + ) { + final long count = runnerPostQueryService.countRunnerPostBySupporterIdAndReviewStatus(supporterId, ReviewStatus.DONE); + return ResponseEntity.ok(RunnerPostResponse.Count.from(count)); + } + @GetMapping("/{runnerPostId}/supporters") public ResponseEntity readSupporterRunnerPostsByRunnerPostId( @AuthRunnerPrincipal final Runner runner, @@ -91,11 +99,30 @@ public ResponseEntity> readRunnerPostByL return ResponseEntity.ok(runnerPostQueryService.pageRunnerPostBySupporterIdAndReviewStatus(pageParams, supporter.getId(), reviewStatus)); } - @GetMapping("/me/runner") public ResponseEntity> readRunnerPostByLoginedRunnerAndReviewStatus( + @GetMapping("/me/runner") + public ResponseEntity> readRunnerPostByLoginedRunnerAndReviewStatus( @AuthRunnerPrincipal final Runner runner, @Valid @ModelAttribute final PageParams pageParams, @RequestParam(required = false) final ReviewStatus reviewStatus ) { return ResponseEntity.ok(runnerPostQueryService.pageRunnerPostByRunnerIdAndReviewStatus(pageParams, runner.getId(), reviewStatus)); } + + @GetMapping("/me/supporter/count") + public ResponseEntity countRunnerPostByLoginedSupporterAndReviewStatus( + @AuthSupporterPrincipal final Supporter supporter, + @RequestParam final ReviewStatus reviewStatus + ) { + final long runnerPostCount = runnerPostQueryService.countRunnerPostBySupporterIdAndReviewStatus(supporter.getId(), reviewStatus); + return ResponseEntity.ok(RunnerPostResponse.Count.from(runnerPostCount)); + } + + @GetMapping("/me/runner/count") + public ResponseEntity countRunnerPostByLoginedRunnerAndReviewStatus( + @AuthRunnerPrincipal final Runner runner, + @RequestParam final ReviewStatus reviewStatus + ) { + final long runnerPostCount = runnerPostQueryService.countRunnerPostByRunnerIdAndReviewStatus(runner.getId(), reviewStatus); + return ResponseEntity.ok(RunnerPostResponse.Count.from(runnerPostCount)); + } } 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 f3dfadbbf..b6be711c4 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 @@ -126,6 +126,13 @@ public Long extractId() { } } + public record Count(Long count) { + + public static Count from(final long count) { + return new Count(count); + } + } + private static List convertToTags(final RunnerPost runnerPost) { return runnerPost.getRunnerPostTags() .getRunnerPostTags() 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 caf078fb6..962577ca5 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 @@ -5,6 +5,7 @@ import org.springframework.data.repository.query.Param; 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 java.util.List; import java.util.Optional; @@ -39,4 +40,18 @@ public interface RunnerPostQueryRepository extends JpaRepository countApplicantsByRunnerPostIds(@Param("runnerPostIds") final List runnerPostIds); + + @Query(""" + select count(1) + from RunnerPost rp + where rp.runner.id = :runnerId and rp.reviewStatus = :reviewStatus + """) + long countByRunnerIdAndReviewStatus(@Param("runnerId") final Long runnerId, @Param("reviewStatus") final ReviewStatus reviewStatus); + + @Query(""" + select count(1) + from RunnerPost rp + where rp.supporter.id = :supporterId and rp.reviewStatus = :reviewStatus + """) + long countBySupporterIdAndReviewStatus(@Param("supporterId") final Long supporterId, @Param("reviewStatus") final 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 ec7db06f7..e711fea85 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 @@ -114,10 +114,21 @@ public boolean existsRunnerPostApplicantByRunnerPostIdAndMemberId(final Long run return supporterRunnerPostQueryRepository.existsByRunnerPostIdAndMemberId(runnerPostId, memberId); } - public long readCountByRunnerPostId(final Long runnerPostId) { + public long countApplicantsByRunnerPostId(final Long runnerPostId) { return supporterRunnerPostQueryRepository.countByRunnerPostId(runnerPostId).orElse(0L); } + public long countRunnerPostByRunnerIdAndReviewStatus(final Long runnerId, final ReviewStatus reviewStatus) { + return runnerPostQueryRepository.countByRunnerIdAndReviewStatus(runnerId, reviewStatus); + } + + public long countRunnerPostBySupporterIdAndReviewStatus(final Long supporterId, final ReviewStatus reviewStatus) { + if (reviewStatus.isNotStarted()) { + return supporterRunnerPostQueryRepository.countRunnerPostBySupporterIdByReviewStatusNotStarted(supporterId); + } + return runnerPostQueryRepository.countBySupporterIdAndReviewStatus(supporterId, reviewStatus); + } + @Transactional public void increaseWatchedCount(final RunnerPost runnerPost) { runnerPost.increaseWatchedCount(); diff --git a/backend/baton/src/test/java/touch/baton/assure/repository/TestSupporterRunnerPostQueryRepository.java b/backend/baton/src/test/java/touch/baton/assure/repository/TestSupporterRunnerPostQueryRepository.java index 376789f1d..a77e69712 100644 --- a/backend/baton/src/test/java/touch/baton/assure/repository/TestSupporterRunnerPostQueryRepository.java +++ b/backend/baton/src/test/java/touch/baton/assure/repository/TestSupporterRunnerPostQueryRepository.java @@ -4,7 +4,6 @@ import touch.baton.domain.member.query.repository.SupporterRunnerPostQueryRepository; @Profile("test") - public interface TestSupporterRunnerPostQueryRepository extends SupporterRunnerPostQueryRepository { default Long getApplicantCountByRunnerPostId(final Long runnerPostId) { diff --git a/backend/baton/src/test/java/touch/baton/assure/runnerpost/query/count/runner/RunnerPostCountByRunnerAssuredTest.java b/backend/baton/src/test/java/touch/baton/assure/runnerpost/query/count/runner/RunnerPostCountByRunnerAssuredTest.java new file mode 100644 index 000000000..183eba4f8 --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/assure/runnerpost/query/count/runner/RunnerPostCountByRunnerAssuredTest.java @@ -0,0 +1,60 @@ +package touch.baton.assure.runnerpost.query.count.runner; + +import org.junit.jupiter.api.Test; +import touch.baton.assure.runnerpost.support.command.RunnerPostCreateSupport; +import touch.baton.assure.runnerpost.support.query.count.runner.RunnerPostCountByRunnerSupport; +import touch.baton.config.AssuredTestConfig; +import touch.baton.config.infra.auth.oauth.authcode.FakeAuthCodes; +import touch.baton.domain.runnerpost.command.vo.ReviewStatus; +import touch.baton.domain.runnerpost.query.controller.response.RunnerPostResponse; + +import java.time.LocalDateTime; +import java.util.List; + +import static touch.baton.assure.runnerpost.support.command.RunnerPostCreateSupport.러너_게시글_생성_요청; +import static touch.baton.assure.runnerpost.support.query.count.runner.RunnerPostCountByRunnerSupport.러너_게시글_개수_응답; + +@SuppressWarnings("NonAsciiCharacters") +class RunnerPostCountByRunnerAssuredTest extends AssuredTestConfig { + + @Test + void 로그인한_러너와_연관된_러너_게시글_개수_조회에_성공한다() { + // given + final String 디투_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.ditooAuthCode()); + final String 헤나_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.hyenaAuthCode()); + 러너_게시글_생성에_성공한다(디투_액세스_토큰); + 러너_게시글_생성에_성공한다(헤나_액세스_토큰); + + // when + final RunnerPostResponse.Count 기대된_러너_게시글_개수 = 러너_게시글_개수_응답(1); + + // then + RunnerPostCountByRunnerSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(디투_액세스_토큰) + .리뷰_상태로_로그인한_러너와_연관된_러너_게시글_개수를_조회한다(ReviewStatus.NOT_STARTED) + + .서버_응답() + .로그인한_러너와_연관된_러너_게시글_개수_조회_성공을_검증한다(기대된_러너_게시글_개수); + } + + private void 러너_게시글_생성에_성공한다(final String 액세스_토큰) { + RunnerPostCreateSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(액세스_토큰) + .러너_게시글_등록_요청한다( + 러너_게시글_생성_요청( + "테스트용_러너_게시글_제목", + List.of("자바", "스프링"), + "https://test-pull-request.com", + LocalDateTime.now().plusHours(100), + "테스트용_러너_게시글_구현_내용", + "테스트용_러너_게시글_궁금한_내용", + "테스트용_러너_게시글_참고_사항" + ) + ) + + .서버_응답() + .러너_게시글_생성_성공을_검증한다(); + } +} diff --git a/backend/baton/src/test/java/touch/baton/assure/runnerpost/query/count/supporter/RunnerPostCountBySupporterAssuredTest.java b/backend/baton/src/test/java/touch/baton/assure/runnerpost/query/count/supporter/RunnerPostCountBySupporterAssuredTest.java new file mode 100644 index 000000000..94ee83049 --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/assure/runnerpost/query/count/supporter/RunnerPostCountBySupporterAssuredTest.java @@ -0,0 +1,188 @@ +package touch.baton.assure.runnerpost.query.count.supporter; + +import org.junit.jupiter.api.Test; +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.query.count.supporter.RunnerPostCountBySupporterSupport; +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 touch.baton.domain.runnerpost.command.vo.ReviewStatus; +import touch.baton.domain.runnerpost.query.controller.response.RunnerPostResponse; + +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.러너의_서포터_선택_요청; +import static touch.baton.assure.runnerpost.support.command.applicant.RunnerPostApplicantCreateSupport.클라이언트_요청; +import static touch.baton.assure.runnerpost.support.query.count.supporter.RunnerPostCountBySupporterSupport.러너_게시글_개수_응답; + +@SuppressWarnings("NonAsciiCharacters") + class RunnerPostCountBySupporterAssuredTest extends AssuredTestConfig { + + @Test + void 로그인한_서포터가_지원했으면서_아직_시작하지_않은_러너_게시글_개수_조회에_성공한다() { + // given + final String 디투_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.ditooAuthCode()); + final String 헤나_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.hyenaAuthCode()); + final String 서포터_에단_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.ethanAuthCode()); + final Long 디투_러너_게시글_식별자_값 = 러너_게시글_생성을_성공하고_러너_게시글_식별자값을_반환한다(디투_액세스_토큰); + + 러너_게시글_생성을_성공하고_러너_게시글_식별자값을_반환한다(헤나_액세스_토큰); + + 서포터가_러너_게시글에_리뷰_신청을_성공한다(서포터_에단_액세스_토큰, 디투_러너_게시글_식별자_값); + + // when + final RunnerPostResponse.Count 기대된_러너_게시글_개수 = 러너_게시글_개수_응답(1); + + // then + RunnerPostCountBySupporterSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(서포터_에단_액세스_토큰) + .리뷰_상태로_로그인한_서포터와_연관된_러너_게시글_개수를_조회한다(ReviewStatus.NOT_STARTED) + + .서버_응답() + .서포터와_연관된_러너_게시글_개수_조회_성공을_검증한다(기대된_러너_게시글_개수); + } + + @Test + void 로그인한_서포터가_리뷰_중인_러너_게시글_개수_조회에_성공한다() { + // given + final String 디투_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.ditooAuthCode()); + final String 헤나_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.hyenaAuthCode()); + final String 서포터_에단_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.ethanAuthCode()); + final SocialId 서포터_에단_소셜_아이디 = jwtTestManager.parseToSocialId(서포터_에단_액세스_토큰); + final Supporter 서포터_에단 = supporterRepository.getBySocialId(서포터_에단_소셜_아이디); + + final Long 디투_러너_게시글_식별자_값 = 러너_게시글_생성을_성공하고_러너_게시글_식별자값을_반환한다(디투_액세스_토큰); + final Long 헤나_러너_게시글_식별자_값 = 러너_게시글_생성을_성공하고_러너_게시글_식별자값을_반환한다(헤나_액세스_토큰); + + 서포터가_러너_게시글에_리뷰_신청을_성공한다(서포터_에단_액세스_토큰, 헤나_러너_게시글_식별자_값); + + 서포터가_러너_게시글에_리뷰_신청을_성공한다(서포터_에단_액세스_토큰, 디투_러너_게시글_식별자_값); + 러너가_서포터의_리뷰_신청_선택에_성공한다(서포터_에단, 디투_액세스_토큰, 디투_러너_게시글_식별자_값); + + // when + final RunnerPostResponse.Count 기대된_러너_게시글_개수 = 러너_게시글_개수_응답(1); + + // then + RunnerPostCountBySupporterSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(서포터_에단_액세스_토큰) + .리뷰_상태로_로그인한_서포터와_연관된_러너_게시글_개수를_조회한다(ReviewStatus.IN_PROGRESS) + + .서버_응답() + .서포터와_연관된_러너_게시글_개수_조회_성공을_검증한다(기대된_러너_게시글_개수); + } + + @Test + void 로그인한_서포터가_완료한_러너_게시글_개수_조회에_성공한다() { + // given + final String 디투_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.ditooAuthCode()); + final String 헤나_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.hyenaAuthCode()); + final String 서포터_에단_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.ethanAuthCode()); + final SocialId 서포터_에단_소셜_아이디 = jwtTestManager.parseToSocialId(서포터_에단_액세스_토큰); + final Supporter 서포터_에단 = supporterRepository.getBySocialId(서포터_에단_소셜_아이디); + + final Long 디투_러너_게시글_식별자_값 = 러너_게시글_생성을_성공하고_러너_게시글_식별자값을_반환한다(디투_액세스_토큰); + final Long 헤나_러너_게시글_식별자_값 = 러너_게시글_생성을_성공하고_러너_게시글_식별자값을_반환한다(헤나_액세스_토큰); + + 서포터가_러너_게시글에_리뷰_신청을_성공한다(서포터_에단_액세스_토큰, 헤나_러너_게시글_식별자_값); + + 서포터가_러너_게시글에_리뷰_신청을_성공한다(서포터_에단_액세스_토큰, 디투_러너_게시글_식별자_값); + 러너가_서포터의_리뷰_신청_선택에_성공한다(서포터_에단, 디투_액세스_토큰, 디투_러너_게시글_식별자_값); + 서포터가_러너_게시글의_리뷰를_완료로_변경하는_것을_성공한다(서포터_에단_액세스_토큰, 디투_러너_게시글_식별자_값); + + // when + final RunnerPostResponse.Count 기대된_러너_게시글_개수 = 러너_게시글_개수_응답(1); + + // then + RunnerPostCountBySupporterSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(서포터_에단_액세스_토큰) + .리뷰_상태로_로그인한_서포터와_연관된_러너_게시글_개수를_조회한다(ReviewStatus.DONE) + + .서버_응답() + .서포터와_연관된_러너_게시글_개수_조회_성공을_검증한다(기대된_러너_게시글_개수); + } + + @Test + void 타인이_서포터가_완료한_러너_게시글_개수_조회에_성공한다() { + // given + final String 러너_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.ditooAuthCode()); + final String 서포터_액세스_토큰 = oauthLoginTestManager.소셜_회원가입을_진행한_후_액세스_토큰을_반환한다(FakeAuthCodes.ethanAuthCode()); + final SocialId 서포터_소셜_아이디 = jwtTestManager.parseToSocialId(서포터_액세스_토큰); + final Supporter 서포터 = supporterRepository.getBySocialId(서포터_소셜_아이디); + + final Long 러너_게시글_식별자_값 = 러너_게시글_생성을_성공하고_러너_게시글_식별자값을_반환한다(러너_액세스_토큰); + + 서포터가_러너_게시글에_리뷰_신청을_성공한다(서포터_액세스_토큰, 러너_게시글_식별자_값); + 러너가_서포터의_리뷰_신청_선택에_성공한다(서포터, 러너_액세스_토큰, 러너_게시글_식별자_값); + 서포터가_러너_게시글의_리뷰를_완료로_변경하는_것을_성공한다(서포터_액세스_토큰, 러너_게시글_식별자_값); + + // when + final RunnerPostResponse.Count 기대된_러너_게시글_개수 = 러너_게시글_개수_응답(1); + + // then + RunnerPostCountBySupporterSupport + .클라이언트_요청() + .서포터_식별자_값으로_서포터가_완료한_러너_게시글_개수를_조회한다(서포터.getId()) + + .서버_응답() + .서포터와_연관된_러너_게시글_개수_조회_성공을_검증한다(기대된_러너_게시글_개수); + } + + private Long 러너_게시글_생성을_성공하고_러너_게시글_식별자값을_반환한다(final String 러너_액세스_토큰) { + return RunnerPostCreateSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(러너_액세스_토큰) + .러너_게시글_등록_요청한다( + 러너_게시글_생성_요청( + "테스트용_러너_게시글_제목", + List.of("자바", "스프링"), + "https://test-pull-request.com", + LocalDateTime.now().plusHours(100), + "테스트용_러너_게시글_구현_내용", + "테스트용_러너_게시글_궁금한_내용", + "테스트용_러너_게시글_참고_사항" + ) + ) + + .서버_응답() + .러너_게시글_생성_성공을_검증한다() + .생성한_러너_게시글의_식별자값을_반환한다(); + } + + private void 서포터가_러너_게시글에_리뷰_신청을_성공한다(final String 서포터_액세스_토큰, final Long 러너_게시글_식별자값) { + 클라이언트_요청() + .액세스_토큰으로_로그인한다(서포터_액세스_토큰) + .서포터가_러너_게시글에_리뷰를_신청한다(러너_게시글_식별자값, "안녕하세요. 서포터 헤나입니다.") + + .서버_응답() + .서포터가_러너_게시글에_리뷰_신청_성공을_검증한다(러너_게시글_식별자값); + } + + private void 러너가_서포터의_리뷰_신청_선택에_성공한다(final Supporter 서포터, final String 러너_액세스_토큰, final Long 러너_게시글_식별자값) { + RunnerPostUpdateSupport + .클라이언트_요청() + .액세스_토큰으로_로그인한다(러너_액세스_토큰) + .러너가_서포터를_선택한다(러너_게시글_식별자값, 러너의_서포터_선택_요청(서포터.getId())) + + .서버_응답() + .러너_게시글에_서포터가_성공적으로_선택되었는지_확인한다(new HttpStatusAndLocationHeader(HttpStatus.NO_CONTENT, "/api/v1/posts/runner")); + } + + private 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/support/query/count/runner/RunnerPostCountByRunnerSupport.java b/backend/baton/src/test/java/touch/baton/assure/runnerpost/support/query/count/runner/RunnerPostCountByRunnerSupport.java new file mode 100644 index 000000000..3f2dbaedc --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/assure/runnerpost/support/query/count/runner/RunnerPostCountByRunnerSupport.java @@ -0,0 +1,68 @@ +package touch.baton.assure.runnerpost.support.query.count.runner; + +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.HttpStatus; +import touch.baton.assure.common.AssuredSupport; +import touch.baton.assure.common.QueryParams; +import touch.baton.domain.runnerpost.command.vo.ReviewStatus; +import touch.baton.domain.runnerpost.query.controller.response.RunnerPostResponse; + +import java.util.Map; + +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +@SuppressWarnings("NonAsciiCharacters") +public class RunnerPostCountByRunnerSupport { + + private RunnerPostCountByRunnerSupport() { + } + + public static RunnerPostCountByRunnerBuilder 클라이언트_요청() { + return new RunnerPostCountByRunnerBuilder(); + } + + public static RunnerPostResponse.Count 러너_게시글_개수_응답(final long 게시글_개수) { + return new RunnerPostResponse.Count(게시글_개수); + } + + public static class RunnerPostCountByRunnerBuilder { + + private ExtractableResponse response; + + private String accessToken; + + public RunnerPostCountByRunnerBuilder 액세스_토큰으로_로그인한다(final String 액세스_토큰) { + this.accessToken = 액세스_토큰; + return this; + } + + public RunnerPostCountByRunnerBuilder 리뷰_상태로_로그인한_러너와_연관된_러너_게시글_개수를_조회한다(final ReviewStatus 리뷰_상태) { + final Map queryParams = Map.of("reviewStatus", 리뷰_상태); + + response = AssuredSupport.get("/api/v1/posts/runner/me/runner/count", accessToken, new QueryParams(queryParams)); + return this; + } + + public RunnerPostCountByRunnerResponseBuilder 서버_응답() { + return new RunnerPostCountByRunnerResponseBuilder(response); + } + } + + public static class RunnerPostCountByRunnerResponseBuilder { + + private final ExtractableResponse response; + + public RunnerPostCountByRunnerResponseBuilder(final ExtractableResponse response) { + this.response = response; + } + + public void 로그인한_러너와_연관된_러너_게시글_개수_조회_성공을_검증한다(final RunnerPostResponse.Count 러너_게시글_개수_응답) { + final RunnerPostResponse.Count actual = this.response.as(RunnerPostResponse.Count.class); + assertSoftly(softly -> { + softly.assertThat(this.response.statusCode()).isEqualTo(HttpStatus.OK.value()); + softly.assertThat(actual).isEqualTo(러너_게시글_개수_응답); + }); + } + } +} diff --git a/backend/baton/src/test/java/touch/baton/assure/runnerpost/support/query/count/supporter/RunnerPostCountBySupporterSupport.java b/backend/baton/src/test/java/touch/baton/assure/runnerpost/support/query/count/supporter/RunnerPostCountBySupporterSupport.java new file mode 100644 index 000000000..20c4cf08b --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/assure/runnerpost/support/query/count/supporter/RunnerPostCountBySupporterSupport.java @@ -0,0 +1,78 @@ +package touch.baton.assure.runnerpost.support.query.count.supporter; + +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.HttpStatus; +import touch.baton.assure.common.AssuredSupport; +import touch.baton.assure.common.QueryParams; +import touch.baton.domain.runnerpost.command.vo.ReviewStatus; +import touch.baton.domain.runnerpost.query.controller.response.RunnerPostResponse; + +import java.util.Map; + +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +@SuppressWarnings("NonAsciiCharacters") +public class RunnerPostCountBySupporterSupport { + + private RunnerPostCountBySupporterSupport() { + } + + public static RunnerPostCountBySupporterBuilder 클라이언트_요청() { + return new RunnerPostCountBySupporterBuilder(); + } + + public static RunnerPostResponse.Count 러너_게시글_개수_응답(final long 게시글_개수) { + return new RunnerPostResponse.Count(게시글_개수); + } + + public static class RunnerPostCountBySupporterBuilder { + + private ExtractableResponse response; + + private String accessToken; + + public RunnerPostCountBySupporterBuilder 액세스_토큰으로_로그인한다(final String 액세스_토큰) { + this.accessToken = 액세스_토큰; + return this; + } + + public RunnerPostCountBySupporterBuilder 리뷰_상태로_로그인한_서포터와_연관된_러너_게시글_개수를_조회한다(final ReviewStatus 리뷰_상태) { + final Map queryParams = Map.of("reviewStatus", 리뷰_상태); + + response = AssuredSupport.get("/api/v1/posts/runner/me/supporter/count", accessToken, new QueryParams(queryParams)); + return this; + } + + public RunnerPostCountBySupporterBuilder 서포터_식별자_값으로_서포터가_완료한_러너_게시글_개수를_조회한다(final Long 서포터_식별자_값) { + final Map queryParams = Map.of( + "reviewStatus", ReviewStatus.DONE, + "supporterId", 서포터_식별자_값 + ); + + response = AssuredSupport.get("/api/v1/posts/runner/search/count", new QueryParams(queryParams)); + return this; + } + + public RunnerPostCountBySupporterResponseBuilder 서버_응답() { + return new RunnerPostCountBySupporterResponseBuilder(response); + } + } + + public static class RunnerPostCountBySupporterResponseBuilder { + + private final ExtractableResponse response; + + public RunnerPostCountBySupporterResponseBuilder(final ExtractableResponse response) { + this.response = response; + } + + public void 서포터와_연관된_러너_게시글_개수_조회_성공을_검증한다(final RunnerPostResponse.Count 러너_게시글_개수_응답) { + final RunnerPostResponse.Count actual = this.response.as(RunnerPostResponse.Count.class); + assertSoftly(softly -> { + softly.assertThat(this.response.statusCode()).isEqualTo(HttpStatus.OK.value()); + softly.assertThat(actual).isEqualTo(러너_게시글_개수_응답); + }); + } + } +} diff --git a/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostCountOfSupporterByGuestApiTest.java b/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostCountOfSupporterByGuestApiTest.java new file mode 100644 index 000000000..641c06d1f --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostCountOfSupporterByGuestApiTest.java @@ -0,0 +1,72 @@ +package touch.baton.document.runnerpost.read; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import touch.baton.config.RestdocsConfig; +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.vo.Deadline; +import touch.baton.domain.runnerpost.command.vo.ReviewStatus; +import touch.baton.fixture.domain.MemberFixture; +import touch.baton.fixture.domain.RunnerFixture; +import touch.baton.fixture.domain.RunnerPostFixture; +import touch.baton.fixture.domain.SupporterFixture; +import touch.baton.fixture.domain.SupporterRunnerPostFixture; + +import java.time.LocalDateTime; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static touch.baton.fixture.vo.DeadlineFixture.deadline; + +class RunnerPostCountOfSupporterByGuestApiTest extends RestdocsConfig { + + @DisplayName("서포터가 완료한 러너 게시글 개수 조회 API") + @Test + void countRunnerPostBySupporterIdAndReviewStatus() throws Exception { + // given + final Runner runner = RunnerFixture.createRunner(MemberFixture.createHyena()); + final Deadline deadline = deadline(LocalDateTime.now().plusHours(100)); + final RunnerPost runnerPost = RunnerPostFixture.create(runner, deadline); + + final Supporter supporter = SupporterFixture.create(MemberFixture.createDitoo()); + SupporterRunnerPostFixture.create(runnerPost, supporter); + runnerPost.assignSupporter(supporter); + + final Supporter spySupporter = spy(supporter); + given(spySupporter.getId()).willReturn(1L); + + // when + final long expectedCount = 1L; + when(runnerPostQueryService.countRunnerPostBySupporterIdAndReviewStatus(eq(1L), eq(ReviewStatus.DONE))) + .thenReturn(expectedCount); + + // then + mockMvc.perform(get("/api/v1/posts/runner/search/count") + .queryParam( + "supporterId", String.valueOf(spySupporter.getId()) + )) + .andExpect(status().isOk()) + .andExpect(content().contentType(APPLICATION_JSON)) + .andDo(restDocs.document( + queryParameters( + parameterWithName("supporterId").description("서포터 식별자 값") + ), + responseFields( + fieldWithPath("count").type(NUMBER).optional().description("게시글 개수") + )) + ); + } +} diff --git a/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostCountWithLoginedRunnerApiTest.java b/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostCountWithLoginedRunnerApiTest.java new file mode 100644 index 000000000..6782990c3 --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostCountWithLoginedRunnerApiTest.java @@ -0,0 +1,80 @@ +package touch.baton.document.runnerpost.read; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import touch.baton.config.RestdocsConfig; +import touch.baton.domain.member.command.Member; +import touch.baton.domain.member.command.Runner; +import touch.baton.domain.runnerpost.command.vo.Deadline; +import touch.baton.domain.runnerpost.command.vo.ReviewStatus; +import touch.baton.domain.tag.command.Tag; +import touch.baton.fixture.domain.MemberFixture; +import touch.baton.fixture.domain.RunnerFixture; +import touch.baton.fixture.domain.RunnerPostFixture; +import touch.baton.fixture.domain.TagFixture; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static touch.baton.fixture.vo.DeadlineFixture.deadline; +import static touch.baton.fixture.vo.TagNameFixture.tagName; + +class RunnerPostCountWithLoginedRunnerApiTest extends RestdocsConfig { + + @DisplayName("로그인한 러너와 관련된 러너 게시글 개수 조회 API") + @Test + void countRunnerPostByLoginedRunnerAndReviewStatus() throws Exception { + // given + final String socialId = "ditooSocialId"; + final Member loginedMember = MemberFixture.createWithSocialId(socialId); + final Runner loginedRunner = RunnerFixture.createRunner(loginedMember); + final String token = getAccessTokenBySocialId(socialId); + + final Tag javaTag = TagFixture.create(tagName("자바")); + final Deadline deadline = deadline(LocalDateTime.now().plusHours(100)); + RunnerPostFixture.create(loginedRunner, deadline, List.of(javaTag)); + final Runner spyLoginedRunner = spy(loginedRunner); + given(oauthRunnerCommandRepository.joinByMemberSocialId(any())).willReturn(Optional.ofNullable(spyLoginedRunner)); + + // when + final long expectedCount = 1L; + when(runnerPostQueryService.countRunnerPostByRunnerIdAndReviewStatus(eq(1L), eq(ReviewStatus.NOT_STARTED))) + .thenReturn(expectedCount); + + // then + mockMvc.perform(get("/api/v1/posts/runner/me/runner/count") + .header(AUTHORIZATION, "Bearer " + token) + .queryParam("reviewStatus", ReviewStatus.NOT_STARTED.name())) + .andExpect(status().isOk()) + .andExpect(content().contentType(APPLICATION_JSON)) + .andDo(restDocs.document( + requestHeaders( + headerWithName(AUTHORIZATION).description("Bearer JWT") + ), + queryParameters( + parameterWithName("reviewStatus").description("리뷰 상태") + ), + responseFields( + fieldWithPath("count").type(NUMBER).optional().description("게시글 개수") + )) + ); + } +} diff --git a/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostCountWithLoginedSupporterApiTest.java b/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostCountWithLoginedSupporterApiTest.java new file mode 100644 index 000000000..f607b1508 --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostCountWithLoginedSupporterApiTest.java @@ -0,0 +1,84 @@ +package touch.baton.document.runnerpost.read; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import touch.baton.config.RestdocsConfig; +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.vo.Deadline; +import touch.baton.domain.runnerpost.command.vo.ReviewStatus; +import touch.baton.fixture.domain.MemberFixture; +import touch.baton.fixture.domain.RunnerFixture; +import touch.baton.fixture.domain.RunnerPostFixture; +import touch.baton.fixture.domain.SupporterFixture; +import touch.baton.fixture.domain.SupporterRunnerPostFixture; + +import java.time.LocalDateTime; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static touch.baton.fixture.vo.DeadlineFixture.deadline; + +class RunnerPostCountWithLoginedSupporterApiTest extends RestdocsConfig { + + @DisplayName("로그인한 서포터와 관련된 러너 게시글 개수 조회 API") + @Test + void countRunnerPostByLoginedSupporterAndReviewStatus() throws Exception { + // given + final String socialId = "ditooSocialId"; + final Member loginedMember = MemberFixture.createWithSocialId(socialId); + final Supporter loginedSupporter = SupporterFixture.create(loginedMember); + final String token = getAccessTokenBySocialId(socialId); + + final Runner runner = RunnerFixture.createRunner(MemberFixture.createHyena()); + + final Deadline deadline = deadline(LocalDateTime.now().plusHours(100)); + final RunnerPost runnerPost = RunnerPostFixture.create(runner, deadline); + SupporterRunnerPostFixture.create(runnerPost, loginedSupporter); + runnerPost.assignSupporter(loginedSupporter); + + final Supporter spyLoginedSupporter = spy(loginedSupporter); + given(oauthSupporterCommandRepository.joinByMemberSocialId(any())).willReturn(Optional.ofNullable(spyLoginedSupporter)); + + // when + final long expectedCount = 1L; + when(runnerPostQueryService.countRunnerPostBySupporterIdAndReviewStatus(eq(1L), eq(ReviewStatus.NOT_STARTED))) + .thenReturn(expectedCount); + + // then + mockMvc.perform(get("/api/v1/posts/runner/me/supporter/count") + .header(AUTHORIZATION, "Bearer " + token) + .queryParam("reviewStatus", ReviewStatus.NOT_STARTED.name())) + .andExpect(status().isOk()) + .andExpect(content().contentType(APPLICATION_JSON)) + .andDo(restDocs.document( + requestHeaders( + headerWithName(AUTHORIZATION).description("Bearer JWT") + ), + queryParameters( + parameterWithName("reviewStatus").description("리뷰 상태") + ), + responseFields( + fieldWithPath("count").type(NUMBER).optional().description("게시글 개수") + )) + ); + } +} diff --git a/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostReadOneApiTest.java b/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostReadOneApiTest.java index 7941f8577..868ac7384 100644 --- a/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostReadOneApiTest.java +++ b/backend/baton/src/test/java/touch/baton/document/runnerpost/read/RunnerPostReadOneApiTest.java @@ -64,7 +64,7 @@ void readByRunnerPostId() throws Exception { when(runnerPostQueryService.readByRunnerPostId(any())) .thenReturn(spyRunnerPost); - when(runnerPostQueryService.readCountByRunnerPostId(any())) + when(runnerPostQueryService.countApplicantsByRunnerPostId(any())) .thenReturn(3L); final String token = getAccessTokenBySocialId(memberHyena.getSocialId().getValue()); diff --git a/backend/baton/src/test/java/touch/baton/domain/runnerpost/command/repository/SupporterRunnerPostQueryRepositoryReadTest.java b/backend/baton/src/test/java/touch/baton/domain/runnerpost/command/repository/SupporterRunnerPostQueryRepositoryReadTest.java deleted file mode 100644 index 5e17464b6..000000000 --- a/backend/baton/src/test/java/touch/baton/domain/runnerpost/command/repository/SupporterRunnerPostQueryRepositoryReadTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package touch.baton.domain.runnerpost.command.repository; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -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.member.command.repository.MemberCommandRepository; -import touch.baton.domain.member.query.repository.RunnerQueryRepository; -import touch.baton.domain.member.query.repository.SupporterQueryRepository; -import touch.baton.domain.member.query.repository.SupporterRunnerPostQueryRepository; -import touch.baton.domain.runnerpost.command.RunnerPost; -import touch.baton.domain.runnerpost.query.repository.RunnerPostQueryRepository; -import touch.baton.fixture.domain.MemberFixture; -import touch.baton.fixture.domain.RunnerFixture; -import touch.baton.fixture.domain.RunnerPostFixture; -import touch.baton.fixture.domain.SupporterFixture; -import touch.baton.fixture.domain.SupporterRunnerPostFixture; - -import java.time.LocalDateTime; -import java.util.List; - -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static touch.baton.fixture.vo.DeadlineFixture.deadline; - -class SupporterRunnerPostQueryRepositoryReadTest extends RepositoryTestConfig { - - @Autowired - private SupporterRunnerPostQueryRepository supporterRunnerPostRepository; - - @Autowired - private MemberCommandRepository memberCommandRepository; - - @Autowired - private RunnerQueryRepository runnerQueryRepository; - - @Autowired - private SupporterQueryRepository supporterQueryRepository; - - @Autowired - private RunnerPostQueryRepository runnerPostQueryRepository; - - @DisplayName("RunnerPostId 와 SupporterId 로 존재 유무를 확인할 수 있다.") - @Test - void existsByRunnerPostIdAndSupporterId() { - // given - final Member ehtanMember = memberCommandRepository.save(MemberFixture.createEthan()); - final Runner runnerPostOwner = runnerQueryRepository.save(RunnerFixture.createRunner(ehtanMember)); - final RunnerPost runner = runnerPostQueryRepository.save(RunnerPostFixture.create(runnerPostOwner, - deadline(LocalDateTime.now().plusDays(10)))); - - final Member hyenaMember = memberCommandRepository.save(MemberFixture.createHyena()); - final Supporter supporter = supporterQueryRepository.save(SupporterFixture.create(hyenaMember)); - supporterRunnerPostRepository.save(SupporterRunnerPostFixture.create(runner, supporter)); - - final Long notSavedRunnerPostId = -1L; - final Long notSavedSupporter = -1L; - - // when, then - assertSoftly(softly -> { - softly.assertThat(supporterRunnerPostRepository.existsByRunnerPostIdAndSupporterId(runner.getId(), supporter.getId())).isTrue(); - softly.assertThat(supporterRunnerPostRepository.existsByRunnerPostIdAndSupporterId(notSavedRunnerPostId, supporter.getId())).isFalse(); - softly.assertThat(supporterRunnerPostRepository.existsByRunnerPostIdAndSupporterId(runner.getId(), notSavedSupporter)).isFalse(); - } - ); - } - - @DisplayName("RunnerPostId 로 지원한 서포터의 수를 확인할 수 있다.") - @Test - void countByRunnerPostIds() { - // given - final Member ehtanMember = memberCommandRepository.save(MemberFixture.createEthan()); - final Runner runnerPostOwner = runnerQueryRepository.save(RunnerFixture.createRunner(ehtanMember)); - final RunnerPost firstRunnerPost = runnerPostQueryRepository.save(RunnerPostFixture.create(runnerPostOwner, - deadline(LocalDateTime.now().plusDays(10)))); - final RunnerPost twoRunnerPost = runnerPostQueryRepository.save(RunnerPostFixture.create(runnerPostOwner, - deadline(LocalDateTime.now().plusDays(10)))); - - final Member hyenaMember = memberCommandRepository.save(MemberFixture.createHyena()); - final Supporter hyenaSupporter = supporterQueryRepository.save(SupporterFixture.create(hyenaMember)); - supporterRunnerPostRepository.save(SupporterRunnerPostFixture.create(firstRunnerPost, hyenaSupporter)); - - final Member judyMember = memberCommandRepository.save(MemberFixture.createJudy()); - final Supporter judySupporter = supporterQueryRepository.save(SupporterFixture.create(judyMember)); - supporterRunnerPostRepository.save(SupporterRunnerPostFixture.create(firstRunnerPost, judySupporter)); - - // when - final List applicantCounts = supporterRunnerPostRepository.countByRunnerPostIds(List.of(firstRunnerPost.getId(), twoRunnerPost.getId())); - - final Long actualSecondRunnerPostApplicantsCount = applicantCounts.get(0); - final Long actualFirstRunnerPostApplicantsCount = applicantCounts.get(1); - final Long expectedSecondRunnerPostApplicantsCount = 0L; - final Long expectedFirstRunnerPostApplicantsCount = 2L; - final int expectedSize = 2; - - // when, then - assertSoftly(softly -> { - softly.assertThat(applicantCounts.size()).isEqualTo(expectedSize); - softly.assertThat(actualSecondRunnerPostApplicantsCount).isEqualTo(expectedSecondRunnerPostApplicantsCount); - softly.assertThat(actualFirstRunnerPostApplicantsCount).isEqualTo(expectedFirstRunnerPostApplicantsCount); - } - ); - } -} 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 e5c950559..f645ad6ba 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 @@ -8,6 +8,7 @@ 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 java.util.ArrayList; @@ -150,4 +151,47 @@ void joinSupporterByRunnerPostId() { softly.assertThat(actual).isEqualTo(runnerPost); }); } + + @DisplayName("러너 관련 게시글 개수를 조회하는데 성공한다.") + @Test + void countByRunnerIdAndReviewStatus() { + // given + final Runner runner = persistRunner(MemberFixture.createDitoo()); + final int expected = 5; + for (int i = 0; i < expected; i++) { + persistRunnerPost(runner); + } + + em.flush(); + em.close(); + + // when + final Long actual = runnerPostQueryRepository.countByRunnerIdAndReviewStatus(runner.getId(), ReviewStatus.NOT_STARTED); + + // then + assertThat(actual.intValue()).isEqualTo(expected); + } + + @DisplayName("서포터 관련 게시글 개수를 조회하는데 성공한다.") + @Test + void countBySupporterIdAndReviewStatus() { + // given + final Runner runner = persistRunner(MemberFixture.createEthan()); + final Supporter supporter = persistSupporter(MemberFixture.createDitoo()); + final int expected = 3; + for (int i = 0; i < expected; i++) { + final RunnerPost runnerPost = persistRunnerPost(runner); + runnerPost.assignSupporter(supporter); + runnerPost.finishReview(); + } + + em.flush(); + em.close(); + + // when + final Long actual = runnerPostQueryRepository.countBySupporterIdAndReviewStatus(supporter.getId(), ReviewStatus.DONE); + + // then + assertThat(actual.intValue()).isEqualTo(expected); + } } 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 a174ff026..45b685a48 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 @@ -794,4 +794,66 @@ void existsRunnerPostApplicantByRunnerPostIdAndMemberId_if_member_is_not_exist_t // then assertThat(isApplicantHistoryExist).isFalse(); } + + @DisplayName("RunnerId 와 ReviewStatus 로 러너 게시글 개수를 조회한다.") + @Test + void countRunnerPostByRunnerIdAndReviewStatus() { + // given + final Member member = memberCommandRepository.save(MemberFixture.createDitoo()); + final Runner runner = runnerQueryRepository.save(RunnerFixture.createRunner(member)); + + final long expected = 3L; + for (long i = 0; i < expected; i++) { + runnerPostCommandRepository.save(RunnerPostFixture.create(runner, new Deadline(now().plusHours(100)))); + } + + // when + final long actual = runnerPostQueryService.countRunnerPostByRunnerIdAndReviewStatus(runner.getId(), NOT_STARTED); + + // then + assertThat(actual).isEqualTo(expected); + } + + @DisplayName("SupporterId 로 ReviewStatus 가 NOT_STARTED 인 러너 게시글 개수를 조회한다.") + @Test + void countRunnerPostBySupporterIdAndReviewStatus_NOT_STARTED() { + // given + final Member member = memberCommandRepository.save(MemberFixture.createDitoo()); + final Runner runner = runnerQueryRepository.save(RunnerFixture.createRunner(member)); + final RunnerPost runnerPost = runnerPostCommandRepository.save(RunnerPostFixture.create(runner, new Deadline(now().plusHours(100)))); + + final Member supporterMember = memberCommandRepository.save(MemberFixture.createDitoo()); + final Supporter supporter = supporterQueryRepository.save(SupporterFixture.create(supporterMember)); + + supporterRunnerPostCommandRepository.save(SupporterRunnerPostFixture.create(runnerPost, supporter)); + + // when + final long expected = 1L; + final long actual = runnerPostQueryService.countRunnerPostBySupporterIdAndReviewStatus(supporter.getId(), NOT_STARTED); + + // then + assertThat(actual).isEqualTo(expected); + } + + @DisplayName("SupporterId 로 ReviewStatus 가 NOT_STARTED 이 아닌 러너 게시글 개수를 조회한다.") + @Test + void countRunnerPostBySupporterIdAndReviewStatus_except_NOT_STARTED() { + // given + final Member member = memberCommandRepository.save(MemberFixture.createDitoo()); + final Runner runner = runnerQueryRepository.save(RunnerFixture.createRunner(member)); + final RunnerPost runnerPost = runnerPostCommandRepository.save(RunnerPostFixture.create(runner, new Deadline(now().plusHours(100)))); + + final Member supporterMember = memberCommandRepository.save(MemberFixture.createDitoo()); + final Supporter supporter = supporterQueryRepository.save(SupporterFixture.create(supporterMember)); + + supporterRunnerPostCommandRepository.save(SupporterRunnerPostFixture.create(runnerPost, supporter)); + runnerPost.assignSupporter(supporter); + + // when + final long expected = 1L; + final long actual = runnerPostQueryService.countRunnerPostBySupporterIdAndReviewStatus(supporter.getId(), IN_PROGRESS); + + // then + assertThat(actual).isEqualTo(expected); + } } diff --git a/backend/baton/src/test/java/touch/baton/domain/supporter/SupporterFeedbackTest.java b/backend/baton/src/test/java/touch/baton/domain/supporter/command/SupporterFeedbackTest.java similarity index 99% rename from backend/baton/src/test/java/touch/baton/domain/supporter/SupporterFeedbackTest.java rename to backend/baton/src/test/java/touch/baton/domain/supporter/command/SupporterFeedbackTest.java index 98e9c7b47..067746f8c 100644 --- a/backend/baton/src/test/java/touch/baton/domain/supporter/SupporterFeedbackTest.java +++ b/backend/baton/src/test/java/touch/baton/domain/supporter/command/SupporterFeedbackTest.java @@ -1,4 +1,4 @@ -package touch.baton.domain.supporter; +package touch.baton.domain.supporter.command; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/backend/baton/src/test/java/touch/baton/domain/supporter/SupporterTest.java b/backend/baton/src/test/java/touch/baton/domain/supporter/command/SupporterTest.java similarity index 99% rename from backend/baton/src/test/java/touch/baton/domain/supporter/SupporterTest.java rename to backend/baton/src/test/java/touch/baton/domain/supporter/command/SupporterTest.java index da90ec67a..7129b7725 100644 --- a/backend/baton/src/test/java/touch/baton/domain/supporter/SupporterTest.java +++ b/backend/baton/src/test/java/touch/baton/domain/supporter/command/SupporterTest.java @@ -1,4 +1,4 @@ -package touch.baton.domain.supporter; +package touch.baton.domain.supporter.command; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/backend/baton/src/test/java/touch/baton/domain/supporter/repository/SupporterQueryRepositoryTest.java b/backend/baton/src/test/java/touch/baton/domain/supporter/command/repository/SupporterQueryRepositoryTest.java similarity index 75% rename from backend/baton/src/test/java/touch/baton/domain/supporter/repository/SupporterQueryRepositoryTest.java rename to backend/baton/src/test/java/touch/baton/domain/supporter/command/repository/SupporterQueryRepositoryTest.java index d7ba7ff9d..6a47eb423 100644 --- a/backend/baton/src/test/java/touch/baton/domain/supporter/repository/SupporterQueryRepositoryTest.java +++ b/backend/baton/src/test/java/touch/baton/domain/supporter/command/repository/SupporterQueryRepositoryTest.java @@ -1,15 +1,12 @@ -package touch.baton.domain.supporter.repository; +package touch.baton.domain.supporter.command.repository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; 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.Supporter; -import touch.baton.domain.member.command.repository.MemberCommandRepository; import touch.baton.domain.member.query.repository.SupporterQueryRepository; import touch.baton.fixture.domain.MemberFixture; -import touch.baton.fixture.domain.SupporterFixture; import java.util.Optional; @@ -18,9 +15,6 @@ class SupporterQueryRepositoryTest extends RepositoryTestConfig { - @Autowired - private MemberCommandRepository memberCommandRepository; - @Autowired private SupporterQueryRepository supporterQueryRepository; @@ -28,8 +22,7 @@ class SupporterQueryRepositoryTest extends RepositoryTestConfig { @Test void joinMemberBySupporterId() { // given - final Member savedMember = memberCommandRepository.save(MemberFixture.createHyena()); - final Supporter savedSupporter = supporterQueryRepository.save(SupporterFixture.create(savedMember)); + final Supporter savedSupporter = persistSupporter(MemberFixture.createHyena()); // when final Optional maybeSupporter = supporterQueryRepository.joinMemberBySupporterId(savedSupporter.getId()); @@ -41,7 +34,7 @@ void joinMemberBySupporterId() { () -> assertThat(maybeSupporter.get().getIntroduction()).isEqualTo(savedSupporter.getIntroduction()), () -> assertThat(maybeSupporter.get().getReviewCount()).isEqualTo(savedSupporter.getReviewCount()), () -> assertThat(maybeSupporter.get().getSupporterTechnicalTags()).isEqualTo(savedSupporter.getSupporterTechnicalTags()), - () -> assertThat(maybeSupporter.get().getMember()).isEqualTo(savedMember) + () -> assertThat(maybeSupporter.get().getMember()).isEqualTo(savedSupporter.getMember()) ); } diff --git a/backend/baton/src/test/java/touch/baton/domain/supporter/command/repository/SupporterRunnerPostCommandRepositoryTest.java b/backend/baton/src/test/java/touch/baton/domain/supporter/command/repository/SupporterRunnerPostCommandRepositoryTest.java new file mode 100644 index 000000000..8a1d9a4f6 --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/domain/supporter/command/repository/SupporterRunnerPostCommandRepositoryTest.java @@ -0,0 +1,79 @@ +package touch.baton.domain.supporter.command.repository; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import touch.baton.config.RepositoryTestConfig; +import touch.baton.domain.member.command.Runner; +import touch.baton.domain.member.command.Supporter; +import touch.baton.domain.member.command.SupporterRunnerPost; +import touch.baton.domain.member.command.repository.SupporterRunnerPostCommandRepository; +import touch.baton.domain.runnerpost.command.RunnerPost; +import touch.baton.fixture.domain.MemberFixture; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +class SupporterRunnerPostCommandRepositoryTest extends RepositoryTestConfig { + + @Autowired + private SupporterRunnerPostCommandRepository supporterRunnerPostCommandRepository; + + @DisplayName("RunnerPost 외래키로 된 SupporterRunnerPost 가 존재하는지 확인한다.") + @Test + void existsByRunnerPostId() { + // given + final Runner runner = persistRunner(MemberFixture.createDitoo()); + final Supporter supporter = persistSupporter(MemberFixture.createHyena()); + final RunnerPost runnerPostOfApplicantExist = persistRunnerPost(runner); + final RunnerPost runnerPostOfApplicantNotExist = persistRunnerPost(runner); + persistApplicant(supporter, runnerPostOfApplicantExist); + + // when + final boolean actualOfExist = supporterRunnerPostCommandRepository.existsByRunnerPostId(runnerPostOfApplicantExist.getId()); + final boolean actualOfNotExist = supporterRunnerPostCommandRepository.existsByRunnerPostId(runnerPostOfApplicantNotExist.getId()); + + // then + assertSoftly(softly -> { + softly.assertThat(actualOfExist).isTrue(); + softly.assertThat(actualOfNotExist).isFalse(); + }); + } + + @DisplayName("RunnerPostId 와 SupporterId 로 존재 유무를 확인할 수 있다.") + @Test + void existsByRunnerPostIdAndSupporterId() { + // given + final Runner runner = persistRunner(MemberFixture.createEthan()); + final RunnerPost runnerPost = persistRunnerPost(runner); + final Supporter supporter = persistSupporter(MemberFixture.createHyena()); + persistApplicant(supporter, runnerPost); + + final Long notSavedRunnerPostId = -1L; + final Long notSavedSupporter = -1L; + + // when, then + assertSoftly(softly -> { + softly.assertThat(supporterRunnerPostCommandRepository.existsByRunnerPostIdAndSupporterId(runnerPost.getId(), supporter.getId())).isTrue(); + softly.assertThat(supporterRunnerPostCommandRepository.existsByRunnerPostIdAndSupporterId(notSavedRunnerPostId, supporter.getId())).isFalse(); + softly.assertThat(supporterRunnerPostCommandRepository.existsByRunnerPostIdAndSupporterId(runnerPost.getId(), notSavedSupporter)).isFalse(); + } + ); + } + + @DisplayName("서포터의 러너 게시글 리뷰 제안을 철회하는데 성공한다") + @Test + void deleteBySupporterAndRunnerPostId() { + // given + final Supporter supporter = persistSupporter(MemberFixture.createDitoo()); + final Runner runner = persistRunner(MemberFixture.createHyena()); + final RunnerPost runnerPost = persistRunnerPost(runner); + final SupporterRunnerPost supporterRunnerPost = persistApplicant(supporter, runnerPost); + + // when + supporterRunnerPostCommandRepository.deleteBySupporterIdAndRunnerPostId(supporterRunnerPost.getId(), runnerPost.getId()); + + // then + assertThat(supporterRunnerPostCommandRepository.findById(runnerPost.getId())).isNotPresent(); + } +} diff --git a/backend/baton/src/test/java/touch/baton/domain/supporter/service/SupporterCommandServiceTest.java b/backend/baton/src/test/java/touch/baton/domain/supporter/command/service/SupporterCommandServiceTest.java similarity index 96% rename from backend/baton/src/test/java/touch/baton/domain/supporter/service/SupporterCommandServiceTest.java rename to backend/baton/src/test/java/touch/baton/domain/supporter/command/service/SupporterCommandServiceTest.java index 6f97ea029..7f6b41747 100644 --- a/backend/baton/src/test/java/touch/baton/domain/supporter/service/SupporterCommandServiceTest.java +++ b/backend/baton/src/test/java/touch/baton/domain/supporter/command/service/SupporterCommandServiceTest.java @@ -1,4 +1,4 @@ -package touch.baton.domain.supporter.service; +package touch.baton.domain.supporter.command.service; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/backend/baton/src/test/java/touch/baton/domain/supporter/query/repository/SupporterRunnerPostQueryRepositoryTest.java b/backend/baton/src/test/java/touch/baton/domain/supporter/query/repository/SupporterRunnerPostQueryRepositoryTest.java new file mode 100644 index 000000000..3f3ec7bd6 --- /dev/null +++ b/backend/baton/src/test/java/touch/baton/domain/supporter/query/repository/SupporterRunnerPostQueryRepositoryTest.java @@ -0,0 +1,112 @@ +package touch.baton.domain.supporter.query.repository; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import touch.baton.config.RepositoryTestConfig; +import touch.baton.domain.member.command.Runner; +import touch.baton.domain.member.command.Supporter; +import touch.baton.domain.member.command.SupporterRunnerPost; +import touch.baton.domain.member.query.repository.SupporterRunnerPostQueryRepository; +import touch.baton.domain.runnerpost.command.RunnerPost; +import touch.baton.fixture.domain.MemberFixture; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class SupporterRunnerPostQueryRepositoryTest extends RepositoryTestConfig { + + @Autowired + private SupporterRunnerPostQueryRepository supporterRunnerPostQueryRepository; + + @DisplayName("Member 가 SupporterRunnerPost 에 지원한 이력이 있을 경우 true 를 반환한다.") + @Test + void existsByRunnerPostIdAndMemberId_return_true() { + // given + final Runner runner = persistRunner(MemberFixture.createDitoo()); + final RunnerPost runnerPost = persistRunnerPost(runner); + final Supporter supporter = persistSupporter(MemberFixture.createEthan()); + final SupporterRunnerPost supporterRunnerPost = persistApplicant(supporter, runnerPost); + + supporterRunnerPostQueryRepository.save(supporterRunnerPost); + + // when + final boolean isApplicantHistoryExist = supporterRunnerPostQueryRepository.existsByRunnerPostIdAndMemberId( + runnerPost.getId(), + supporter.getMember().getId() + ); + + // then + assertThat(isApplicantHistoryExist).isTrue(); + } + + @DisplayName("Member 가 SupporterRunnerPost 에 지원한 이력이 없을 경우 false 를 반환한다.") + @Test + void existsByRunnerPostIdAndMemberId_if_supporterRunnerPost_is_not_exist_then_return_false() { + // given + final Runner runner = persistRunner(MemberFixture.createDitoo()); + final RunnerPost runnerPost = persistRunnerPost(runner); + final Supporter supporter = persistSupporter(MemberFixture.createEthan()); + + // when + final boolean isApplicantHistoryNotExist = supporterRunnerPostQueryRepository.existsByRunnerPostIdAndMemberId( + runnerPost.getId(), + supporter.getMember().getId() + ); + + // then + assertThat(isApplicantHistoryNotExist).isFalse(); + } + + @DisplayName("Member 가 SupporterRunnerPost 에 지원한 이력을 조회할 때 RunnerPost 자체가 없으면 false 를 반환한다.") + @Test + void existsByRunnerPostIdAndMemberId_if_runnerPost_is_not_exist_then_return_false() { + // given + final Supporter supporter = persistSupporter(MemberFixture.createDitoo()); + + // when + final Long notExistRunnerPostId = -1L; + final boolean isApplicantHistoryNotExist = supporterRunnerPostQueryRepository.existsByRunnerPostIdAndMemberId( + notExistRunnerPostId, + supporter.getMember().getId() + ); + + // then + assertThat(isApplicantHistoryNotExist).isFalse(); + } + + @DisplayName("RunnerPostId 로 SupporterRunnerPost 목록을 조회한다.") + @Test + void readByRunnerPostId() { + // given + final Runner runner = persistRunner(MemberFixture.createHyena()); + final RunnerPost runnerPost = persistRunnerPost(runner); + final Supporter supporterDitoo = persistSupporter(MemberFixture.createDitoo()); + final SupporterRunnerPost supporterDitooRunnerPost = persistApplicant(supporterDitoo, runnerPost); + final Supporter supporterEthan = persistSupporter(MemberFixture.createEthan()); + final SupporterRunnerPost supporterEthanRunnerPost = persistApplicant(supporterEthan, runnerPost); + + // when + final List actual = supporterRunnerPostQueryRepository.readByRunnerPostId(runnerPost.getId()); + + // then + assertThat(actual).containsExactly(supporterDitooRunnerPost, supporterEthanRunnerPost); + } + + @DisplayName("SupporterId 와 Not Started 인 ReviewStatus 로 RunnerPost 개수를 센다.") + @Test + void countRunnerPostBySupporterIdByReviewStatusNotStarted() { + // given + final Runner runner = persistRunner(MemberFixture.createHyena()); + final RunnerPost runnerPost = persistRunnerPost(runner); + final Supporter supporter = persistSupporter(MemberFixture.createDitoo()); + persistApplicant(supporter, runnerPost); + + // when + final long count = supporterRunnerPostQueryRepository.countRunnerPostBySupporterIdByReviewStatusNotStarted(supporter.getId()); + + // then + assertThat(count).isEqualTo(1); + } +} diff --git a/backend/baton/src/test/java/touch/baton/domain/supporter/service/SupporterQueryServiceTest.java b/backend/baton/src/test/java/touch/baton/domain/supporter/query/service/SupporterQueryServiceTest.java similarity index 97% rename from backend/baton/src/test/java/touch/baton/domain/supporter/service/SupporterQueryServiceTest.java rename to backend/baton/src/test/java/touch/baton/domain/supporter/query/service/SupporterQueryServiceTest.java index 6ffe4712f..1d1b0aead 100644 --- a/backend/baton/src/test/java/touch/baton/domain/supporter/service/SupporterQueryServiceTest.java +++ b/backend/baton/src/test/java/touch/baton/domain/supporter/query/service/SupporterQueryServiceTest.java @@ -1,4 +1,4 @@ -package touch.baton.domain.supporter.service; +package touch.baton.domain.supporter.query.service; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/backend/baton/src/test/java/touch/baton/domain/supporter/repository/SupporterRunnerPostQueryRepositoryTest.java b/backend/baton/src/test/java/touch/baton/domain/supporter/repository/SupporterRunnerPostQueryRepositoryTest.java deleted file mode 100644 index 5c9ad6548..000000000 --- a/backend/baton/src/test/java/touch/baton/domain/supporter/repository/SupporterRunnerPostQueryRepositoryTest.java +++ /dev/null @@ -1,211 +0,0 @@ -package touch.baton.domain.supporter.repository; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -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.member.command.SupporterRunnerPost; -import touch.baton.domain.member.command.repository.MemberCommandRepository; -import touch.baton.domain.member.command.vo.Message; -import touch.baton.domain.member.query.repository.RunnerQueryRepository; -import touch.baton.domain.member.query.repository.SupporterQueryRepository; -import touch.baton.domain.member.query.repository.SupporterRunnerPostQueryRepository; -import touch.baton.domain.runnerpost.command.RunnerPost; -import touch.baton.domain.runnerpost.command.vo.Deadline; -import touch.baton.domain.runnerpost.query.repository.RunnerPostQueryRepository; -import touch.baton.fixture.domain.MemberFixture; -import touch.baton.fixture.domain.RunnerFixture; -import touch.baton.fixture.domain.RunnerPostFixture; -import touch.baton.fixture.domain.SupporterFixture; -import touch.baton.fixture.domain.SupporterRunnerPostFixture; - -import java.time.LocalDateTime; -import java.util.List; - -import static java.time.LocalDateTime.now; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static touch.baton.fixture.vo.DeadlineFixture.deadline; - -class SupporterRunnerPostQueryRepositoryTest extends RepositoryTestConfig { - - @Autowired - private SupporterRunnerPostQueryRepository supporterRunnerPostRepository; - - @Autowired - private MemberCommandRepository memberCommandRepository; - - @Autowired - private RunnerQueryRepository runnerQueryRepository; - - @Autowired - private SupporterQueryRepository supporterQueryRepository; - - @Autowired - private RunnerPostQueryRepository runnerPostQueryRepository; - - @DisplayName("러너 게시글 식별자값으로 서포터가 지원한 수를 count 한다.") - @Test - void countByRunnerPostIdIn() { - // given - final Member savedMemberDitoo = memberCommandRepository.save(MemberFixture.createDitoo()); - final Runner savedRunnerDitoo = runnerQueryRepository.save(RunnerFixture.createRunner(savedMemberDitoo)); - - final Member savedMemberHyena = memberCommandRepository.save(MemberFixture.createDitoo()); - final Supporter savedSupporterHyena = supporterQueryRepository.save(SupporterFixture.create(savedMemberHyena)); - - final RunnerPost savedRunnerPostOne = runnerPostQueryRepository.save(RunnerPostFixture.create(savedRunnerDitoo, new Deadline(now().plusHours(100)))); - final RunnerPost savedRunnerPostTwo = runnerPostQueryRepository.save(RunnerPostFixture.create(savedRunnerDitoo, new Deadline(now().plusHours(100)))); - final RunnerPost savedRunnerPostThree = runnerPostQueryRepository.save(RunnerPostFixture.create(savedRunnerDitoo, new Deadline(now().plusHours(100)))); - final RunnerPost savedRunnerPostFour = runnerPostQueryRepository.save(RunnerPostFixture.create(savedRunnerDitoo, new Deadline(now().plusHours(100)))); - - savedRunnerPostOne.assignSupporter(savedSupporterHyena); - savedRunnerPostTwo.assignSupporter(savedSupporterHyena); - savedRunnerPostThree.assignSupporter(savedSupporterHyena); - savedRunnerPostFour.assignSupporter(savedSupporterHyena); - - supporterRunnerPostRepository.save(createSupporterRunnerPost(savedSupporterHyena, savedRunnerPostOne)); - supporterRunnerPostRepository.save(createSupporterRunnerPost(savedSupporterHyena, savedRunnerPostTwo)); - supporterRunnerPostRepository.save(createSupporterRunnerPost(savedSupporterHyena, savedRunnerPostThree)); - supporterRunnerPostRepository.save(createSupporterRunnerPost(savedSupporterHyena, savedRunnerPostFour)); - - // when - final List runnerPostIds = List.of( - savedRunnerPostOne.getId(), - savedRunnerPostTwo.getId(), - savedRunnerPostThree.getId(), - savedRunnerPostFour.getId() - ); - final List foundRunnerPostsApplicantCounts = supporterRunnerPostRepository.countByRunnerPostIdIn(runnerPostIds); - - // then - assertThat(foundRunnerPostsApplicantCounts).containsExactly(1L, 1L, 1L, 1L); - } - - private SupporterRunnerPost createSupporterRunnerPost(final Supporter supporter, final RunnerPost runnerPost) { - return SupporterRunnerPost.builder() - .runnerPost(runnerPost) - .supporter(supporter) - .message(new Message("안녕하세요. 서포터 헤나입니다.")) - .build(); - } - - @DisplayName("Member 가 SupporterRunnerPost 에 지원한 이력이 있을 경우 true 를 반환한다.") - @Test - void existsByRunnerPostIdAndMemberId_return_true() { - // given - final Member savedMemberDitoo = memberCommandRepository.save(MemberFixture.createDitoo()); - final Runner savedRunnerDitoo = runnerQueryRepository.save(RunnerFixture.createRunner(savedMemberDitoo)); - - final Member savedMemberHyena = memberCommandRepository.save(MemberFixture.createHyena()); - final Supporter savedSupporterHyena = supporterQueryRepository.save(SupporterFixture.create(savedMemberHyena)); - - final RunnerPost savedRunnerPost = runnerPostQueryRepository.save(RunnerPostFixture.create(savedRunnerDitoo, new Deadline(now().plusHours(100)))); - - final SupporterRunnerPost runnerPostApplicant = createSupporterRunnerPost(savedSupporterHyena, savedRunnerPost); - supporterRunnerPostRepository.save(runnerPostApplicant); - - // when - final Long notExistMemberId = -1L; - final boolean isApplicantHistoryExist = supporterRunnerPostRepository.existsByRunnerPostIdAndMemberId( - savedRunnerPost.getId(), - savedMemberHyena.getId() - ); - - // then - assertThat(isApplicantHistoryExist).isTrue(); - } - - @DisplayName("Member 가 SupporterRunnerPost 에 지원한 이력이 없을 경우 false 를 반환한다.") - @Test - void existsByRunnerPostIdAndMemberId_if_supporterRunnerPost_is_not_exist_then_return_false() { - // given - final Member savedMemberDitoo = memberCommandRepository.save(MemberFixture.createDitoo()); - final Runner savedRunnerDitoo = runnerQueryRepository.save(RunnerFixture.createRunner(savedMemberDitoo)); - - final Member savedMemberHyena = memberCommandRepository.save(MemberFixture.createHyena()); - - final RunnerPost savedRunnerPost = runnerPostQueryRepository.save(RunnerPostFixture.create(savedRunnerDitoo, new Deadline(now().plusHours(100)))); - - // when - final boolean isApplicantHistoryNotExist = supporterRunnerPostRepository.existsByRunnerPostIdAndMemberId( - savedRunnerPost.getId(), - savedMemberHyena.getId() - ); - - // then - assertThat(isApplicantHistoryNotExist).isFalse(); - } - - @DisplayName("Member 가 SupporterRunnerPost 에 지원한 이력을 조회할 때 RunnerPost 자체가 없으면 false 를 반환한다.") - @Test - void existsByRunnerPostIdAndMemberId_if_runnerPost_is_not_exist_then_return_false() { - // given - final Member savedMemberHyena = memberCommandRepository.save(MemberFixture.createDitoo()); - - // when - final Long notExistRunnerPostId = -1L; - final boolean isApplicantHistoryNotExist = supporterRunnerPostRepository.existsByRunnerPostIdAndMemberId( - notExistRunnerPostId, - savedMemberHyena.getId() - ); - - // then - assertThat(isApplicantHistoryNotExist).isFalse(); - } - - @DisplayName("RunnerPost 외래키로 된 SupporterRunnerPost 가 존재하는지 확인한다.") - @Test - void existsByRunnerPostId() { - // given - final Member savedMemberDitoo = memberCommandRepository.save(MemberFixture.createDitoo()); - final Runner savedRunnerDitoo = runnerQueryRepository.save(RunnerFixture.createRunner(savedMemberDitoo)); - - final Member savedMemberHyena = memberCommandRepository.save(MemberFixture.createDitoo()); - final Supporter savedSupporterHyena = supporterQueryRepository.save(SupporterFixture.create(savedMemberHyena)); - - final RunnerPost runnerPostOfApplicantExist = runnerPostQueryRepository.save(RunnerPostFixture.create(savedRunnerDitoo, new Deadline(now().plusHours(100)))); - final RunnerPost runnerPostOfApplicantNotExist = runnerPostQueryRepository.save(RunnerPostFixture.create(savedRunnerDitoo, new Deadline(now().plusHours(100)))); - runnerPostOfApplicantExist.assignSupporter(savedSupporterHyena); - supporterRunnerPostRepository.save(createSupporterRunnerPost(savedSupporterHyena, runnerPostOfApplicantExist)); - - // when - final boolean actualOfExist = supporterRunnerPostRepository.existsByRunnerPostId(runnerPostOfApplicantExist.getId()); - final boolean actualOfNotExist = supporterRunnerPostRepository.existsByRunnerPostId(runnerPostOfApplicantNotExist.getId()); - - // then - assertSoftly(softly -> { - softly.assertThat(actualOfExist).isTrue(); - softly.assertThat(actualOfNotExist).isFalse(); - }); - } - - @DisplayName("서포터의 러너 게시글 리뷰 제안을 철회하는데 성공한다") - @Test - void deleteBySupporterAndRunnerPostId() { - // given - final Member reviewerMember = memberCommandRepository.save(MemberFixture.createDitoo()); - final Supporter reviewerSupporter = supporterQueryRepository.save(SupporterFixture.create(reviewerMember)); - - final Member revieweeMember = memberCommandRepository.save(MemberFixture.createJudy()); - final Runner revieweeRunner = runnerQueryRepository.save(RunnerFixture.createRunner(revieweeMember)); - - final RunnerPost runnerPost = runnerPostQueryRepository.save(RunnerPostFixture.create( - revieweeRunner, - reviewerSupporter, - deadline(LocalDateTime.now().plusHours(100)) - )); - - final SupporterRunnerPost deletedSupporterRunnerPost = supporterRunnerPostRepository.save( - SupporterRunnerPostFixture.create(runnerPost, reviewerSupporter)); - - // when - supporterRunnerPostRepository.deleteBySupporterIdAndRunnerPostId(reviewerSupporter.getId(), runnerPost.getId()); - - // then - assertThat(supporterRunnerPostRepository.findById(deletedSupporterRunnerPost.getId())).isNotPresent(); - } -}