From 509c2d12557c48b686e958c95456f392358f72b7 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Tue, 27 Feb 2024 23:39:06 +0900 Subject: [PATCH 01/18] =?UTF-8?q?chore:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20h2=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lime-api/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/lime-api/build.gradle b/lime-api/build.gradle index 4e741d780..e47ab942f 100644 --- a/lime-api/build.gradle +++ b/lime-api/build.gradle @@ -35,6 +35,7 @@ dependencies { // 테스트 관련 testImplementation(testFixtures(project(':lime-domain'))) testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'com.h2database:h2' // cache implementation 'org.springframework.boot:spring-boot-starter-cache' From 64caf29802057f1d34a632a9dcc36b18ff74dac2 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Tue, 27 Feb 2024 17:10:52 +0900 Subject: [PATCH 02/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vote/application/VoteServiceTest.java | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java new file mode 100644 index 000000000..848adca34 --- /dev/null +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -0,0 +1,116 @@ +package com.programmers.lime.domains.vote.application; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; + +import com.programmers.lime.IntegrationTest; +import com.programmers.lime.common.model.Hobby; +import com.programmers.lime.domains.item.domain.setup.ItemSetup; +import com.programmers.lime.domains.vote.application.dto.request.VoteCreateServiceRequest; +import com.programmers.lime.error.BusinessException; +import com.programmers.lime.error.EntityNotFoundException; +import com.programmers.lime.error.ErrorCode; +import com.programmers.lime.global.util.MemberUtils; +import com.programmers.lime.redis.vote.VoteRedisManager; + +class VoteServiceTest extends IntegrationTest { + + @Autowired + private VoteService voteService; + + @Autowired + private ItemSetup itemSetup; + + @MockBean + private MemberUtils memberUtils; + + @MockBean + private VoteRedisManager voteRedisManager; + + @BeforeEach + void setUp() { + itemSetup.saveOne(1L); + itemSetup.saveOne(2L); + } + + @Nested + class CreateVote { + @Test + @DisplayName("투표를 생성한다.") + void createVoteTest() { + // given + final VoteCreateServiceRequest request = VoteCreateServiceRequest.builder() + .hobby(Hobby.BASKETBALL) + .content("농구공 추천 좀 해주세요!") + .item1Id(1L) + .item2Id(2L) + .maximumParticipants(1000) + .build(); + + given(memberUtils.getCurrentMemberId()) + .willReturn(1L); + + willDoNothing() + .given(voteRedisManager).addRanking(any(), any()); + + // when + final Long result = voteService.createVote(request); + + // then + assertThat(result).isNotNull(); + } + + @Test + @DisplayName("투표 아이템이 똑같다면 예외를 발생시킨다.") + void createVoteWithSameItemTest() { + // given + final VoteCreateServiceRequest request = VoteCreateServiceRequest.builder() + .hobby(Hobby.BASKETBALL) + .content("농구공 추천 좀 해주세요!") + .item1Id(1L) + .item2Id(1L) + .maximumParticipants(1000) + .build(); + + given(memberUtils.getCurrentMemberId()) + .willReturn(1L); + + // when & then + assertThatThrownBy(() -> voteService.createVote(request)) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.VOTE_ITEM_DUPLICATED); + + then(voteRedisManager).shouldHaveNoInteractions(); + } + + @Test + @DisplayName("투표 아이템이 존재하지 않는다면 예외를 발생시킨다.") + void createVoteWithNotExistItemTest() { + // given + final VoteCreateServiceRequest request = VoteCreateServiceRequest.builder() + .hobby(Hobby.BASKETBALL) + .content("농구공 추천 좀 해주세요!") + .item1Id(3L) + .item2Id(4L) + .maximumParticipants(1000) + .build(); + + given(memberUtils.getCurrentMemberId()) + .willReturn(1L); + + // when & then + assertThatThrownBy(() -> voteService.createVote(request)) + .isInstanceOf(EntityNotFoundException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.ITEM_NOT_FOUND); + + then(voteRedisManager).shouldHaveNoInteractions(); + } + } +} From d3933ad13eeb9e02c2b167163aa8f5c0029a9703 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Wed, 28 Feb 2024 16:43:14 +0900 Subject: [PATCH 03/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vote/application/VoteServiceTest.java | 108 ++++++++++++++++++ .../lime/domains/vote/domain/VoteBuilder.java | 18 +++ .../domains/vote/domain/setup/VoteSetUp.java | 26 +++++ .../domains/vote/domain/setup/VoterSetUp.java | 27 +++++ 4 files changed, 179 insertions(+) create mode 100644 lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoteSetUp.java create mode 100644 lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoterSetUp.java diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index 848adca34..90b6a74b6 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -3,21 +3,29 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.BDDMockito.*; +import java.time.LocalDateTime; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.transaction.annotation.Transactional; import com.programmers.lime.IntegrationTest; import com.programmers.lime.common.model.Hobby; import com.programmers.lime.domains.item.domain.setup.ItemSetup; import com.programmers.lime.domains.vote.application.dto.request.VoteCreateServiceRequest; +import com.programmers.lime.domains.vote.domain.Vote; +import com.programmers.lime.domains.vote.domain.Voter; +import com.programmers.lime.domains.vote.domain.setup.VoteSetUp; +import com.programmers.lime.domains.vote.domain.setup.VoterSetUp; import com.programmers.lime.error.BusinessException; import com.programmers.lime.error.EntityNotFoundException; import com.programmers.lime.error.ErrorCode; import com.programmers.lime.global.util.MemberUtils; +import com.programmers.lime.redis.vote.VoteRankingInfo; import com.programmers.lime.redis.vote.VoteRedisManager; class VoteServiceTest extends IntegrationTest { @@ -25,6 +33,12 @@ class VoteServiceTest extends IntegrationTest { @Autowired private VoteService voteService; + @Autowired + private VoteSetUp voteSetup; + + @Autowired + private VoterSetUp voterSetup; + @Autowired private ItemSetup itemSetup; @@ -113,4 +127,98 @@ void createVoteWithNotExistItemTest() { then(voteRedisManager).shouldHaveNoInteractions(); } } + + @Transactional // 지연 로딩을 위해 필요 + @Nested + class ParticipateVote { + @Test + @DisplayName("투표에 참여한다.") + void participateVoteTest() { + // given + final Long voteId = 1L; + final Long itemId = 1L; + final Vote vote = voteSetup.saveOne(voteId, 1L, 2L); + + given(memberUtils.getCurrentMemberId()) + .willReturn(1L); + + willDoNothing() + .given(voteRedisManager) + .updateRanking(eq(String.valueOf(vote.getHobby())), eq(true), any(VoteRankingInfo.class)); + + // when + voteService.participateVote(voteId, itemId); + + // then + assertThat(vote.getVoters()).hasSize(1); + + // verify + then(voteRedisManager).should(times(1)) + .updateRanking(eq(String.valueOf(vote.getHobby())), eq(true), any(VoteRankingInfo.class)); + } + + @Test + @DisplayName("투표에 재참여한다.") + void reParticipateVoteTest() { + // given + final Long voteId = 1L; + final Long itemId = 2L; + final Vote vote = voteSetup.saveOne(voteId, 1L, 2L); + final Voter voter = voterSetup.saveOne(vote, 1L, 1L); + + given(memberUtils.getCurrentMemberId()) + .willReturn(1L); + + // when + voteService.participateVote(voteId, itemId); + + // then + assertThat(vote.getVoters()).hasSize(1); + assertThat(voter.getItemId()).isEqualTo(itemId); + + // verify + then(voteRedisManager).shouldHaveNoInteractions(); + } + + @Test + @DisplayName("종료된 투표에 참여하려고 하면 예외를 발생시킨다.") + void participateVoteWithEndedVoteTest() { + // given + final Long voteId = 1L; + final Vote vote = voteSetup.saveOne(voteId, 1L, 2L); + + vote.close(LocalDateTime.now()); + + given(memberUtils.getCurrentMemberId()) + .willReturn(1L); + + // when & then + assertThatThrownBy(() -> voteService.participateVote(voteId, 1L)) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.VOTE_CANNOT_PARTICIPATE); + + // verify + then(voteRedisManager).shouldHaveNoInteractions(); + } + + @Test + @DisplayName("투표에 없는 아이템에 참여하려고 하면 예외를 발생시킨다.") + void participateVoteWithNotExistItemTest() { + // given + final Long voteId = 1L; + final Long itemId = 3L; + voteSetup.saveOne(voteId, 1L, 2L); + + given(memberUtils.getCurrentMemberId()) + .willReturn(1L); + + // when & then + assertThatThrownBy(() -> voteService.participateVote(voteId, itemId)) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.VOTE_NOT_CONTAIN_ITEM); + + // verify + then(voteRedisManager).shouldHaveNoInteractions(); + } + } } diff --git a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/VoteBuilder.java b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/VoteBuilder.java index adebc716d..f68047ae7 100644 --- a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/VoteBuilder.java +++ b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/VoteBuilder.java @@ -31,6 +31,24 @@ public static Vote build(final Long memberId) { return vote; } + public static Vote build( + final Long voteId, + final Long item1Id, + final Long item2Id + ) { + final Vote vote = Vote.builder() + .memberId(1L) + .item1Id(item1Id) + .item2Id(item2Id) + .hobby(Hobby.BASKETBALL) + .content("농구공 사려는데 뭐가 더 좋음?") + .maximumParticipants(3) + .build(); + setVoteId(vote, voteId); + + return vote; + } + public static List buildMany(final int size) { final List votes = new ArrayList<>(); for (int i = 0; i < size; i++) { diff --git a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoteSetUp.java b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoteSetUp.java new file mode 100644 index 000000000..5ccf02f50 --- /dev/null +++ b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoteSetUp.java @@ -0,0 +1,26 @@ +package com.programmers.lime.domains.vote.domain.setup; + +import org.springframework.stereotype.Component; + +import com.programmers.lime.domains.vote.domain.Vote; +import com.programmers.lime.domains.vote.domain.VoteBuilder; +import com.programmers.lime.domains.vote.repository.VoteRepository; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class VoteSetUp { + + private final VoteRepository voteRepository; + + public Vote saveOne( + final Long voteId, + final Long item1Id, + final Long item2Id + ) { + final Vote vote = VoteBuilder.build(voteId, item1Id, item2Id); + + return voteRepository.save(vote); + } +} diff --git a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoterSetUp.java b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoterSetUp.java new file mode 100644 index 000000000..726612264 --- /dev/null +++ b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoterSetUp.java @@ -0,0 +1,27 @@ +package com.programmers.lime.domains.vote.domain.setup; + +import org.springframework.stereotype.Component; + +import com.programmers.lime.domains.vote.domain.Vote; +import com.programmers.lime.domains.vote.domain.Voter; +import com.programmers.lime.domains.vote.repository.VoterRepository; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class VoterSetUp { + + private final VoterRepository voterRepository; + + public Voter saveOne( + final Vote vote, + final Long memberId, + final Long itemId + ) { + final Voter voter = new Voter(vote, memberId, itemId); + + return voterRepository.save(voter); + + } +} From 9ee1db315f22cd2ef6574a9a7e4e592a14acd524 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Wed, 28 Feb 2024 17:34:08 +0900 Subject: [PATCH 04/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=A4=91=EB=B3=B5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vote/application/VoteServiceTest.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index 90b6a74b6..946b8e879 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -131,13 +131,20 @@ void createVoteWithNotExistItemTest() { @Transactional // 지연 로딩을 위해 필요 @Nested class ParticipateVote { + + Long voteId = 1L; + Vote vote; + + @BeforeEach + void setUp() { + vote = voteSetup.saveOne(voteId, 1L, 2L); + } + @Test @DisplayName("투표에 참여한다.") void participateVoteTest() { // given - final Long voteId = 1L; final Long itemId = 1L; - final Vote vote = voteSetup.saveOne(voteId, 1L, 2L); given(memberUtils.getCurrentMemberId()) .willReturn(1L); @@ -161,9 +168,7 @@ void participateVoteTest() { @DisplayName("투표에 재참여한다.") void reParticipateVoteTest() { // given - final Long voteId = 1L; final Long itemId = 2L; - final Vote vote = voteSetup.saveOne(voteId, 1L, 2L); final Voter voter = voterSetup.saveOne(vote, 1L, 1L); given(memberUtils.getCurrentMemberId()) @@ -184,9 +189,6 @@ void reParticipateVoteTest() { @DisplayName("종료된 투표에 참여하려고 하면 예외를 발생시킨다.") void participateVoteWithEndedVoteTest() { // given - final Long voteId = 1L; - final Vote vote = voteSetup.saveOne(voteId, 1L, 2L); - vote.close(LocalDateTime.now()); given(memberUtils.getCurrentMemberId()) @@ -205,9 +207,7 @@ void participateVoteWithEndedVoteTest() { @DisplayName("투표에 없는 아이템에 참여하려고 하면 예외를 발생시킨다.") void participateVoteWithNotExistItemTest() { // given - final Long voteId = 1L; final Long itemId = 3L; - voteSetup.saveOne(voteId, 1L, 2L); given(memberUtils.getCurrentMemberId()) .willReturn(1L); From 72f598068fc7de13a64acbc98e0a7f5a3a8a1e1f Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Wed, 28 Feb 2024 17:41:03 +0900 Subject: [PATCH 05/18] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85=EC=9D=84=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EA=B4=80=EC=A0=90=EC=9D=98=20=EA=B8=B0=EB=8A=A5=20=EB=AA=85?= =?UTF-8?q?=EC=84=B8=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vote/application/VoteServiceTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index 946b8e879..2e9fee3ca 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -57,7 +57,7 @@ void setUp() { @Nested class CreateVote { @Test - @DisplayName("투표를 생성한다.") + @DisplayName("투표를 생성할 수 있다.") void createVoteTest() { // given final VoteCreateServiceRequest request = VoteCreateServiceRequest.builder() @@ -82,7 +82,7 @@ void createVoteTest() { } @Test - @DisplayName("투표 아이템이 똑같다면 예외를 발생시킨다.") + @DisplayName("동일한 투표 아이템으로 투표를 생성할 수 없다.") void createVoteWithSameItemTest() { // given final VoteCreateServiceRequest request = VoteCreateServiceRequest.builder() @@ -105,7 +105,7 @@ void createVoteWithSameItemTest() { } @Test - @DisplayName("투표 아이템이 존재하지 않는다면 예외를 발생시킨다.") + @DisplayName("존재하지 않는 아이템으로 투표를 생성할 수 없다.") void createVoteWithNotExistItemTest() { // given final VoteCreateServiceRequest request = VoteCreateServiceRequest.builder() @@ -141,7 +141,7 @@ void setUp() { } @Test - @DisplayName("투표에 참여한다.") + @DisplayName("투표에 참여할 수 있다.") void participateVoteTest() { // given final Long itemId = 1L; @@ -165,7 +165,7 @@ void participateVoteTest() { } @Test - @DisplayName("투표에 재참여한다.") + @DisplayName("이미 참여한 투표에 다시 참여할 수 있다.") void reParticipateVoteTest() { // given final Long itemId = 2L; @@ -186,8 +186,8 @@ void reParticipateVoteTest() { } @Test - @DisplayName("종료된 투표에 참여하려고 하면 예외를 발생시킨다.") - void participateVoteWithEndedVoteTest() { + @DisplayName("종료된 투표에 참여할 수 없다.") + void participateVoteWithClosedVoteTest() { // given vote.close(LocalDateTime.now()); @@ -204,7 +204,7 @@ void participateVoteWithEndedVoteTest() { } @Test - @DisplayName("투표에 없는 아이템에 참여하려고 하면 예외를 발생시킨다.") + @DisplayName("투표에 없는 아이템으로 참여할 수 없다.") void participateVoteWithNotExistItemTest() { // given final Long itemId = 3L; From b25972d01889eacaca86da449a4d889255949f7c Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Wed, 28 Feb 2024 18:31:09 +0900 Subject: [PATCH 06/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=97=90=20verify=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lime/domains/vote/application/VoteServiceTest.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index 2e9fee3ca..3177ad164 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -72,13 +72,18 @@ void createVoteTest() { .willReturn(1L); willDoNothing() - .given(voteRedisManager).addRanking(any(), any()); + .given(voteRedisManager) + .addRanking(String.valueOf(Hobby.BASKETBALL), any(VoteRankingInfo.class)); // when final Long result = voteService.createVote(request); // then assertThat(result).isNotNull(); + + // verify + then(voteRedisManager).should(times(1)) + .addRanking(String.valueOf(Hobby.BASKETBALL), any(VoteRankingInfo.class)); } @Test @@ -101,6 +106,7 @@ void createVoteWithSameItemTest() { .isInstanceOf(BusinessException.class) .hasFieldOrPropertyWithValue("errorCode", ErrorCode.VOTE_ITEM_DUPLICATED); + // verify then(voteRedisManager).shouldHaveNoInteractions(); } @@ -124,6 +130,7 @@ void createVoteWithNotExistItemTest() { .isInstanceOf(EntityNotFoundException.class) .hasFieldOrPropertyWithValue("errorCode", ErrorCode.ITEM_NOT_FOUND); + // verify then(voteRedisManager).shouldHaveNoInteractions(); } } From ba3408a63b89b8954acdeb305ba52f5f639e22e2 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Thu, 29 Feb 2024 17:07:29 +0900 Subject: [PATCH 07/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=20=EC=B7=A8=EC=86=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vote/application/VoteServiceTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index 3177ad164..16a87a6a0 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -228,4 +228,30 @@ void participateVoteWithNotExistItemTest() { then(voteRedisManager).shouldHaveNoInteractions(); } } + + @Test + @DisplayName("투표 참여를 취소할 수 있다.") + void cancelVoteTest() { + // given + final Long voteId = 1L; + final Vote vote = voteSetup.saveOne(voteId, 1L, 2L); + voterSetup.saveOne(vote, 1L, 1L); + + given(memberUtils.getCurrentMemberId()) + .willReturn(1L); + + willDoNothing() + .given(voteRedisManager) + .decreasePopularity(eq(String.valueOf(vote.getHobby())), any(VoteRankingInfo.class)); + + // when + voteService.cancelVote(voteId); + + // then + assertThat(vote.getVoters()).isEmpty(); + + // verify + then(voteRedisManager).should(times(1)) + .decreasePopularity(eq(String.valueOf(vote.getHobby())), any(VoteRankingInfo.class)); + } } From b65010ae73a35d59b7e9a45b65a296f619fbc202 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Fri, 1 Mar 2024 13:43:58 +0900 Subject: [PATCH 08/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=ED=85=8C=EC=8A=A4=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 --- .../vote/application/VoteServiceTest.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index 16a87a6a0..f7edbffe1 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -21,6 +21,7 @@ import com.programmers.lime.domains.vote.domain.Voter; import com.programmers.lime.domains.vote.domain.setup.VoteSetUp; import com.programmers.lime.domains.vote.domain.setup.VoterSetUp; +import com.programmers.lime.domains.vote.implementation.VoteReader; import com.programmers.lime.error.BusinessException; import com.programmers.lime.error.EntityNotFoundException; import com.programmers.lime.error.ErrorCode; @@ -39,6 +40,9 @@ class VoteServiceTest extends IntegrationTest { @Autowired private VoterSetUp voterSetup; + @Autowired + private VoteReader voteReader; + @Autowired private ItemSetup itemSetup; @@ -254,4 +258,60 @@ void cancelVoteTest() { then(voteRedisManager).should(times(1)) .decreasePopularity(eq(String.valueOf(vote.getHobby())), any(VoteRankingInfo.class)); } + + @Nested + class DeleteVote { + + Long voteId = 1L; + Vote vote; + + @BeforeEach + void setUp() { + vote = voteSetup.saveOne(voteId, 1L, 2L); + } + + @Test + @DisplayName("투표를 삭제할 수 있다.") + void deleteVoteTest() { + // given + final Long memberId = vote.getMemberId(); + + given(memberUtils.getCurrentMemberId()) + .willReturn(memberId); + + willDoNothing() + .given(voteRedisManager) + .remove(eq(String.valueOf(vote.getHobby())), any(VoteRankingInfo.class)); + + // when + voteService.deleteVote(voteId); + + // then + assertThatThrownBy(() -> voteReader.read(voteId)) // 삭제된 투표 조회 시 EntityNotFoundException 발생 + .isInstanceOf(EntityNotFoundException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.VOTE_NOT_FOUND); + + // verify + then(voteRedisManager).should(times(1)) + .remove(eq(String.valueOf(vote.getHobby())), any(VoteRankingInfo.class)); + } + + @Test + @DisplayName("투표 작성자가 아닌 경우 투표를 삭제할 수 없다.") + void deleteVoteWithNotOwnerTest() { + // given + final Long memberId = 2L; + + given(memberUtils.getCurrentMemberId()) + .willReturn(memberId); + + // when & then + assertThatThrownBy(() -> voteService.deleteVote(voteId)) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.VOTE_NOT_OWNER); + + // verify + then(voteRedisManager).shouldHaveNoInteractions(); + } + } } From b42162a811e381e8bb97bc00e53a9646e498459b Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Fri, 1 Mar 2024 15:15:57 +0900 Subject: [PATCH 09/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vote/application/VoteServiceTest.java | 133 +++++++++++------- 1 file changed, 79 insertions(+), 54 deletions(-) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index f7edbffe1..be6d1327d 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -15,6 +15,7 @@ import com.programmers.lime.IntegrationTest; import com.programmers.lime.common.model.Hobby; +import com.programmers.lime.domains.item.domain.Item; import com.programmers.lime.domains.item.domain.setup.ItemSetup; import com.programmers.lime.domains.vote.application.dto.request.VoteCreateServiceRequest; import com.programmers.lime.domains.vote.domain.Vote; @@ -52,23 +53,32 @@ class VoteServiceTest extends IntegrationTest { @MockBean private VoteRedisManager voteRedisManager; + private Item item1; + private Item item2; + private Vote vote; + private Long voteId; + @BeforeEach void setUp() { - itemSetup.saveOne(1L); - itemSetup.saveOne(2L); + item1 = itemSetup.saveOne(1L); + item2 = itemSetup.saveOne(2L); + vote = voteSetup.saveOne(1L, item1.getId(), item2.getId()); + voteId = vote.getId(); } @Nested class CreateVote { @Test - @DisplayName("투표를 생성할 수 있다.") + @DisplayName("회원은 투표를 생성할 수 있다.") void createVoteTest() { // given + final Long item1Id = item1.getId(); + final Long item2Id = item2.getId(); final VoteCreateServiceRequest request = VoteCreateServiceRequest.builder() .hobby(Hobby.BASKETBALL) .content("농구공 추천 좀 해주세요!") - .item1Id(1L) - .item2Id(2L) + .item1Id(item1Id) + .item2Id(item2Id) .maximumParticipants(1000) .build(); @@ -77,7 +87,7 @@ void createVoteTest() { willDoNothing() .given(voteRedisManager) - .addRanking(String.valueOf(Hobby.BASKETBALL), any(VoteRankingInfo.class)); + .addRanking(anyString(), any(VoteRankingInfo.class)); // when final Long result = voteService.createVote(request); @@ -87,18 +97,26 @@ void createVoteTest() { // verify then(voteRedisManager).should(times(1)) - .addRanking(String.valueOf(Hobby.BASKETBALL), any(VoteRankingInfo.class)); + .addRanking( + String.valueOf(Hobby.BASKETBALL), + VoteRankingInfo.builder() + .id(Long.MAX_VALUE - result) + .item1Image(item1.getImage()) + .item2Image(item2.getImage()) + .build() + ); } @Test @DisplayName("동일한 투표 아이템으로 투표를 생성할 수 없다.") void createVoteWithSameItemTest() { // given + final Long sameItemId = item1.getId(); final VoteCreateServiceRequest request = VoteCreateServiceRequest.builder() .hobby(Hobby.BASKETBALL) .content("농구공 추천 좀 해주세요!") - .item1Id(1L) - .item2Id(1L) + .item1Id(sameItemId) + .item2Id(sameItemId) .maximumParticipants(1000) .build(); @@ -118,11 +136,13 @@ void createVoteWithSameItemTest() { @DisplayName("존재하지 않는 아이템으로 투표를 생성할 수 없다.") void createVoteWithNotExistItemTest() { // given + final Long notExistItem1Id = 3L; + final Long notExistItem2Id = 4L; final VoteCreateServiceRequest request = VoteCreateServiceRequest.builder() .hobby(Hobby.BASKETBALL) .content("농구공 추천 좀 해주세요!") - .item1Id(3L) - .item2Id(4L) + .item1Id(notExistItem1Id) + .item2Id(notExistItem2Id) .maximumParticipants(1000) .build(); @@ -142,27 +162,18 @@ void createVoteWithNotExistItemTest() { @Transactional // 지연 로딩을 위해 필요 @Nested class ParticipateVote { - - Long voteId = 1L; - Vote vote; - - @BeforeEach - void setUp() { - vote = voteSetup.saveOne(voteId, 1L, 2L); - } - @Test - @DisplayName("투표에 참여할 수 있다.") + @DisplayName("회원은 투표 아이템 중 하나를 선택하여 투표할 수 있다.") void participateVoteTest() { // given - final Long itemId = 1L; + final Long itemId = vote.getItem1Id(); given(memberUtils.getCurrentMemberId()) .willReturn(1L); willDoNothing() .given(voteRedisManager) - .updateRanking(eq(String.valueOf(vote.getHobby())), eq(true), any(VoteRankingInfo.class)); + .updateRanking(anyString(), eq(true), any(VoteRankingInfo.class)); // when voteService.participateVote(voteId, itemId); @@ -172,25 +183,35 @@ void participateVoteTest() { // verify then(voteRedisManager).should(times(1)) - .updateRanking(eq(String.valueOf(vote.getHobby())), eq(true), any(VoteRankingInfo.class)); + .updateRanking( + String.valueOf(vote.getHobby()), + true, + VoteRankingInfo.builder() + .id(Long.MAX_VALUE - voteId) + .item1Image(item1.getImage()) + .item2Image(item2.getImage()) + .build() + ); } @Test - @DisplayName("이미 참여한 투표에 다시 참여할 수 있다.") + @DisplayName("회원은 이미 참여한 투표에 다시 투표할 수 있다.") void reParticipateVoteTest() { // given - final Long itemId = 2L; - final Voter voter = voterSetup.saveOne(vote, 1L, 1L); + final Long memberId = 1L; + final Long selectedItemId = vote.getItem1Id(); + final Voter voter = voterSetup.saveOne(vote, memberId, selectedItemId); + final Long reSelectedItemId = vote.getItem2Id(); given(memberUtils.getCurrentMemberId()) - .willReturn(1L); + .willReturn(memberId); // when - voteService.participateVote(voteId, itemId); + voteService.participateVote(voteId, reSelectedItemId); // then assertThat(vote.getVoters()).hasSize(1); - assertThat(voter.getItemId()).isEqualTo(itemId); + assertThat(voter.getItemId()).isEqualTo(reSelectedItemId); // verify then(voteRedisManager).shouldHaveNoInteractions(); @@ -218,13 +239,13 @@ void participateVoteWithClosedVoteTest() { @DisplayName("투표에 없는 아이템으로 참여할 수 없다.") void participateVoteWithNotExistItemTest() { // given - final Long itemId = 3L; + final Long notExistItemId = 3L; given(memberUtils.getCurrentMemberId()) .willReturn(1L); // when & then - assertThatThrownBy(() -> voteService.participateVote(voteId, itemId)) + assertThatThrownBy(() -> voteService.participateVote(voteId, notExistItemId)) .isInstanceOf(BusinessException.class) .hasFieldOrPropertyWithValue("errorCode", ErrorCode.VOTE_NOT_CONTAIN_ITEM); @@ -234,19 +255,18 @@ void participateVoteWithNotExistItemTest() { } @Test - @DisplayName("투표 참여를 취소할 수 있다.") + @DisplayName("회원은 투표 참여를 취소할 수 있다.") void cancelVoteTest() { // given - final Long voteId = 1L; - final Vote vote = voteSetup.saveOne(voteId, 1L, 2L); - voterSetup.saveOne(vote, 1L, 1L); + final Long memberId = 1L; + voterSetup.saveOne(vote, memberId, vote.getItem1Id()); given(memberUtils.getCurrentMemberId()) - .willReturn(1L); + .willReturn(memberId); willDoNothing() .given(voteRedisManager) - .decreasePopularity(eq(String.valueOf(vote.getHobby())), any(VoteRankingInfo.class)); + .decreasePopularity(anyString(), any(VoteRankingInfo.class)); // when voteService.cancelVote(voteId); @@ -256,22 +276,20 @@ void cancelVoteTest() { // verify then(voteRedisManager).should(times(1)) - .decreasePopularity(eq(String.valueOf(vote.getHobby())), any(VoteRankingInfo.class)); + .decreasePopularity( + String.valueOf(vote.getHobby()), + VoteRankingInfo.builder() + .id(Long.MAX_VALUE - voteId) + .item1Image(item1.getImage()) + .item2Image(item2.getImage()) + .build() + ); } @Nested class DeleteVote { - - Long voteId = 1L; - Vote vote; - - @BeforeEach - void setUp() { - vote = voteSetup.saveOne(voteId, 1L, 2L); - } - @Test - @DisplayName("투표를 삭제할 수 있다.") + @DisplayName("투표 생성자는 투표를 삭제할 수 있다.") void deleteVoteTest() { // given final Long memberId = vote.getMemberId(); @@ -281,7 +299,7 @@ void deleteVoteTest() { willDoNothing() .given(voteRedisManager) - .remove(eq(String.valueOf(vote.getHobby())), any(VoteRankingInfo.class)); + .remove(anyString(), any(VoteRankingInfo.class)); // when voteService.deleteVote(voteId); @@ -293,17 +311,24 @@ void deleteVoteTest() { // verify then(voteRedisManager).should(times(1)) - .remove(eq(String.valueOf(vote.getHobby())), any(VoteRankingInfo.class)); + .remove( + String.valueOf(vote.getHobby()), + VoteRankingInfo.builder() + .id(Long.MAX_VALUE - voteId) + .item1Image(item1.getImage()) + .item2Image(item2.getImage()) + .build() + ); } @Test - @DisplayName("투표 작성자가 아닌 경우 투표를 삭제할 수 없다.") + @DisplayName("투표 생성자가 아닌 경우 투표를 삭제할 수 없다.") void deleteVoteWithNotOwnerTest() { // given - final Long memberId = 2L; + final Long notOwnerMemberId = 2L; given(memberUtils.getCurrentMemberId()) - .willReturn(memberId); + .willReturn(notOwnerMemberId); // when & then assertThatThrownBy(() -> voteService.deleteVote(voteId)) From 59ae6cfbb597dd9e4cac2ece3dc8617ed3a2214d Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Fri, 1 Mar 2024 15:42:16 +0900 Subject: [PATCH 10/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/vote/application/VoteServiceTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index be6d1327d..c2c3c07b2 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -17,12 +17,15 @@ import com.programmers.lime.common.model.Hobby; import com.programmers.lime.domains.item.domain.Item; import com.programmers.lime.domains.item.domain.setup.ItemSetup; +import com.programmers.lime.domains.item.model.ItemInfo; import com.programmers.lime.domains.vote.application.dto.request.VoteCreateServiceRequest; +import com.programmers.lime.domains.vote.application.dto.response.VoteGetServiceResponse; import com.programmers.lime.domains.vote.domain.Vote; import com.programmers.lime.domains.vote.domain.Voter; import com.programmers.lime.domains.vote.domain.setup.VoteSetUp; import com.programmers.lime.domains.vote.domain.setup.VoterSetUp; import com.programmers.lime.domains.vote.implementation.VoteReader; +import com.programmers.lime.domains.vote.model.VoteDetailInfo; import com.programmers.lime.error.BusinessException; import com.programmers.lime.error.EntityNotFoundException; import com.programmers.lime.error.ErrorCode; @@ -339,4 +342,16 @@ void deleteVoteWithNotOwnerTest() { then(voteRedisManager).shouldHaveNoInteractions(); } } + + @Test + @DisplayName("사용자는 투표를 상세 조회할 수 있다.") + void readVoteTest() { + // when + final VoteGetServiceResponse result = voteService.getVote(voteId); + + // then + assertThat(result.item1Info()).isEqualTo(ItemInfo.from(item1)); + assertThat(result.item2Info()).isEqualTo(ItemInfo.from(item2)); + assertThat(result.voteInfo()).isEqualTo(VoteDetailInfo.of(vote, 0, 0)); + } } From 4a953fe8b0e621dcc75de66c9648594e4b18fd90 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Sat, 2 Mar 2024 10:52:49 +0900 Subject: [PATCH 11/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vote/application/VoteServiceTest.java | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index c2c3c07b2..a3fa99da0 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -343,15 +343,55 @@ void deleteVoteWithNotOwnerTest() { } } - @Test - @DisplayName("사용자는 투표를 상세 조회할 수 있다.") - void readVoteTest() { - // when - final VoteGetServiceResponse result = voteService.getVote(voteId); + @Nested + class RaadVote { + @Test + @DisplayName("사용자는 투표를 상세 조회할 수 있다.") + // 사용자는 회원과 비회원을 모두 의미 + void readVoteTest() { + // when + final VoteGetServiceResponse result = voteService.getVote(voteId); - // then - assertThat(result.item1Info()).isEqualTo(ItemInfo.from(item1)); - assertThat(result.item2Info()).isEqualTo(ItemInfo.from(item2)); - assertThat(result.voteInfo()).isEqualTo(VoteDetailInfo.of(vote, 0, 0)); + // then + assertThat(result.item1Info()).isEqualTo(ItemInfo.from(item1)); + assertThat(result.item2Info()).isEqualTo(ItemInfo.from(item2)); + assertThat(result.voteInfo()).isEqualTo(VoteDetailInfo.of(vote, 0, 0)); + assertThat(result.isOwner()).isFalse(); + assertThat(result.selectedItemId()).isNull(); + } + + @Test + @DisplayName("회원이 생성한 투표를 상세 조회 시 본인이 생성한 투표임을 확인할 수 있다.") + void readVoteWithOwnerTest() { + // given + final Long memberId = vote.getMemberId(); + + given(memberUtils.getCurrentMemberId()) + .willReturn(memberId); + + // when + final VoteGetServiceResponse result = voteService.getVote(voteId); + + // then + assertThat(result.isOwner()).isTrue(); + } + + @Test + @DisplayName("회원이 참여한 투표를 상세 조회 시 본인이 선택한 아이템을 확인할 수 있다.") + void readVoteWithParticipatedTest() { + // given + final Long memberId = 1L; + final Long selectedItemId = vote.getItem1Id(); + voterSetup.saveOne(vote, memberId, selectedItemId); + + given(memberUtils.getCurrentMemberId()) + .willReturn(memberId); + + // when + final VoteGetServiceResponse result = voteService.getVote(voteId); + + // then + assertThat(result.selectedItemId()).isEqualTo(selectedItemId); + } } } From 321a036fb2947d68c6bf8ae4c143b209ff364623 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Sat, 2 Mar 2024 10:53:58 +0900 Subject: [PATCH 12/18] =?UTF-8?q?comment:=20=EC=A3=BC=EC=84=9D=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lime/domains/vote/application/VoteServiceTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index a3fa99da0..a9a3ad918 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -346,8 +346,7 @@ void deleteVoteWithNotOwnerTest() { @Nested class RaadVote { @Test - @DisplayName("사용자는 투표를 상세 조회할 수 있다.") - // 사용자는 회원과 비회원을 모두 의미 + @DisplayName("사용자는 투표를 상세 조회할 수 있다.") // 사용자는 회원과 비회원을 모두 의미 void readVoteTest() { // when final VoteGetServiceResponse result = voteService.getVote(voteId); From 5d799aa258033e2815370f1147b2a8fa03ed8c92 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Mon, 4 Mar 2024 15:03:38 +0900 Subject: [PATCH 13/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vote/application/VoteServiceTest.java | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index a9a3ad918..01a115216 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -14,6 +14,8 @@ import org.springframework.transaction.annotation.Transactional; import com.programmers.lime.IntegrationTest; +import com.programmers.lime.common.cursor.CursorPageParameters; +import com.programmers.lime.common.cursor.CursorSummary; import com.programmers.lime.common.model.Hobby; import com.programmers.lime.domains.item.domain.Item; import com.programmers.lime.domains.item.domain.setup.ItemSetup; @@ -26,6 +28,9 @@ import com.programmers.lime.domains.vote.domain.setup.VoterSetUp; import com.programmers.lime.domains.vote.implementation.VoteReader; import com.programmers.lime.domains.vote.model.VoteDetailInfo; +import com.programmers.lime.domains.vote.model.VoteSortCondition; +import com.programmers.lime.domains.vote.model.VoteStatusCondition; +import com.programmers.lime.domains.vote.model.VoteSummary; import com.programmers.lime.error.BusinessException; import com.programmers.lime.error.EntityNotFoundException; import com.programmers.lime.error.ErrorCode; @@ -393,4 +398,160 @@ void readVoteWithParticipatedTest() { assertThat(result.selectedItemId()).isEqualTo(selectedItemId); } } + + @Nested + class GetVotesByCursor { + + Vote vote2; + Vote vote3; + + @BeforeEach + void setUp() { + vote2 = voteSetup.saveOne(2L, item1.getId(), item2.getId()); + vote3 = voteSetup.saveOne(3L, item1.getId(), item2.getId()); + } + + @Test + @DisplayName("사용자는 투표 목록을 취미별로 최신순으로 조회할 수 있다.") + void getVotesByCursorWithRecentTest() { + // given + final Hobby hobby = Hobby.BASKETBALL; + final VoteSortCondition sortCondition = VoteSortCondition.RECENT; + + // when + final CursorSummary result = voteService.getVotesByCursor( + hobby, + null, + sortCondition, + new CursorPageParameters(null, null) + ); + + // then + assertThat(result.summaries()).hasSize(3); + assertThat(result.summaries().get(0).voteInfo().id()).isEqualTo(vote3.getId()); + assertThat(result.summaries().get(1).voteInfo().id()).isEqualTo(vote2.getId()); + assertThat(result.summaries().get(2).voteInfo().id()).isEqualTo(vote.getId()); + } + + @Test + @DisplayName("사용자는 투표 목록을 취미별로 인기순으로 조회할 수 있다.") + void getVotesByCursorWithPopularTest() { + // given + final Hobby hobby = Hobby.BASKETBALL; + final VoteSortCondition sortCondition = VoteSortCondition.POPULARITY; + + voterSetup.saveOne(vote2, 1L, vote.getItem1Id()); + voterSetup.saveOne(vote2, 2L, vote.getItem1Id()); + voterSetup.saveOne(vote3, 1L, vote.getItem1Id()); + + // when + final CursorSummary result = voteService.getVotesByCursor( + hobby, + null, + sortCondition, + new CursorPageParameters(null, null) + ); + + // then + assertThat(result.summaries()).hasSize(3); + assertThat(result.summaries().get(0).voteInfo().id()).isEqualTo(vote2.getId()); + assertThat(result.summaries().get(1).voteInfo().id()).isEqualTo(vote3.getId()); + assertThat(result.summaries().get(2).voteInfo().id()).isEqualTo(vote.getId()); + } + + @Test + @DisplayName("사용자는 투표 목록을 취미별로 투표 마감순으로 조회할 수 있다.") + void getVotesByCursorWithClosedTest() { + // given + final Hobby hobby = Hobby.BASKETBALL; + final VoteSortCondition sortCondition = VoteSortCondition.CLOSED; + + // when + final CursorSummary result = voteService.getVotesByCursor( + hobby, + null, + sortCondition, + new CursorPageParameters(null, null) + ); + + // then + assertThat(result.summaries()).hasSize(3); + assertThat(result.summaries().get(0).voteInfo().id()).isEqualTo(vote.getId()); + assertThat(result.summaries().get(1).voteInfo().id()).isEqualTo(vote2.getId()); + assertThat(result.summaries().get(2).voteInfo().id()).isEqualTo(vote3.getId()); + } + + @Test + @DisplayName("회원은 본인이 생성한 투표 목록을 취미별로 조회할 수 있다.") + void getVotesByCursorWithPostedTest() { + // given + final Hobby hobby = Hobby.BASKETBALL; + final VoteStatusCondition statusCondition = VoteStatusCondition.POSTED; + final Long memberId = 1L; + + given(memberUtils.getCurrentMemberId()) + .willReturn(memberId); + + // when + final CursorSummary result = voteService.getVotesByCursor( + hobby, + statusCondition, + VoteSortCondition.RECENT, + new CursorPageParameters(null, null) + ); + + // then + assertThat(result.summaries()).hasSize(3); + } + + @Test + @DisplayName("회원은 본인이 참여한 투표 목록을 취미별로 조회할 수 있다.") + void getVotesByCursorWithParticipatedTest() { + // given + final Hobby hobby = Hobby.BASKETBALL; + final VoteStatusCondition statusCondition = VoteStatusCondition.PARTICIPATED; + final Long memberId = 1L; + + voterSetup.saveOne(vote2, memberId, vote.getItem1Id()); + voterSetup.saveOne(vote3, memberId, vote.getItem1Id()); + + given(memberUtils.getCurrentMemberId()) + .willReturn(memberId); + + // when + final CursorSummary result = voteService.getVotesByCursor( + hobby, + statusCondition, + VoteSortCondition.RECENT, + new CursorPageParameters(null, null) + ); + + // then + assertThat(result.summaries()).hasSize(2); + assertThat(result.summaries().get(0).voteInfo().id()).isEqualTo(vote3.getId()); + assertThat(result.summaries().get(1).voteInfo().id()).isEqualTo(vote2.getId()); + } + + @Test + @DisplayName("비회원은 본인이 생성하거나 참여한 투표 목록을 조회할 수 없다.") + void getVotesByCursorWithUnauthorizedTest() { + // given + final Hobby hobby = Hobby.BASKETBALL; + final VoteStatusCondition statusCondition = VoteStatusCondition.POSTED; + final Long memberId = null; + + given(memberUtils.getCurrentMemberId()) + .willReturn(memberId); + + // when & then + assertThatThrownBy(() -> voteService.getVotesByCursor( + hobby, + statusCondition, + VoteSortCondition.RECENT, + new CursorPageParameters(null, null) + )) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.UNAUTHORIZED); + } + } } From a2a927cc7a538e56c0c354ccd41726d2e9e13158 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Mon, 4 Mar 2024 15:05:23 +0900 Subject: [PATCH 14/18] =?UTF-8?q?chore:=20H2=20=EC=9D=98=EC=A1=B4=EC=84=B1?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MySQL의 DATE_FORMAT 함수가 H2에 없어서 호환성 문제로 일단 제거, 해결 방법 시도 중 --- lime-api/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/lime-api/build.gradle b/lime-api/build.gradle index e47ab942f..4e741d780 100644 --- a/lime-api/build.gradle +++ b/lime-api/build.gradle @@ -35,7 +35,6 @@ dependencies { // 테스트 관련 testImplementation(testFixtures(project(':lime-domain'))) testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'com.h2database:h2' // cache implementation 'org.springframework.boot:spring-boot-starter-cache' From 8838b43c3f86fa775a3b3f8fdd6a0eaf77f5e300 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Mon, 4 Mar 2024 16:32:27 +0900 Subject: [PATCH 15/18] =?UTF-8?q?fix:=20=EC=BB=A4=EC=84=9C=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=EA=B0=80=20Swagger=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=95=84=EC=88=98=20=EC=9E=85=EB=A0=A5=EA=B0=92?= =?UTF-8?q?=EC=9D=B8=20=EB=AC=B8=EC=A0=9C=EC=99=80=20Json=20=ED=98=95?= =?UTF-8?q?=ED=83=9C=EB=A1=9C=20=EC=9E=85=EB=A0=A5=EB=B0=9B=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lime/domains/vote/api/VoteController.java | 4 ++-- .../programmers/lime/global/cursor/CursorRequest.java | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lime-api/src/main/java/com/programmers/lime/domains/vote/api/VoteController.java b/lime-api/src/main/java/com/programmers/lime/domains/vote/api/VoteController.java index 46008b78a..6364e7ae0 100644 --- a/lime-api/src/main/java/com/programmers/lime/domains/vote/api/VoteController.java +++ b/lime-api/src/main/java/com/programmers/lime/domains/vote/api/VoteController.java @@ -95,7 +95,7 @@ public ResponseEntity getVotesByCursor( @RequestParam final String hobby, @RequestParam(required = false, name = "status") final String statusCondition, @RequestParam(required = false, name = "sort") final String sortCondition, - @ModelAttribute @Valid final CursorRequest request + @ModelAttribute final CursorRequest request ) { final CursorSummary cursorSummary = voteService.getVotesByCursor( Hobby.from(hobby), @@ -112,7 +112,7 @@ public ResponseEntity getVotesByCursor( @GetMapping("/search") public ResponseEntity getVotesByKeyword( @RequestParam final String keyword, - @ModelAttribute @Valid final CursorRequest request + @ModelAttribute final CursorRequest request ) { final VoteGetByKeywordServiceResponse serviceResponse = voteService.getVotesByKeyword(keyword, request.toParameters()); diff --git a/lime-api/src/main/java/com/programmers/lime/global/cursor/CursorRequest.java b/lime-api/src/main/java/com/programmers/lime/global/cursor/CursorRequest.java index 3a00ba559..101c8faba 100644 --- a/lime-api/src/main/java/com/programmers/lime/global/cursor/CursorRequest.java +++ b/lime-api/src/main/java/com/programmers/lime/global/cursor/CursorRequest.java @@ -1,14 +1,17 @@ package com.programmers.lime.global.cursor; +import org.springdoc.core.annotations.ParameterObject; + import com.programmers.lime.common.cursor.CursorPageParameters; -import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.Parameter; +@ParameterObject public record CursorRequest( - @Schema(description = "커서아이디, 첫 조회는 커서아이디 없는 요청입니다.", example = "2023110124000001") + @Parameter(description = "커서아이디, 첫 조회는 커서아이디 없는 요청입니다.", example = "2023110124000001") String cursorId, - @Schema(description = "페이징 사이즈입니다", example = "10") + @Parameter(description = "페이징 사이즈입니다", example = "10") Integer size ) { public CursorPageParameters toParameters() { From eb9123e84cc64d5e202a0865a2ad7b927bdcd44a Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Mon, 4 Mar 2024 17:06:28 +0900 Subject: [PATCH 16/18] =?UTF-8?q?fix:=20=EC=B4=9D=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EB=90=9C=20=ED=88=AC=ED=91=9C=20=EC=88=98=EC=99=80=20=EC=8B=A4?= =?UTF-8?q?=EC=A0=9C=20=EA=B2=80=EC=83=89=EB=90=98=EB=8A=94=20=ED=88=AC?= =?UTF-8?q?=ED=91=9C=20=EC=88=98=EA=B0=80=20=EB=8B=A4=EB=A5=B8=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vote/repository/VoteRepositoryForCursorImpl.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/vote/repository/VoteRepositoryForCursorImpl.java b/lime-domain/src/main/java/com/programmers/lime/domains/vote/repository/VoteRepositoryForCursorImpl.java index 56823bea5..ef2f6c45d 100644 --- a/lime-domain/src/main/java/com/programmers/lime/domains/vote/repository/VoteRepositoryForCursorImpl.java +++ b/lime-domain/src/main/java/com/programmers/lime/domains/vote/repository/VoteRepositoryForCursorImpl.java @@ -3,7 +3,6 @@ import static com.programmers.lime.domains.item.domain.QItem.*; import static com.programmers.lime.domains.vote.domain.QVote.*; -import java.time.LocalDateTime; import java.util.List; import com.programmers.lime.common.model.Hobby; @@ -69,10 +68,7 @@ public Long countByKeyword(final String keyword) { return jpaQueryFactory .select(vote.count()) .from(vote) - .where( - isCompleted(), - containsKeyword(keyword) - ) + .where(containsKeyword(keyword)) .fetchOne(); } @@ -105,10 +101,6 @@ private BooleanExpression getExpressionBy( } } - private BooleanExpression isCompleted() { - return vote.endTime.before(LocalDateTime.now()); - } - private BooleanExpression isPosted(final Long memberId) { return vote.memberId.eq(memberId); } From 3c85c2d291c29fc56c72e6ebfdd473e62b0a217f Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Mon, 4 Mar 2024 17:10:21 +0900 Subject: [PATCH 17/18] =?UTF-8?q?feat:=20=ED=88=AC=ED=91=9C=20=EC=A0=9C?= =?UTF-8?q?=EB=AA=A9=EC=97=90=20=EB=8C=80=ED=95=B4=EC=84=9C=EB=8F=84=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EB=90=98=EB=8A=94=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/vote/repository/VoteRepositoryForCursorImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lime-domain/src/main/java/com/programmers/lime/domains/vote/repository/VoteRepositoryForCursorImpl.java b/lime-domain/src/main/java/com/programmers/lime/domains/vote/repository/VoteRepositoryForCursorImpl.java index ef2f6c45d..b84cd2b22 100644 --- a/lime-domain/src/main/java/com/programmers/lime/domains/vote/repository/VoteRepositoryForCursorImpl.java +++ b/lime-domain/src/main/java/com/programmers/lime/domains/vote/repository/VoteRepositoryForCursorImpl.java @@ -134,7 +134,7 @@ private BooleanExpression containsKeyword(final String keyword) { final List itemIds = getItemIds(keyword); - return vote.item1Id.in(itemIds).or(vote.item2Id.in(itemIds)); + return vote.item1Id.in(itemIds).or(vote.item2Id.in(itemIds)).or(vote.content.content.contains(keyword)); } private List getItemIds(final String keyword) { From 99f06cf673e03a5437b9bac544a55c605c5ff470 Mon Sep 17 00:00:00 2001 From: Yiseul Park Date: Thu, 7 Mar 2024 14:21:33 +0900 Subject: [PATCH 18/18] =?UTF-8?q?test:=20=ED=88=AC=ED=91=9C=20=ED=82=A4?= =?UTF-8?q?=EC=9B=8C=EB=93=9C=20=EA=B2=80=EC=83=89=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 --- .../vote/application/VoteServiceTest.java | 55 +++++++++++++++++++ .../domains/vote/domain/setup/VoteSetUp.java | 4 ++ 2 files changed, 59 insertions(+) diff --git a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java index 01a115216..d35f0d987 100644 --- a/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java +++ b/lime-api/src/test/java/com/programmers/lime/domains/vote/application/VoteServiceTest.java @@ -21,6 +21,7 @@ import com.programmers.lime.domains.item.domain.setup.ItemSetup; import com.programmers.lime.domains.item.model.ItemInfo; import com.programmers.lime.domains.vote.application.dto.request.VoteCreateServiceRequest; +import com.programmers.lime.domains.vote.application.dto.response.VoteGetByKeywordServiceResponse; import com.programmers.lime.domains.vote.application.dto.response.VoteGetServiceResponse; import com.programmers.lime.domains.vote.domain.Vote; import com.programmers.lime.domains.vote.domain.Voter; @@ -554,4 +555,58 @@ void getVotesByCursorWithUnauthorizedTest() { .hasFieldOrPropertyWithValue("errorCode", ErrorCode.UNAUTHORIZED); } } + + @Nested + class GetVotesByKeyword { + + Vote vote2; + + @BeforeEach + void setUp() { + vote2 = voteSetup.save(Vote.builder() + .memberId(1L) + .item1Id(item1.getId()) + .item2Id(item2.getId()) + .hobby(Hobby.BASKETBALL) + .content("농린이 추천템은?") + .maximumParticipants(1000) + .build()); + } + + @Test + @DisplayName("사용자는 투표 아이템명에 키워드가 포함된 투표 목록을 조회할 수 있다.") + void getVotesByItemNameTest() { + // given + final String keyword = item1.getName(); + + // when + final VoteGetByKeywordServiceResponse result = voteService.getVotesByKeyword( + keyword, + new CursorPageParameters(null, 1) + ); + + // then + assertThat(result.voteSummary().summaryCount()).isEqualTo(1); + assertThat(result.voteSummary().summaries().get(0).voteInfo().id()).isEqualTo(vote2.getId()); + assertThat(result.totalVoteCount()).isEqualTo(2); + } + + @Test + @DisplayName("사용자는 투표 제목에 키워드가 포함된 투표 목록을 조회할 수 있다.") + void getVotesByTitleTest() { + // given + final String keyword = "농린이"; + + // when + final VoteGetByKeywordServiceResponse result = voteService.getVotesByKeyword( + keyword, + new CursorPageParameters(null, 1) + ); + + // then + assertThat(result.voteSummary().summaryCount()).isEqualTo(1); + assertThat(result.voteSummary().summaries().get(0).voteInfo().id()).isEqualTo(vote2.getId()); + assertThat(result.totalVoteCount()).isEqualTo(1); + } + } } diff --git a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoteSetUp.java b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoteSetUp.java index 5ccf02f50..cefefa3b2 100644 --- a/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoteSetUp.java +++ b/lime-domain/src/testFixtures/java/com/programmers/lime/domains/vote/domain/setup/VoteSetUp.java @@ -23,4 +23,8 @@ public Vote saveOne( return voteRepository.save(vote); } + + public Vote save(final Vote vote) { + return voteRepository.save(vote); + } }