diff --git a/src/main/java/com/debatetimer/controller/poll/PollController.java b/src/main/java/com/debatetimer/controller/poll/PollController.java new file mode 100644 index 00000000..88da0830 --- /dev/null +++ b/src/main/java/com/debatetimer/controller/poll/PollController.java @@ -0,0 +1,49 @@ +package com.debatetimer.controller.poll; + +import com.debatetimer.controller.auth.AuthMember; +import com.debatetimer.domain.member.Member; +import com.debatetimer.dto.poll.response.PollCreateResponse; +import com.debatetimer.dto.poll.response.PollInfoResponse; +import com.debatetimer.service.poll.PollService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class PollController { + + private final PollService pollService; + + @PostMapping("/api/polls/{tableId}") + @ResponseStatus(HttpStatus.CREATED) + public PollCreateResponse createPoll( + @AuthMember Member member, + @PathVariable(name = "tableId") long tableId + ) { + return pollService.create(tableId, member); + } + + @GetMapping("/api/polls/{pollId}") + @ResponseStatus(HttpStatus.OK) + public PollInfoResponse getPollInfo( + @AuthMember Member member, + @PathVariable(name = "pollId") long pollId + ) { + return pollService.getPollInfo(pollId, member); + } + + @PatchMapping("/api/polls/{pollId}") + @ResponseStatus(HttpStatus.OK) + public PollInfoResponse finishPoll( + @AuthMember Member member, + @PathVariable(name = "pollId") long pollId + ) { + return pollService.finishPoll(pollId, member); + } +} diff --git a/src/main/java/com/debatetimer/domain/poll/ParticipantName.java b/src/main/java/com/debatetimer/domain/poll/ParticipantName.java new file mode 100644 index 00000000..86c012a2 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/ParticipantName.java @@ -0,0 +1,22 @@ +package com.debatetimer.domain.poll; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import lombok.Getter; + +@Getter +public class ParticipantName { + + private final String value; + + public ParticipantName(String value) { + validateName(value); + this.value = value; + } + + private void validateName(String value) { + if (value == null || value.isBlank()) { + throw new DTClientErrorException(ClientErrorCode.INVALID_POLL_PARTICIPANT_NAME); + } + } +} diff --git a/src/main/java/com/debatetimer/domain/poll/ParticipateCode.java b/src/main/java/com/debatetimer/domain/poll/ParticipateCode.java new file mode 100644 index 00000000..29c323a2 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/ParticipateCode.java @@ -0,0 +1,22 @@ +package com.debatetimer.domain.poll; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import lombok.Getter; + +@Getter +public class ParticipateCode { + + private final String value; + + public ParticipateCode(String value) { + validateName(value); + this.value = value; + } + + private void validateName(String value) { + if (value == null || value.isBlank()) { + throw new DTClientErrorException(ClientErrorCode.INVALID_POLL_PARTICIPANT_CODE); + } + } +} diff --git a/src/main/java/com/debatetimer/domain/poll/Poll.java b/src/main/java/com/debatetimer/domain/poll/Poll.java new file mode 100644 index 00000000..fd1c15a5 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/Poll.java @@ -0,0 +1,29 @@ +package com.debatetimer.domain.poll; + +import com.debatetimer.domain.customize.Agenda; +import com.debatetimer.domain.customize.TeamName; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class Poll { + + private final Long id; + private final long tableId; + private final long memberId; + private final PollStatus status; + private final TeamName prosTeamName; + private final TeamName consTeamName; + private final Agenda agenda; + + public Poll(long tableId, long memberId, String prosTeamName, String consTeamName, String agenda) { + this(null, tableId, memberId, PollStatus.PROGRESS, + new TeamName(prosTeamName), new TeamName(consTeamName), new Agenda(agenda)); + } + + public Poll(Long id, long tableId, long memberId, PollStatus status, + String prosTeamName, String consTeamName, String agenda) { + this(id, tableId, memberId, status, new TeamName(prosTeamName), new TeamName(consTeamName), new Agenda(agenda)); + } +} diff --git a/src/main/java/com/debatetimer/domain/poll/Vote.java b/src/main/java/com/debatetimer/domain/poll/Vote.java new file mode 100644 index 00000000..a69f946a --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/Vote.java @@ -0,0 +1,19 @@ +package com.debatetimer.domain.poll; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class Vote { + + private final Long id; + private final long pollId; + private final VoteTeam team; + private final ParticipantName name; + private final ParticipateCode code; + + public Vote(Long id, long pollId, VoteTeam team, String name, String code) { + this(id, pollId, team, new ParticipantName(name), new ParticipateCode(code)); + } +} diff --git a/src/main/java/com/debatetimer/domain/poll/VoteInfo.java b/src/main/java/com/debatetimer/domain/poll/VoteInfo.java new file mode 100644 index 00000000..36734ebb --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/VoteInfo.java @@ -0,0 +1,19 @@ +package com.debatetimer.domain.poll; + +import lombok.Getter; + +@Getter +public class VoteInfo { + + private final long pollId; + private final long totalCount; + private final long prosCount; + private final long consCount; + + public VoteInfo(long pollId, long prosCount, long consCount) { + this.pollId = pollId; + this.totalCount = prosCount + consCount; + this.prosCount = prosCount; + this.consCount = consCount; + } +} diff --git a/src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java new file mode 100644 index 00000000..850c53bd --- /dev/null +++ b/src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java @@ -0,0 +1,24 @@ +package com.debatetimer.domainrepository.poll; + +import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.member.Member; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.repository.customize.CustomizeTableRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +@Repository +@RequiredArgsConstructor +public class CustomizeTableDomainRepository { + + private final CustomizeTableRepository customizeTableRepository; + + @Transactional(readOnly = true) + public CustomizeTable getByIdAndMember(long tableId, Member member) { + return customizeTableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)) + .toDomain(); + } +} diff --git a/src/main/java/com/debatetimer/domainrepository/poll/PollDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/poll/PollDomainRepository.java new file mode 100644 index 00000000..fa46adfb --- /dev/null +++ b/src/main/java/com/debatetimer/domainrepository/poll/PollDomainRepository.java @@ -0,0 +1,42 @@ +package com.debatetimer.domainrepository.poll; + +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.repository.poll.PollRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +@Repository +@RequiredArgsConstructor +public class PollDomainRepository { + + private final PollRepository pollRepository; + + @Transactional + public Poll create(Poll poll) { + PollEntity pollEntity = new PollEntity(poll); + return pollRepository.save(pollEntity) + .toDomain(); + } + + @Transactional(readOnly = true) + public Poll getByIdAndMemberId(long id, long memberId) { + return findPoll(id, memberId) + .toDomain(); + } + + @Transactional + public Poll finishPoll(long pollId, long memberId) { + PollEntity pollEntity = findPoll(pollId, memberId); + pollEntity.updateToDone(); + return pollEntity.toDomain(); + } + + private PollEntity findPoll(long pollId, long memberId) { + return pollRepository.findByIdAndMemberId(pollId, memberId) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.POLL_NOT_FOUND)); + } +} diff --git a/src/main/java/com/debatetimer/domainrepository/poll/VoteDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/poll/VoteDomainRepository.java new file mode 100644 index 00000000..97f8dd1f --- /dev/null +++ b/src/main/java/com/debatetimer/domainrepository/poll/VoteDomainRepository.java @@ -0,0 +1,31 @@ +package com.debatetimer.domainrepository.poll; + +import com.debatetimer.domain.poll.VoteInfo; +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.entity.poll.VoteEntity; +import com.debatetimer.repository.poll.VoteRepository; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class VoteDomainRepository { + + private final VoteRepository voteRepository; + + public VoteInfo findVoteInfoByPollId(long pollId) { + List pollVotes = voteRepository.findAllByPollId(pollId); + return countVotes(pollId, pollVotes); + } + + private VoteInfo countVotes(long pollId, List voteEntities) { + Map teamCount = voteEntities.stream() + .collect(Collectors.groupingBy(VoteEntity::getTeam, Collectors.counting())); + long prosCount = teamCount.getOrDefault(VoteTeam.PROS, 0L); + long consCount = teamCount.getOrDefault(VoteTeam.CONS, 0L); + return new VoteInfo(pollId, prosCount, consCount); + } +} diff --git a/src/main/java/com/debatetimer/dto/poll/response/PollCreateResponse.java b/src/main/java/com/debatetimer/dto/poll/response/PollCreateResponse.java new file mode 100644 index 00000000..462ec3d5 --- /dev/null +++ b/src/main/java/com/debatetimer/dto/poll/response/PollCreateResponse.java @@ -0,0 +1,16 @@ +package com.debatetimer.dto.poll.response; + +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.PollStatus; + +public record PollCreateResponse( + long id, + PollStatus status, + String prosTeamName, + String consTeamName +) { + + public PollCreateResponse(Poll poll) { + this(poll.getId(), poll.getStatus(), poll.getProsTeamName().getValue(), poll.getConsTeamName().getValue()); + } +} diff --git a/src/main/java/com/debatetimer/dto/poll/response/PollInfoResponse.java b/src/main/java/com/debatetimer/dto/poll/response/PollInfoResponse.java new file mode 100644 index 00000000..69f3f6ae --- /dev/null +++ b/src/main/java/com/debatetimer/dto/poll/response/PollInfoResponse.java @@ -0,0 +1,28 @@ +package com.debatetimer.dto.poll.response; + +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domain.poll.VoteInfo; + +public record PollInfoResponse( + long id, + PollStatus status, + String prosTeamName, + String consTeamName, + long totalCount, + long prosCount, + long consCount +) { + + public PollInfoResponse(Poll poll, VoteInfo voteInfo) { + this( + poll.getId(), + poll.getStatus(), + poll.getProsTeamName().getValue(), + poll.getConsTeamName().getValue(), + voteInfo.getTotalCount(), + voteInfo.getProsCount(), + voteInfo.getConsCount() + ); + } +} diff --git a/src/main/java/com/debatetimer/entity/poll/PollEntity.java b/src/main/java/com/debatetimer/entity/poll/PollEntity.java index d4700a3d..bb4e5433 100644 --- a/src/main/java/com/debatetimer/entity/poll/PollEntity.java +++ b/src/main/java/com/debatetimer/entity/poll/PollEntity.java @@ -1,7 +1,9 @@ package com.debatetimer.entity.poll; +import com.debatetimer.domain.poll.Poll; import com.debatetimer.domain.poll.PollStatus; import com.debatetimer.entity.customize.BaseTimeEntity; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -12,9 +14,11 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity +@Getter @Table(name = "poll") @NoArgsConstructor(access = AccessLevel.PROTECTED) public class PollEntity extends BaseTimeEntity { @@ -23,8 +27,12 @@ public class PollEntity extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(name = "table_id") private long tableId; + @Column(name = "member_id") + private long memberId; + @NotNull @Enumerated(EnumType.STRING) private PollStatus status; @@ -36,4 +44,22 @@ public class PollEntity extends BaseTimeEntity { private String consTeamName; private String agenda; + + public PollEntity(Poll poll) { + this.id = poll.getId(); + this.tableId = poll.getTableId(); + this.memberId = poll.getMemberId(); + this.status = poll.getStatus(); + this.prosTeamName = poll.getProsTeamName().getValue(); + this.consTeamName = poll.getConsTeamName().getValue(); + this.agenda = poll.getAgenda().getValue(); + } + + public void updateToDone() { + this.status = PollStatus.DONE; + } + + public Poll toDomain() { + return new Poll(id, tableId, memberId, status, prosTeamName, consTeamName, agenda); + } } diff --git a/src/main/java/com/debatetimer/entity/poll/VoteEntity.java b/src/main/java/com/debatetimer/entity/poll/VoteEntity.java index 9b01e7f4..47dd0a2c 100644 --- a/src/main/java/com/debatetimer/entity/poll/VoteEntity.java +++ b/src/main/java/com/debatetimer/entity/poll/VoteEntity.java @@ -1,5 +1,6 @@ package com.debatetimer.entity.poll; +import com.debatetimer.domain.poll.Vote; import com.debatetimer.domain.poll.VoteTeam; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -14,11 +15,15 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity +@Getter @Table(name = "vote") @NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor public class VoteEntity { @Id @@ -39,4 +44,8 @@ public class VoteEntity { @NotBlank private String participantCode; + + public Vote toDomain() { + return new Vote(id, poll.getId(), team, name, participantCode); + } } diff --git a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java index 59eb8cb9..f0672c70 100644 --- a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java +++ b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java @@ -48,8 +48,12 @@ public enum ClientErrorCode implements ResponseErrorCode { "토론 주제는 1자 이상 %d자 이하여야 합니다.".formatted(Agenda.AGENDA_MAX_LENGTH) ), + INVALID_POLL_PARTICIPANT_NAME(HttpStatus.BAD_REQUEST, "잘못된 투표자 이름입니다"), + INVALID_POLL_PARTICIPANT_CODE(HttpStatus.BAD_REQUEST, "잘못된 투표참여 코드입니다"), + TABLE_NOT_FOUND(HttpStatus.NOT_FOUND, "토론 테이블을 찾을 수 없습니다."), NOT_TABLE_OWNER(HttpStatus.UNAUTHORIZED, "테이블을 소유한 회원이 아닙니다."), + POLL_NOT_FOUND(HttpStatus.NOT_FOUND, "투표를 찾을 수 없습니다."), UNAUTHORIZED_MEMBER(HttpStatus.UNAUTHORIZED, "접근 권한이 없습니다"), EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "토큰 기한이 만료되었습니다"), diff --git a/src/main/java/com/debatetimer/repository/poll/PollRepository.java b/src/main/java/com/debatetimer/repository/poll/PollRepository.java new file mode 100644 index 00000000..121d1627 --- /dev/null +++ b/src/main/java/com/debatetimer/repository/poll/PollRepository.java @@ -0,0 +1,10 @@ +package com.debatetimer.repository.poll; + +import com.debatetimer.entity.poll.PollEntity; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PollRepository extends JpaRepository { + + Optional findByIdAndMemberId(long id, long memberId); +} diff --git a/src/main/java/com/debatetimer/repository/poll/VoteRepository.java b/src/main/java/com/debatetimer/repository/poll/VoteRepository.java new file mode 100644 index 00000000..a9709200 --- /dev/null +++ b/src/main/java/com/debatetimer/repository/poll/VoteRepository.java @@ -0,0 +1,10 @@ +package com.debatetimer.repository.poll; + +import com.debatetimer.entity.poll.VoteEntity; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface VoteRepository extends JpaRepository { + + List findAllByPollId(long pollId); +} diff --git a/src/main/java/com/debatetimer/service/poll/PollService.java b/src/main/java/com/debatetimer/service/poll/PollService.java new file mode 100644 index 00000000..a6409fdb --- /dev/null +++ b/src/main/java/com/debatetimer/service/poll/PollService.java @@ -0,0 +1,46 @@ +package com.debatetimer.service.poll; + +import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.VoteInfo; +import com.debatetimer.domainrepository.poll.CustomizeTableDomainRepository; +import com.debatetimer.domainrepository.poll.PollDomainRepository; +import com.debatetimer.domainrepository.poll.VoteDomainRepository; +import com.debatetimer.dto.poll.response.PollCreateResponse; +import com.debatetimer.dto.poll.response.PollInfoResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class PollService { + + private final CustomizeTableDomainRepository customizeTableDomainRepository; + private final PollDomainRepository pollDomainRepository; + private final VoteDomainRepository voteDomainRepository; + + @Transactional + public PollCreateResponse create(long tableId, Member member) { + CustomizeTable table = customizeTableDomainRepository.getByIdAndMember(tableId, member); + Poll poll = new Poll(table.getId(), member.getId(), table.getProsTeamName(), + table.getConsTeamName(), table.getAgenda()); + Poll savedPoll = pollDomainRepository.create(poll); + return new PollCreateResponse(savedPoll); + } + + @Transactional(readOnly = true) + public PollInfoResponse getPollInfo(long pollId, Member member) { + Poll poll = pollDomainRepository.getByIdAndMemberId(pollId, member.getId()); + VoteInfo voteInfo = voteDomainRepository.findVoteInfoByPollId(pollId); + return new PollInfoResponse(poll, voteInfo); + } + + @Transactional + public PollInfoResponse finishPoll(long pollId, Member member) { + Poll poll = pollDomainRepository.finishPoll(pollId, member.getId()); + VoteInfo voteInfo = voteDomainRepository.findVoteInfoByPollId(pollId); + return new PollInfoResponse(poll, voteInfo); + } +} diff --git a/src/main/resources/db/migration/V11__add_memberId_into_poll.sql b/src/main/resources/db/migration/V11__add_memberId_into_poll.sql new file mode 100644 index 00000000..08772417 --- /dev/null +++ b/src/main/resources/db/migration/V11__add_memberId_into_poll.sql @@ -0,0 +1,2 @@ +ALTER TABLE poll + ADD COLUMN member_id BIGINT NOT NULL diff --git a/src/test/java/com/debatetimer/controller/BaseControllerTest.java b/src/test/java/com/debatetimer/controller/BaseControllerTest.java index 2ca415ea..83d35dda 100644 --- a/src/test/java/com/debatetimer/controller/BaseControllerTest.java +++ b/src/test/java/com/debatetimer/controller/BaseControllerTest.java @@ -12,7 +12,9 @@ import com.debatetimer.fixture.CustomizeTimeBoxGenerator; import com.debatetimer.fixture.HeaderGenerator; import com.debatetimer.fixture.MemberGenerator; +import com.debatetimer.fixture.PollGenerator; import com.debatetimer.fixture.TokenGenerator; +import com.debatetimer.fixture.VoteGenerator; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.navercorp.fixturemonkey.ArbitraryBuilder; import com.navercorp.fixturemonkey.FixtureMonkey; @@ -45,6 +47,12 @@ public abstract class BaseControllerTest { @Autowired protected CustomizeTimeBoxGenerator customizeTimeBoxGenerator; + @Autowired + protected PollGenerator pollGenerator; + + @Autowired + protected VoteGenerator voteGenerator; + @Autowired protected HeaderGenerator headerGenerator; diff --git a/src/test/java/com/debatetimer/controller/BaseDocumentTest.java b/src/test/java/com/debatetimer/controller/BaseDocumentTest.java index 206c8cda..40c80cc1 100644 --- a/src/test/java/com/debatetimer/controller/BaseDocumentTest.java +++ b/src/test/java/com/debatetimer/controller/BaseDocumentTest.java @@ -12,6 +12,7 @@ import com.debatetimer.service.auth.AuthService; import com.debatetimer.service.customize.CustomizeService; import com.debatetimer.service.member.MemberService; +import com.debatetimer.service.poll.PollService; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.filter.log.RequestLoggingFilter; @@ -62,6 +63,9 @@ public abstract class BaseDocumentTest { @MockitoBean protected AuthService authService; + @MockitoBean + protected PollService pollService; + @MockitoBean protected AuthManager authManager; diff --git a/src/test/java/com/debatetimer/controller/Tag.java b/src/test/java/com/debatetimer/controller/Tag.java index c7dfa897..39a3c588 100644 --- a/src/test/java/com/debatetimer/controller/Tag.java +++ b/src/test/java/com/debatetimer/controller/Tag.java @@ -6,6 +6,7 @@ public enum Tag { PARLIAMENTARY_API("Parliamentary Table API"), TIME_BASED_API("Time Based Table API"), CUSTOMIZE_API("Customize Table API"), + POLL_API("Poll API"), ; private final String displayName; diff --git a/src/test/java/com/debatetimer/controller/poll/PollControllerTest.java b/src/test/java/com/debatetimer/controller/poll/PollControllerTest.java new file mode 100644 index 00000000..22487d85 --- /dev/null +++ b/src/test/java/com/debatetimer/controller/poll/PollControllerTest.java @@ -0,0 +1,93 @@ +package com.debatetimer.controller.poll; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.controller.BaseControllerTest; +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.dto.poll.response.PollInfoResponse; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import io.restassured.http.ContentType; +import io.restassured.http.Headers; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; + +class PollControllerTest extends BaseControllerTest { + + @Nested + class CreatePoll { + + @Test + void 선거를_생성할_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + Headers headers = headerGenerator.generateAccessTokenHeader(member); + + given() + .contentType(ContentType.JSON) + .headers(headers) + .pathParam("tableId", table.getId()) + .when().post("/api/polls/{tableId}") + .then().statusCode(HttpStatus.CREATED.value()); + } + } + + @Nested + class GetPollInfo { + + @Test + void 선거정보를_읽을_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); + Headers headers = headerGenerator.generateAccessTokenHeader(member); + + PollInfoResponse response = given() + .contentType(ContentType.JSON) + .headers(headers) + .pathParam("pollId", pollEntity.getId()) + .when().get("/api/polls/{pollId}") + .then().statusCode(HttpStatus.OK.value()) + .extract().as(PollInfoResponse.class); + + assertAll( + () -> assertThat(response.id()).isEqualTo(pollEntity.getId()), + () -> assertThat(response.prosTeamName()).isEqualTo(pollEntity.getProsTeamName()), + () -> assertThat(response.consTeamName()).isEqualTo(pollEntity.getConsTeamName()), + () -> assertThat(response.status()).isEqualTo(pollEntity.getStatus()), + () -> assertThat(response.totalCount()).isEqualTo(3L), + () -> assertThat(response.prosCount()).isEqualTo(2L), + () -> assertThat(response.consCount()).isEqualTo(1L) + ); + } + } + + @Nested + class FinishPoll { + + @Test + void 선거정보를_완료상태로_변경한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + Headers headers = headerGenerator.generateAccessTokenHeader(member); + + PollInfoResponse response = given() + .contentType(ContentType.JSON) + .headers(headers) + .pathParam("pollId", pollEntity.getId()) + .when().patch("/api/polls/{pollId}") + .then().statusCode(HttpStatus.OK.value()) + .extract().as(PollInfoResponse.class); + + assertThat(response.status()).isEqualTo(PollStatus.DONE); + } + } +} diff --git a/src/test/java/com/debatetimer/controller/poll/PollDocumentTest.java b/src/test/java/com/debatetimer/controller/poll/PollDocumentTest.java new file mode 100644 index 00000000..e0cd3b42 --- /dev/null +++ b/src/test/java/com/debatetimer/controller/poll/PollDocumentTest.java @@ -0,0 +1,168 @@ +package com.debatetimer.controller.poll; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doReturn; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; + +import com.debatetimer.controller.BaseDocumentTest; +import com.debatetimer.controller.RestDocumentationRequest; +import com.debatetimer.controller.RestDocumentationResponse; +import com.debatetimer.controller.Tag; +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.dto.poll.response.PollCreateResponse; +import com.debatetimer.dto.poll.response.PollInfoResponse; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; + +public class PollDocumentTest extends BaseDocumentTest { + + @Nested + class CreatePoll { + + private final RestDocumentationRequest requestDocument = request() + .tag(Tag.POLL_API) + .summary("선거 생성") + .requestHeader( + headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") + ) + .pathParameter( + parameterWithName("tableId").description("테이블 ID") + ); + + private final RestDocumentationResponse responseDocument = response() + .responseBodyField( + fieldWithPath("id").type(NUMBER).description("선거 ID"), + fieldWithPath("status").type(STRING).description("선거 상태 - 진행중 : PROGRESS, 완료 : DONE"), + fieldWithPath("prosTeamName").type(STRING).description("찬성측 팀 이름"), + fieldWithPath("consTeamName").type(STRING).description("반대측 팀 이름") + ); + + @Test + void 선거_생성_성공() { + PollCreateResponse response = new PollCreateResponse(1l, PollStatus.PROGRESS, "찬성", "반대"); + doReturn(response).when(pollService).create(anyLong(), any(Member.class)); + + var document = document("poll/post", 201) + .request(requestDocument) + .response(responseDocument) + .build(); + + given(document) + .contentType(ContentType.JSON) + .headers(EXIST_MEMBER_HEADER) + .pathParam("tableId", 1l) + .when().post("/api/polls/{tableId}") + .then().statusCode(201); + } + } + + @Nested + class GetPollInfo { + + private final RestDocumentationRequest requestDocument = request() + .tag(Tag.POLL_API) + .summary("선거 정보 조회") + .requestHeader( + headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") + ) + .pathParameter( + parameterWithName("pollId").description("선거 ID") + ); + + private final RestDocumentationResponse responseDocument = response() + .responseBodyField( + fieldWithPath("id").type(NUMBER).description("선거 ID"), + fieldWithPath("status").type(STRING).description("선거 상태 - 진행중 : PROGRESS, 완료 : DONE"), + fieldWithPath("prosTeamName").type(STRING).description("찬성측 팀 이름"), + fieldWithPath("consTeamName").type(STRING).description("반대측 팀 이름"), + fieldWithPath("totalCount").type(NUMBER).description("전체 투표 수"), + fieldWithPath("prosCount").type(NUMBER).description("찬성 투표 수"), + fieldWithPath("consCount").type(NUMBER).description("반대 투표 수") + ); + + @Test + void 선거_정보_조회() { + PollInfoResponse response = new PollInfoResponse( + 1L, + PollStatus.PROGRESS, + "찬성", + "반대", + 3L, + 2L, + 1L + ); + doReturn(response).when(pollService).getPollInfo(anyLong(), any(Member.class)); + + var document = document("poll/get", 200) + .request(requestDocument) + .response(responseDocument) + .build(); + + given(document) + .contentType(ContentType.JSON) + .headers(EXIST_MEMBER_HEADER) + .pathParam("pollId", 1l) + .when().get("/api/polls/{pollId}") + .then().statusCode(200); + } + } + + @Nested + class FinishPoll { + + private final RestDocumentationRequest requestDocument = request() + .tag(Tag.POLL_API) + .summary("선거 완료") + .requestHeader( + headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") + ) + .pathParameter( + parameterWithName("pollId").description("선거 ID") + ); + + private final RestDocumentationResponse responseDocument = response() + .responseBodyField( + fieldWithPath("id").type(NUMBER).description("선거 ID"), + fieldWithPath("status").type(STRING).description("선거 상태 - 진행중 : PROGRESS, 완료 : DONE"), + fieldWithPath("prosTeamName").type(STRING).description("찬성측 팀 이름"), + fieldWithPath("consTeamName").type(STRING).description("반대측 팀 이름"), + fieldWithPath("totalCount").type(NUMBER).description("전체 투표 수"), + fieldWithPath("prosCount").type(NUMBER).description("찬성 투표 수"), + fieldWithPath("consCount").type(NUMBER).description("반대 투표 수") + ); + + @Test + void 선거_완료() { + PollInfoResponse response = new PollInfoResponse( + 1L, + PollStatus.DONE, + "찬성", + "반대", + 3L, + 2L, + 1L + ); + doReturn(response).when(pollService).finishPoll(anyLong(), any(Member.class)); + + var document = document("poll/patch", 200) + .request(requestDocument) + .response(responseDocument) + .build(); + + given(document) + .contentType(ContentType.JSON) + .headers(EXIST_MEMBER_HEADER) + .pathParam("pollId", 1l) + .when().patch("/api/polls/{pollId}") + .then().statusCode(200); + } + } +} diff --git a/src/test/java/com/debatetimer/domain/poll/ParticipantNameTest.java b/src/test/java/com/debatetimer/domain/poll/ParticipantNameTest.java new file mode 100644 index 00000000..a6f25f32 --- /dev/null +++ b/src/test/java/com/debatetimer/domain/poll/ParticipantNameTest.java @@ -0,0 +1,24 @@ +package com.debatetimer.domain.poll; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.fixture.NullAndEmptyAndBlankSource; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; + +class ParticipantNameTest { + + @Nested + class Validate { + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 투표참여자_이름은_널이거나_빈_값_일_수_없다(String name) { + assertThatThrownBy(() -> new ParticipantName(name)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_POLL_PARTICIPANT_NAME.getMessage()); + } + } +} diff --git a/src/test/java/com/debatetimer/domain/poll/ParticipateCodeTest.java b/src/test/java/com/debatetimer/domain/poll/ParticipateCodeTest.java new file mode 100644 index 00000000..d8d5245c --- /dev/null +++ b/src/test/java/com/debatetimer/domain/poll/ParticipateCodeTest.java @@ -0,0 +1,24 @@ +package com.debatetimer.domain.poll; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.fixture.NullAndEmptyAndBlankSource; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; + +class ParticipateCodeTest { + + @Nested + class Validate { + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 투표_참여_코드는_널이거나_빈_값_일_수_없다(String participatecode) { + assertThatThrownBy(() -> new ParticipateCode(participatecode)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_POLL_PARTICIPANT_CODE.getMessage()); + } + } +} diff --git a/src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java new file mode 100644 index 00000000..37ac4fba --- /dev/null +++ b/src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java @@ -0,0 +1,31 @@ +package com.debatetimer.domainrepository; + +import com.debatetimer.DataBaseCleaner; +import com.debatetimer.fixture.CustomizeTableGenerator; +import com.debatetimer.fixture.MemberGenerator; +import com.debatetimer.fixture.PollGenerator; +import com.debatetimer.fixture.VoteGenerator; +import com.debatetimer.repository.poll.PollRepository; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@ExtendWith(DataBaseCleaner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +public abstract class BaseDomainRepositoryTest { + + @Autowired + protected MemberGenerator memberGenerator; + + @Autowired + protected CustomizeTableGenerator customizeTableGenerator; + + @Autowired + protected PollGenerator pollGenerator; + + @Autowired + protected VoteGenerator voteGenerator; + + @Autowired + protected PollRepository pollRepository; +} diff --git a/src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java new file mode 100644 index 00000000..c97849e4 --- /dev/null +++ b/src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java @@ -0,0 +1,74 @@ +package com.debatetimer.domainrepository.poll; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domainrepository.BaseDomainRepositoryTest; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import java.util.Optional; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class PollDomainRepositoryTest extends BaseDomainRepositoryTest { + + @Autowired + private PollDomainRepository pollDomainRepository; + + @Nested + class Create { + + @Test + void 선거를_생성한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + Poll poll = new Poll(table.getId(), member.getId(), "찬성", "반대", "주제"); + + Poll createdPoll = pollDomainRepository.create(poll); + + Optional foundPollEntity = pollRepository.findById(createdPoll.getId()); + assertThat(foundPollEntity).isPresent(); + } + } + + @Nested + class GetByIdAndMemberId { + + @Test + void 회원이_개최한_선거를_가져온다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + + Poll foundPoll = pollDomainRepository.getByIdAndMemberId(pollEntity.getId(), member.getId()); + + assertAll( + () -> assertThat(foundPoll.getId()).isEqualTo(pollEntity.getId()), + () -> assertThat(foundPoll.getAgenda().getValue()).isEqualTo(pollEntity.getAgenda()), + () -> assertThat(foundPoll.getStatus()).isEqualTo(pollEntity.getStatus()), + () -> assertThat(foundPoll.getMemberId()).isEqualTo(pollEntity.getMemberId()), + () -> assertThat(foundPoll.getProsTeamName().getValue()).isEqualTo(pollEntity.getProsTeamName()), + () -> assertThat(foundPoll.getConsTeamName().getValue()).isEqualTo(pollEntity.getConsTeamName()) + ); + } + } + + @Nested + class FinishPoll { + + @Test + void 선거를_완료_상태로_변경한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + + Poll updatedPoll = pollDomainRepository.finishPoll(pollEntity.getId(), member.getId()); + + assertThat(updatedPoll.getStatus()).isEqualTo(PollStatus.DONE); + } + } +} diff --git a/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java new file mode 100644 index 00000000..068e782e --- /dev/null +++ b/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java @@ -0,0 +1,45 @@ +package com.debatetimer.domainrepository.poll; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domain.poll.VoteInfo; +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.domainrepository.BaseDomainRepositoryTest; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class VoteDomainRepositoryTest extends BaseDomainRepositoryTest { + + @Autowired + private VoteDomainRepository voteDomainRepository; + + @Nested + class GetVoteInfo { + + @Test + void 팀별_투표_현황을_알_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); + + VoteInfo voteInfo = voteDomainRepository.findVoteInfoByPollId(pollEntity.getId()); + + assertAll( + () -> assertThat(voteInfo.getPollId()).isEqualTo(pollEntity.getId()), + () -> assertThat(voteInfo.getTotalCount()).isEqualTo(3L), + () -> assertThat(voteInfo.getProsCount()).isEqualTo(2L), + () -> assertThat(voteInfo.getConsCount()).isEqualTo(1L) + ); + } + } + +} diff --git a/src/test/java/com/debatetimer/fixture/PollGenerator.java b/src/test/java/com/debatetimer/fixture/PollGenerator.java new file mode 100644 index 00000000..24059cba --- /dev/null +++ b/src/test/java/com/debatetimer/fixture/PollGenerator.java @@ -0,0 +1,31 @@ +package com.debatetimer.fixture; + +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.repository.poll.PollRepository; +import org.springframework.stereotype.Component; + +@Component +public class PollGenerator { + + private final PollRepository pollRepository; + + public PollGenerator(final PollRepository pollRepository) { + this.pollRepository = pollRepository; + } + + public PollEntity generate(CustomizeTableEntity customizeTableEntity, PollStatus status) { + Poll poll = new Poll( + null, + customizeTableEntity.getId(), + customizeTableEntity.getMember().getId(), + status, + "찬성", + "반대", + "주제" + ); + return pollRepository.save(new PollEntity(poll)); + } +} diff --git a/src/test/java/com/debatetimer/fixture/VoteGenerator.java b/src/test/java/com/debatetimer/fixture/VoteGenerator.java new file mode 100644 index 00000000..9efdd80c --- /dev/null +++ b/src/test/java/com/debatetimer/fixture/VoteGenerator.java @@ -0,0 +1,23 @@ +package com.debatetimer.fixture; + +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.entity.poll.VoteEntity; +import com.debatetimer.repository.poll.VoteRepository; +import java.util.UUID; +import org.springframework.stereotype.Component; + +@Component +public class VoteGenerator { + + private final VoteRepository voteRepository; + + public VoteGenerator(VoteRepository voteRepository) { + this.voteRepository = voteRepository; + } + + public VoteEntity generate(PollEntity pollEntity, VoteTeam team, String name) { + VoteEntity vote = new VoteEntity(null, pollEntity, team, name, UUID.randomUUID().toString()); + return voteRepository.save(vote); + } +} diff --git a/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java b/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java index 2b7d1c04..87b5404b 100644 --- a/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java @@ -14,7 +14,7 @@ MemberGenerator.class, CustomizeTableGenerator.class, CustomizeTimeBoxGenerator.class, - BellGenerator.class + BellGenerator.class, }) @DataJpaTest public abstract class BaseRepositoryTest { diff --git a/src/test/java/com/debatetimer/service/BaseServiceTest.java b/src/test/java/com/debatetimer/service/BaseServiceTest.java index 6220cc2a..a68e418d 100644 --- a/src/test/java/com/debatetimer/service/BaseServiceTest.java +++ b/src/test/java/com/debatetimer/service/BaseServiceTest.java @@ -5,10 +5,13 @@ import com.debatetimer.fixture.CustomizeTableGenerator; import com.debatetimer.fixture.CustomizeTimeBoxGenerator; import com.debatetimer.fixture.MemberGenerator; +import com.debatetimer.fixture.PollGenerator; +import com.debatetimer.fixture.VoteGenerator; import com.debatetimer.repository.customize.BellRepository; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import com.debatetimer.repository.member.MemberRepository; +import com.debatetimer.repository.poll.PollRepository; import java.util.List; import java.util.stream.IntStream; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,6 +34,9 @@ public abstract class BaseServiceTest { @Autowired protected BellRepository bellRepository; + @Autowired + protected PollRepository pollRepository; + @Autowired protected MemberGenerator memberGenerator; @@ -43,6 +49,12 @@ public abstract class BaseServiceTest { @Autowired protected BellGenerator bellGenerator; + @Autowired + protected PollGenerator pollGenerator; + + @Autowired + protected VoteGenerator voteGenerator; + protected void runAtSameTime(int count, Runnable task) throws InterruptedException { List threads = IntStream.range(0, count) .mapToObj(i -> new Thread(task)) diff --git a/src/test/java/com/debatetimer/service/poll/PollServiceTest.java b/src/test/java/com/debatetimer/service/poll/PollServiceTest.java new file mode 100644 index 00000000..fd5c96c2 --- /dev/null +++ b/src/test/java/com/debatetimer/service/poll/PollServiceTest.java @@ -0,0 +1,79 @@ +package com.debatetimer.service.poll; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.dto.poll.response.PollCreateResponse; +import com.debatetimer.dto.poll.response.PollInfoResponse; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.service.BaseServiceTest; +import java.util.Optional; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class PollServiceTest extends BaseServiceTest { + + @Autowired + private PollService pollService; + + @Nested + class CreatePoll { + + @Test + void 선거를_생성한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + + PollCreateResponse createdPoll = pollService.create(table.getId(), member); + + Optional foundPoll = pollRepository.findById(createdPoll.id()); + assertThat(foundPoll).isPresent(); + } + } + + @Nested + class GetPollInfo { + + @Test + void 선거_정보를_읽어온다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); + + PollInfoResponse pollInfo = pollService.getPollInfo(table.getId(), member); + + assertAll( + () -> assertThat(pollInfo.id()).isEqualTo(pollEntity.getId()), + () -> assertThat(pollInfo.prosTeamName()).isEqualTo(pollEntity.getProsTeamName()), + () -> assertThat(pollInfo.consTeamName()).isEqualTo(pollEntity.getConsTeamName()), + () -> assertThat(pollInfo.status()).isEqualTo(pollEntity.getStatus()), + () -> assertThat(pollInfo.totalCount()).isEqualTo(3L), + () -> assertThat(pollInfo.prosCount()).isEqualTo(2L), + () -> assertThat(pollInfo.consCount()).isEqualTo(1L) + ); + } + } + + @Nested + class FinishPoll { + + @Test + void 선거를_완료상태로_변경한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + pollGenerator.generate(table, PollStatus.PROGRESS); + + PollInfoResponse pollInfo = pollService.finishPoll(table.getId(), member); + + assertThat(pollInfo.status()).isEqualTo(PollStatus.DONE); + } + } +}