diff --git a/src/main/java/com/debatetimer/config/ErrorDecoderConfig.java b/src/main/java/com/debatetimer/config/ErrorDecoderConfig.java new file mode 100644 index 00000000..5fee2c04 --- /dev/null +++ b/src/main/java/com/debatetimer/config/ErrorDecoderConfig.java @@ -0,0 +1,33 @@ +package com.debatetimer.config; + +import com.debatetimer.exception.decoder.H2ErrorDecoder; +import com.debatetimer.exception.decoder.MySqlErrorDecoder; +import com.debatetimer.exception.decoder.RepositoryErrorDecoder; +import lombok.NoArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) +public class ErrorDecoderConfig { + + @Profile({"dev", "prod"}) + @Configuration + public static class MySqlErrorDecoderConfig { + + @Bean + public RepositoryErrorDecoder mySqlErrorDecoder() { + return new MySqlErrorDecoder(); + } + } + + @Profile({"test", "local"}) + @Configuration + public static class H2ErrorDecoderConfig { + + @Bean + public RepositoryErrorDecoder h2ErrorDecoder() { + return new H2ErrorDecoder(); + } + } +} diff --git a/src/main/java/com/debatetimer/controller/poll/VoteController.java b/src/main/java/com/debatetimer/controller/poll/VoteController.java new file mode 100644 index 00000000..64f53365 --- /dev/null +++ b/src/main/java/com/debatetimer/controller/poll/VoteController.java @@ -0,0 +1,37 @@ +package com.debatetimer.controller.poll; + +import com.debatetimer.dto.poll.request.VoteRequest; +import com.debatetimer.dto.poll.response.VoteCreateResponse; +import com.debatetimer.dto.poll.response.VoterPollInfoResponse; +import com.debatetimer.service.poll.VoteService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class VoteController { + + private final VoteService voteService; + + @GetMapping("/api/polls/{pollId}/votes") + @ResponseStatus(HttpStatus.OK) + public VoterPollInfoResponse getVotersPollInfo(@PathVariable long pollId) { + return voteService.getVoterPollInfo(pollId); + } + + @PostMapping("/api/polls/{pollId}/votes") + @ResponseStatus(HttpStatus.CREATED) + public VoteCreateResponse votePoll( + @PathVariable long pollId, + @RequestBody @Valid VoteRequest voteRequest + ) { + return voteService.vote(pollId, voteRequest); + } +} diff --git a/src/main/java/com/debatetimer/domain/customize/Bell.java b/src/main/java/com/debatetimer/domain/customize/Bell.java index 58fb4443..4766bb31 100644 --- a/src/main/java/com/debatetimer/domain/customize/Bell.java +++ b/src/main/java/com/debatetimer/domain/customize/Bell.java @@ -9,22 +9,18 @@ public class Bell { public static final int MAX_BELL_COUNT = 3; + private final BellType type; private final int time; private final int count; - public Bell(int time, int count) { - validateTime(time); + public Bell(BellType type, int time, int count) { + type.validateTime(time); validateCount(count); + this.type = type; this.time = time; this.count = count; } - private void validateTime(int time) { - if (time < 0) { - throw new DTClientErrorException(ClientErrorCode.INVALID_BELL_TIME); - } - } - private void validateCount(int count) { if (count <= 0 || count > MAX_BELL_COUNT) { throw new DTClientErrorException(ClientErrorCode.INVALID_BELL_COUNT); diff --git a/src/main/java/com/debatetimer/domain/customize/BellType.java b/src/main/java/com/debatetimer/domain/customize/BellType.java new file mode 100644 index 00000000..e90dc35d --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/BellType.java @@ -0,0 +1,25 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import java.util.function.IntPredicate; + +public enum BellType { + + AFTER_START(time -> time >= 0), + BEFORE_END(time -> time >= 0), + AFTER_END(time -> time <= 0), + ; + + private final IntPredicate timeValidator; + + BellType(IntPredicate timeValidator) { + this.timeValidator = timeValidator; + } + + public void validateTime(int time) { + if (!timeValidator.test(time)) { + throw new DTClientErrorException(ClientErrorCode.INVALID_BELL_TIME); + } + } +} diff --git a/src/main/java/com/debatetimer/domain/member/Member.java b/src/main/java/com/debatetimer/domain/member/Member.java index 9dec2246..923d17c5 100644 --- a/src/main/java/com/debatetimer/domain/member/Member.java +++ b/src/main/java/com/debatetimer/domain/member/Member.java @@ -1,6 +1,6 @@ package com.debatetimer.domain.member; -import com.debatetimer.entity.customize.BaseTimeEntity; +import com.debatetimer.entity.BaseTimeEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; diff --git a/src/main/java/com/debatetimer/domain/poll/Poll.java b/src/main/java/com/debatetimer/domain/poll/Poll.java index fd1c15a5..b3b23663 100644 --- a/src/main/java/com/debatetimer/domain/poll/Poll.java +++ b/src/main/java/com/debatetimer/domain/poll/Poll.java @@ -26,4 +26,8 @@ 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)); } + + public boolean isProgress() { + return status.isProgress(); + } } diff --git a/src/main/java/com/debatetimer/domain/poll/PollStatus.java b/src/main/java/com/debatetimer/domain/poll/PollStatus.java index f2d0ebad..98379b68 100644 --- a/src/main/java/com/debatetimer/domain/poll/PollStatus.java +++ b/src/main/java/com/debatetimer/domain/poll/PollStatus.java @@ -5,4 +5,8 @@ public enum PollStatus { PROGRESS, DONE, ; + + public boolean isProgress() { + return this == PROGRESS; + } } diff --git a/src/main/java/com/debatetimer/domain/poll/Vote.java b/src/main/java/com/debatetimer/domain/poll/Vote.java index a69f946a..6d14cf5f 100644 --- a/src/main/java/com/debatetimer/domain/poll/Vote.java +++ b/src/main/java/com/debatetimer/domain/poll/Vote.java @@ -13,6 +13,10 @@ public class Vote { private final ParticipantName name; private final ParticipateCode code; + public Vote(long pollId, VoteTeam team, String name, String code) { + this(null, pollId, team, name, 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/service/customize/CustomizeServiceV2.java b/src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java similarity index 61% rename from src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java rename to src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java index 392f8c89..de3705b8 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java +++ b/src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java @@ -1,10 +1,8 @@ -package com.debatetimer.service.customize; +package com.debatetimer.domainrepository.customize; import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.customize.CustomizeTimeBox; import com.debatetimer.domain.member.Member; -import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; -import com.debatetimer.dto.customize.response.CustomizeTableResponse; import com.debatetimer.entity.customize.BellEntity; import com.debatetimer.entity.customize.CustomizeTableEntity; import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; @@ -15,84 +13,77 @@ import java.util.List; import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; +import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -@Service +@Repository @RequiredArgsConstructor -public class CustomizeServiceV2 { +public class CustomizeTableDomainRepository { private final CustomizeTableRepository tableRepository; private final CustomizeTimeBoxRepository timeBoxRepository; private final BellRepository bellRepository; @Transactional - public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateRequest, Member member) { - CustomizeTable table = tableCreateRequest.toTable(member); - List timeBoxes = tableCreateRequest.toTimeBoxList(); - + public CustomizeTable save(CustomizeTable table, List timeBoxes) { CustomizeTableEntity savedTableEntity = tableRepository.save(new CustomizeTableEntity(table)); saveTimeBoxes(savedTableEntity, timeBoxes); - return new CustomizeTableResponse(savedTableEntity.toDomain(), timeBoxes); + + return savedTableEntity.toDomain(); + } + + private void saveTimeBoxes(CustomizeTableEntity tableEntity, List timeBoxes) { + IntStream.range(0, timeBoxes.size()) + .forEach(i -> saveTimeBox(tableEntity, timeBoxes.get(i), i + 1)); + } + + private void saveTimeBox(CustomizeTableEntity tableEntity, CustomizeTimeBox timeBox, int sequence) { + CustomizeTimeBoxEntity timeBoxEntity = timeBoxRepository.save( + new CustomizeTimeBoxEntity(tableEntity, timeBox, sequence)); + timeBox.getBells() + .forEach(bell -> bellRepository.save(new BellEntity(timeBoxEntity, bell))); } @Transactional(readOnly = true) - public CustomizeTableResponse findTable(long tableId, Member member) { + public CustomizeTable getByIdAndMember(long tableId, Member member) { + return tableRepository.getByIdAndMember(tableId, member) + .toDomain(); + } + + @Transactional(readOnly = true) + public List getCustomizeTimeBoxes(long tableId, Member member) { CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); List timeBoxEntityList = timeBoxRepository.findAllByCustomizeTable(tableEntity); List bellEntityList = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntityList); CustomizeTimeBoxEntities timeBoxEntities = new CustomizeTimeBoxEntities(timeBoxEntityList, bellEntityList); - - return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxEntities.toDomain()); + return timeBoxEntities.toDomain(); } @Transactional - public CustomizeTableResponse updateTable( - CustomizeTableCreateRequest tableCreateRequest, - long tableId, - Member member - ) { + public CustomizeTable update(CustomizeTable table, long tableId, Member member, List timeBoxes) { CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); - tableEntity.updateTable(tableCreateRequest.toTable(member)); + tableEntity.updateTable(table); bellRepository.deleteAllByTable(tableEntity.getId()); timeBoxRepository.deleteAllByTable(tableEntity.getId()); - List timeBoxes = tableCreateRequest.toTimeBoxList(); + saveTimeBoxes(tableEntity, timeBoxes); - return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxes); + return tableEntity.toDomain(); } @Transactional - public CustomizeTableResponse updateUsedAt(long tableId, Member member) { + public CustomizeTable updateUsedAt(long tableId, Member member) { CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); - List timeBoxEntityList = timeBoxRepository.findAllByCustomizeTable(tableEntity); - List bellEntityList = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntityList); - CustomizeTimeBoxEntities timeBoxEntities = new CustomizeTimeBoxEntities(timeBoxEntityList, bellEntityList); - tableEntity.updateUsedAt(); - CustomizeTable table = tableEntity.toDomain(); - List timeBoxes = timeBoxEntities.toDomain(); - return new CustomizeTableResponse(table, timeBoxes); + return tableEntity.toDomain(); } @Transactional - public void deleteTable(long tableId, Member member) { + public void delete(long tableId, Member member) { CustomizeTableEntity table = tableRepository.getByIdAndMember(tableId, member); bellRepository.deleteAllByTable(table.getId()); timeBoxRepository.deleteAllByTable(table.getId()); tableRepository.delete(table); } - - private void saveTimeBoxes(CustomizeTableEntity tableEntity, List timeBoxes) { - IntStream.range(0, timeBoxes.size()) - .forEach(i -> saveTimeBox(tableEntity, timeBoxes.get(i), i + 1)); - } - - private void saveTimeBox(CustomizeTableEntity tableEntity, CustomizeTimeBox timeBox, int sequence) { - CustomizeTimeBoxEntity timeBoxEntity = timeBoxRepository.save( - new CustomizeTimeBoxEntity(tableEntity, timeBox, sequence)); - timeBox.getBells() - .forEach(bell -> bellRepository.save(new BellEntity(timeBoxEntity, bell))); - } } diff --git a/src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java deleted file mode 100644 index 850c53bd..00000000 --- a/src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java +++ /dev/null @@ -1,24 +0,0 @@ -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 index fa46adfb..e35ceca4 100644 --- a/src/main/java/com/debatetimer/domainrepository/poll/PollDomainRepository.java +++ b/src/main/java/com/debatetimer/domainrepository/poll/PollDomainRepository.java @@ -2,8 +2,6 @@ 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; @@ -24,19 +22,20 @@ public Poll create(Poll poll) { @Transactional(readOnly = true) public Poll getByIdAndMemberId(long id, long memberId) { - return findPoll(id, memberId) + return pollRepository.getByIdAndMemberId(id, memberId) + .toDomain(); + } + + @Transactional(readOnly = true) + public Poll getById(long id) { + return pollRepository.getById(id) .toDomain(); } @Transactional public Poll finishPoll(long pollId, long memberId) { - PollEntity pollEntity = findPoll(pollId, memberId); + PollEntity pollEntity = pollRepository.getByIdAndMemberId(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 index 97f8dd1f..dc79f01f 100644 --- a/src/main/java/com/debatetimer/domainrepository/poll/VoteDomainRepository.java +++ b/src/main/java/com/debatetimer/domainrepository/poll/VoteDomainRepository.java @@ -1,20 +1,30 @@ package com.debatetimer.domainrepository.poll; +import com.debatetimer.domain.poll.ParticipateCode; +import com.debatetimer.domain.poll.Vote; import com.debatetimer.domain.poll.VoteInfo; import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.entity.poll.PollEntity; import com.debatetimer.entity.poll.VoteEntity; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.decoder.RepositoryErrorDecoder; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.repository.poll.PollRepository; 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.dao.DataIntegrityViolationException; import org.springframework.stereotype.Repository; @Repository @RequiredArgsConstructor public class VoteDomainRepository { + private final PollRepository pollRepository; private final VoteRepository voteRepository; + private final RepositoryErrorDecoder errorDecoder; public VoteInfo findVoteInfoByPollId(long pollId) { List pollVotes = voteRepository.findAllByPollId(pollId); @@ -28,4 +38,22 @@ private VoteInfo countVotes(long pollId, List voteEntities) { long consCount = teamCount.getOrDefault(VoteTeam.CONS, 0L); return new VoteInfo(pollId, prosCount, consCount); } + + public boolean isExists(long pollId, ParticipateCode code) { + return voteRepository.existsByPollIdAndParticipateCode(pollId, code.getValue()); + } + + public Vote save(Vote vote) { + try { + PollEntity pollEntity = pollRepository.getById(vote.getPollId()); + VoteEntity voteEntity = new VoteEntity(vote, pollEntity); + return voteRepository.save(voteEntity) + .toDomain(); + } catch (DataIntegrityViolationException exception) { + if (errorDecoder.isUniqueConstraintViolation(exception)) { + throw new DTClientErrorException(ClientErrorCode.ALREADY_VOTED_PARTICIPANT); + } + throw exception; + } + } } diff --git a/src/main/java/com/debatetimer/dto/customize/request/BellRequest.java b/src/main/java/com/debatetimer/dto/customize/request/BellRequest.java index fa5f2d88..5142fe8c 100644 --- a/src/main/java/com/debatetimer/dto/customize/request/BellRequest.java +++ b/src/main/java/com/debatetimer/dto/customize/request/BellRequest.java @@ -1,13 +1,17 @@ package com.debatetimer.dto.customize.request; import com.debatetimer.domain.customize.Bell; +import com.debatetimer.domain.customize.BellType; +import jakarta.validation.constraints.NotNull; public record BellRequest( + @NotNull + BellType type, int time, int count ) { public Bell toDomain() { - return new Bell(time, count); + return new Bell(type, time, count); } } diff --git a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableCreateRequest.java b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableCreateRequest.java index 8dc05809..b6ab52e1 100644 --- a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableCreateRequest.java +++ b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableCreateRequest.java @@ -2,31 +2,22 @@ import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.customize.CustomizeTimeBox; -import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; import com.debatetimer.domain.member.Member; import jakarta.validation.Valid; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; public record CustomizeTableCreateRequest( @Valid CustomizeTableInfoCreateRequest info, @Valid List table ) { - public CustomizeTimeBoxEntities toTimeBoxes(CustomizeTable customizeTable) { - return IntStream.range(0, table.size()) - .mapToObj(i -> table.get(i).toTimeBox(customizeTable, i + 1)) - .collect(Collectors.collectingAndThen(Collectors.toList(), CustomizeTimeBoxEntities::new)); + public List toTimeBoxes() { + return table.stream() + .map(CustomizeTimeBoxCreateRequest::toDomain) + .toList(); } public CustomizeTable toTable(Member member) { return info.toTable(member); } - - public List toTimeBoxList() { // TODO 메서드 네이밍 변경 toTimeBoxList() -> toTimeBoxes() - return table.stream() - .map(CustomizeTimeBoxCreateRequest::toDomain) - .toList(); - } } diff --git a/src/main/java/com/debatetimer/dto/customize/response/BellResponse.java b/src/main/java/com/debatetimer/dto/customize/response/BellResponse.java index 0ec2b060..080782d5 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/BellResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/BellResponse.java @@ -1,13 +1,15 @@ package com.debatetimer.dto.customize.response; import com.debatetimer.domain.customize.Bell; +import com.debatetimer.domain.customize.BellType; public record BellResponse( + BellType type, int time, int count ) { public BellResponse(Bell bell) { - this(bell.getTime(), bell.getCount()); + this(bell.getType(), bell.getTime(), bell.getCount()); } } diff --git a/src/main/java/com/debatetimer/dto/poll/request/VoteRequest.java b/src/main/java/com/debatetimer/dto/poll/request/VoteRequest.java new file mode 100644 index 00000000..ccf22242 --- /dev/null +++ b/src/main/java/com/debatetimer/dto/poll/request/VoteRequest.java @@ -0,0 +1,13 @@ +package com.debatetimer.dto.poll.request; + +import com.debatetimer.domain.poll.VoteTeam; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record VoteRequest( + @NotBlank String name, + @NotBlank String participateCode, + @NotNull VoteTeam team +) { + +} diff --git a/src/main/java/com/debatetimer/dto/poll/response/VoteCreateResponse.java b/src/main/java/com/debatetimer/dto/poll/response/VoteCreateResponse.java new file mode 100644 index 00000000..07bf2f2a --- /dev/null +++ b/src/main/java/com/debatetimer/dto/poll/response/VoteCreateResponse.java @@ -0,0 +1,16 @@ +package com.debatetimer.dto.poll.response; + +import com.debatetimer.domain.poll.Vote; +import com.debatetimer.domain.poll.VoteTeam; + +public record VoteCreateResponse( + long id, + String name, + String participateCode, + VoteTeam team +) { + + public VoteCreateResponse(Vote vote) { + this(vote.getId(), vote.getName().getValue(), vote.getCode().getValue(), vote.getTeam()); + } +} diff --git a/src/main/java/com/debatetimer/dto/poll/response/VoterPollInfoResponse.java b/src/main/java/com/debatetimer/dto/poll/response/VoterPollInfoResponse.java new file mode 100644 index 00000000..acf86030 --- /dev/null +++ b/src/main/java/com/debatetimer/dto/poll/response/VoterPollInfoResponse.java @@ -0,0 +1,31 @@ +package com.debatetimer.dto.poll.response; + +import com.debatetimer.domain.poll.ParticipateCode; +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domain.poll.VoteInfo; + +public record VoterPollInfoResponse( + long id, + PollStatus status, + String prosTeamName, + String consTeamName, + String participateCode, + long totalCount, + long prosCount, + long consCount +) { + + public VoterPollInfoResponse(Poll poll, VoteInfo voteInfo, ParticipateCode code) { + this( + poll.getId(), + poll.getStatus(), + poll.getProsTeamName().getValue(), + poll.getConsTeamName().getValue(), + code.getValue(), + voteInfo.getTotalCount(), + voteInfo.getProsCount(), + voteInfo.getConsCount() + ); + } +} diff --git a/src/main/java/com/debatetimer/entity/customize/BaseTimeEntity.java b/src/main/java/com/debatetimer/entity/BaseTimeEntity.java similarity index 93% rename from src/main/java/com/debatetimer/entity/customize/BaseTimeEntity.java rename to src/main/java/com/debatetimer/entity/BaseTimeEntity.java index 1bca8c75..e9405201 100644 --- a/src/main/java/com/debatetimer/entity/customize/BaseTimeEntity.java +++ b/src/main/java/com/debatetimer/entity/BaseTimeEntity.java @@ -1,4 +1,4 @@ -package com.debatetimer.entity.customize; +package com.debatetimer.entity; import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; diff --git a/src/main/java/com/debatetimer/entity/customize/BellEntity.java b/src/main/java/com/debatetimer/entity/customize/BellEntity.java index 9c1f9982..81c33816 100644 --- a/src/main/java/com/debatetimer/entity/customize/BellEntity.java +++ b/src/main/java/com/debatetimer/entity/customize/BellEntity.java @@ -1,10 +1,13 @@ package com.debatetimer.entity.customize; import com.debatetimer.domain.customize.Bell; +import com.debatetimer.domain.customize.BellType; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -34,21 +37,28 @@ public class BellEntity { @JoinColumn(name = "customize_time_box_id") private CustomizeTimeBoxEntity customizeTimeBox; + @Column(name = "bell_type") + @NotNull + @Enumerated(value = EnumType.STRING) + private BellType type; + @Column(name = "bell_time") private int time; private int count; - public BellEntity(CustomizeTimeBoxEntity customizeTimeBox, int time, int count) { + public BellEntity(CustomizeTimeBoxEntity customizeTimeBox, BellType type, int time, int count) { validateTime(time); validateCount(count); this.customizeTimeBox = customizeTimeBox; + this.type = type; this.time = time; this.count = count; } public BellEntity(CustomizeTimeBoxEntity customizeTimeBox, Bell bell) { this.customizeTimeBox = customizeTimeBox; + this.type = bell.getType(); this.time = bell.getTime(); this.count = bell.getCount(); } @@ -66,7 +76,7 @@ private void validateCount(int count) { } public Bell toDomain() { - return new Bell(time, count); + return new Bell(type, time, count); } public boolean isContained(CustomizeTimeBoxEntity timeBox) { diff --git a/src/main/java/com/debatetimer/entity/customize/CustomizeTableEntity.java b/src/main/java/com/debatetimer/entity/customize/CustomizeTableEntity.java index 88cfd3bb..ff24334e 100644 --- a/src/main/java/com/debatetimer/entity/customize/CustomizeTableEntity.java +++ b/src/main/java/com/debatetimer/entity/customize/CustomizeTableEntity.java @@ -3,6 +3,7 @@ import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; import com.debatetimer.dto.member.TableType; +import com.debatetimer.entity.BaseTimeEntity; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; @@ -22,7 +23,7 @@ @Getter @Table(name = "customize_table") @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class CustomizeTableEntity { +public class CustomizeTableEntity extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/com/debatetimer/entity/poll/PollEntity.java b/src/main/java/com/debatetimer/entity/poll/PollEntity.java index bb4e5433..04e8ac28 100644 --- a/src/main/java/com/debatetimer/entity/poll/PollEntity.java +++ b/src/main/java/com/debatetimer/entity/poll/PollEntity.java @@ -2,7 +2,7 @@ import com.debatetimer.domain.poll.Poll; import com.debatetimer.domain.poll.PollStatus; -import com.debatetimer.entity.customize.BaseTimeEntity; +import com.debatetimer.entity.BaseTimeEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; diff --git a/src/main/java/com/debatetimer/entity/poll/VoteEntity.java b/src/main/java/com/debatetimer/entity/poll/VoteEntity.java index 47dd0a2c..0b0f26c7 100644 --- a/src/main/java/com/debatetimer/entity/poll/VoteEntity.java +++ b/src/main/java/com/debatetimer/entity/poll/VoteEntity.java @@ -2,6 +2,7 @@ import com.debatetimer.domain.poll.Vote; import com.debatetimer.domain.poll.VoteTeam; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -12,6 +13,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; @@ -21,7 +23,9 @@ @Entity @Getter -@Table(name = "vote") +@Table(name = "vote", uniqueConstraints = { + @UniqueConstraint(columnNames = {"poll_id", "participate_code"}) +}) @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor public class VoteEntity { @@ -43,9 +47,14 @@ public class VoteEntity { private String name; @NotBlank - private String participantCode; + @Column(name = "participate_code") + private String participateCode; + + public VoteEntity(Vote vote, PollEntity pollEntity) { + this(vote.getId(), pollEntity, vote.getTeam(), vote.getName().getValue(), vote.getCode().getValue()); + } public Vote toDomain() { - return new Vote(id, poll.getId(), team, name, participantCode); + return new Vote(id, poll.getId(), team, name, participateCode); } } diff --git a/src/main/java/com/debatetimer/exception/decoder/H2ErrorDecoder.java b/src/main/java/com/debatetimer/exception/decoder/H2ErrorDecoder.java new file mode 100644 index 00000000..42ea2660 --- /dev/null +++ b/src/main/java/com/debatetimer/exception/decoder/H2ErrorDecoder.java @@ -0,0 +1,22 @@ +package com.debatetimer.exception.decoder; + +import org.hibernate.exception.ConstraintViolationException; +import org.springframework.dao.DataIntegrityViolationException; + +public class H2ErrorDecoder implements RepositoryErrorDecoder { + + protected static final String UNIQUE_CONSTRAINT_VIOLATION_SQL_STATE = "23505"; + + @Override + public boolean isUniqueConstraintViolation(DataIntegrityViolationException e) { + Throwable cause = e.getCause(); + while (cause != null) { + if (cause instanceof ConstraintViolationException cve) { + String sqlState = cve.getSQLException().getSQLState(); + return UNIQUE_CONSTRAINT_VIOLATION_SQL_STATE.equals(sqlState); + } + cause = cause.getCause(); + } + return false; + } +} diff --git a/src/main/java/com/debatetimer/exception/decoder/MySqlErrorDecoder.java b/src/main/java/com/debatetimer/exception/decoder/MySqlErrorDecoder.java new file mode 100644 index 00000000..f7ddf38c --- /dev/null +++ b/src/main/java/com/debatetimer/exception/decoder/MySqlErrorDecoder.java @@ -0,0 +1,27 @@ +package com.debatetimer.exception.decoder; + +import java.sql.SQLException; +import org.hibernate.exception.ConstraintViolationException; +import org.springframework.dao.DataIntegrityViolationException; + +public class MySqlErrorDecoder implements RepositoryErrorDecoder { + + protected static final String MYSQL_UNIQUE_VIOLATION = "23000"; + protected static final int MYSQL_DUP_ERROR_CODE = 1062; + + @Override + public boolean isUniqueConstraintViolation(DataIntegrityViolationException e) { + Throwable cause = e.getCause(); + while (cause != null) { + if (cause instanceof ConstraintViolationException cve) { + SQLException sqlEx = cve.getSQLException(); + String sqlState = sqlEx.getSQLState(); + int errorCode = sqlEx.getErrorCode(); + return MYSQL_UNIQUE_VIOLATION.equals(sqlState) + && MYSQL_DUP_ERROR_CODE == errorCode; + } + cause = cause.getCause(); + } + return false; + } +} diff --git a/src/main/java/com/debatetimer/exception/decoder/RepositoryErrorDecoder.java b/src/main/java/com/debatetimer/exception/decoder/RepositoryErrorDecoder.java new file mode 100644 index 00000000..8d71e834 --- /dev/null +++ b/src/main/java/com/debatetimer/exception/decoder/RepositoryErrorDecoder.java @@ -0,0 +1,8 @@ +package com.debatetimer.exception.decoder; + +import org.springframework.dao.DataIntegrityViolationException; + +public interface RepositoryErrorDecoder { + + boolean isUniqueConstraintViolation(DataIntegrityViolationException exception); +} diff --git a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java index f0672c70..695cbb0c 100644 --- a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java +++ b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java @@ -50,6 +50,8 @@ public enum ClientErrorCode implements ResponseErrorCode { INVALID_POLL_PARTICIPANT_NAME(HttpStatus.BAD_REQUEST, "잘못된 투표자 이름입니다"), INVALID_POLL_PARTICIPANT_CODE(HttpStatus.BAD_REQUEST, "잘못된 투표참여 코드입니다"), + ALREADY_DONE_POLL(HttpStatus.BAD_REQUEST, "이미 완료된 투표 입니다"), + ALREADY_VOTED_PARTICIPANT(HttpStatus.BAD_REQUEST, "이미 참여한 투표자 입니다"), TABLE_NOT_FOUND(HttpStatus.NOT_FOUND, "토론 테이블을 찾을 수 없습니다."), NOT_TABLE_OWNER(HttpStatus.UNAUTHORIZED, "테이블을 소유한 회원이 아닙니다."), diff --git a/src/main/java/com/debatetimer/repository/poll/PollRepository.java b/src/main/java/com/debatetimer/repository/poll/PollRepository.java index 121d1627..22c6db56 100644 --- a/src/main/java/com/debatetimer/repository/poll/PollRepository.java +++ b/src/main/java/com/debatetimer/repository/poll/PollRepository.java @@ -1,10 +1,22 @@ package com.debatetimer.repository.poll; import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface PollRepository extends JpaRepository { Optional findByIdAndMemberId(long id, long memberId); + + default PollEntity getById(long id) { + return findById(id) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.POLL_NOT_FOUND)); + } + + default PollEntity getByIdAndMemberId(long id, long memberId) { + return findByIdAndMemberId(id, memberId) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.POLL_NOT_FOUND)); + } } diff --git a/src/main/java/com/debatetimer/repository/poll/VoteRepository.java b/src/main/java/com/debatetimer/repository/poll/VoteRepository.java index a9709200..5f211efd 100644 --- a/src/main/java/com/debatetimer/repository/poll/VoteRepository.java +++ b/src/main/java/com/debatetimer/repository/poll/VoteRepository.java @@ -7,4 +7,6 @@ public interface VoteRepository extends JpaRepository { List findAllByPollId(long pollId); + + boolean existsByPollIdAndParticipateCode(long pollId, String participateCode); } diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeService.java b/src/main/java/com/debatetimer/service/customize/CustomizeService.java index 13b3e44d..1670036f 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeService.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeService.java @@ -1,14 +1,11 @@ package com.debatetimer.service.customize; import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.customize.CustomizeTimeBox; import com.debatetimer.domain.member.Member; +import com.debatetimer.domainrepository.customize.CustomizeTableDomainRepository; import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; import com.debatetimer.dto.customize.response.CustomizeTableResponse; -import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; -import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; -import com.debatetimer.repository.customize.CustomizeTableRepository; -import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -18,23 +15,21 @@ @RequiredArgsConstructor public class CustomizeService { - private final CustomizeTableRepository tableRepository; - private final CustomizeTimeBoxRepository timeBoxRepository; + private final CustomizeTableDomainRepository customizeTableDomainRepository; @Transactional public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateRequest, Member member) { CustomizeTable table = tableCreateRequest.toTable(member); - CustomizeTableEntity savedTable = tableRepository.save(new CustomizeTableEntity(table)); - - CustomizeTimeBoxEntities savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, savedTable.toDomain()); - return new CustomizeTableResponse(savedTable.toDomain(), savedCustomizeTimeBoxes); + List timeBoxes = tableCreateRequest.toTimeBoxes(); + CustomizeTable savedTable = customizeTableDomainRepository.save(table, timeBoxes); + return new CustomizeTableResponse(savedTable, timeBoxes); } @Transactional(readOnly = true) public CustomizeTableResponse findTable(long tableId, Member member) { - CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); - CustomizeTimeBoxEntities timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); - return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxes); + CustomizeTable table = customizeTableDomainRepository.getByIdAndMember(tableId, member); + List timeBoxes = customizeTableDomainRepository.getCustomizeTimeBoxes(tableId, member); + return new CustomizeTableResponse(table, timeBoxes); } @Transactional @@ -43,38 +38,22 @@ public CustomizeTableResponse updateTable( long tableId, Member member ) { - CustomizeTableEntity existingTable = tableRepository.getByIdAndMember(tableId, member); - CustomizeTable renewedTable = tableCreateRequest.toTable(member); - existingTable.updateTable(renewedTable); + CustomizeTable table = tableCreateRequest.toTable(member); + List timeBoxes = tableCreateRequest.toTimeBoxes(); - timeBoxRepository.deleteAllByTable(existingTable.getId()); - CustomizeTimeBoxEntities savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, existingTable.toDomain()); - return new CustomizeTableResponse(existingTable.toDomain(), savedCustomizeTimeBoxes); + CustomizeTable updatedTable = customizeTableDomainRepository.update(table, tableId, member, timeBoxes); + return new CustomizeTableResponse(updatedTable, timeBoxes); } @Transactional public CustomizeTableResponse updateUsedAt(long tableId, Member member) { - CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); - CustomizeTimeBoxEntities timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); - tableEntity.updateUsedAt(); - - return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxes); + CustomizeTable table = customizeTableDomainRepository.updateUsedAt(tableId, member); + List timeBoxes = customizeTableDomainRepository.getCustomizeTimeBoxes(tableId, member); + return new CustomizeTableResponse(table, timeBoxes); } @Transactional public void deleteTable(long tableId, Member member) { - CustomizeTableEntity table = tableRepository.getByIdAndMember(tableId, member); - timeBoxRepository.deleteAllByTable(table.getId()); - tableRepository.delete(table); - } - - private CustomizeTimeBoxEntities saveTimeBoxes( - CustomizeTableCreateRequest tableCreateRequest, - CustomizeTable table - ) { - CustomizeTimeBoxEntities customizeTimeBoxes = tableCreateRequest.toTimeBoxes(table); - List savedTimeBoxes = timeBoxRepository.saveAll( - customizeTimeBoxes.getTimeBoxes()); - return new CustomizeTimeBoxEntities(savedTimeBoxes); + customizeTableDomainRepository.delete(tableId, member); } } diff --git a/src/main/java/com/debatetimer/service/poll/PollService.java b/src/main/java/com/debatetimer/service/poll/PollService.java index a6409fdb..cfa021b9 100644 --- a/src/main/java/com/debatetimer/service/poll/PollService.java +++ b/src/main/java/com/debatetimer/service/poll/PollService.java @@ -4,7 +4,7 @@ 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.customize.CustomizeTableDomainRepository; import com.debatetimer.domainrepository.poll.PollDomainRepository; import com.debatetimer.domainrepository.poll.VoteDomainRepository; import com.debatetimer.dto.poll.response.PollCreateResponse; diff --git a/src/main/java/com/debatetimer/service/poll/VoteService.java b/src/main/java/com/debatetimer/service/poll/VoteService.java new file mode 100644 index 00000000..8c485907 --- /dev/null +++ b/src/main/java/com/debatetimer/service/poll/VoteService.java @@ -0,0 +1,56 @@ +package com.debatetimer.service.poll; + +import com.debatetimer.domain.poll.ParticipateCode; +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.Vote; +import com.debatetimer.domain.poll.VoteInfo; +import com.debatetimer.domainrepository.poll.PollDomainRepository; +import com.debatetimer.domainrepository.poll.VoteDomainRepository; +import com.debatetimer.dto.poll.request.VoteRequest; +import com.debatetimer.dto.poll.response.VoteCreateResponse; +import com.debatetimer.dto.poll.response.VoterPollInfoResponse; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class VoteService { + + private final VoteDomainRepository voteDomainRepository; + private final PollDomainRepository pollDomainRepository; + + @Transactional + public VoteCreateResponse vote(long pollId, VoteRequest voteRequest) { + validateProgressPoll(pollId); + validateAlreadyVoted(pollId, voteRequest.participateCode()); + Vote vote = new Vote(pollId, voteRequest.team(), voteRequest.name(), voteRequest.participateCode()); + Vote savedVote = voteDomainRepository.save(vote); + return new VoteCreateResponse(savedVote); + } + + private void validateProgressPoll(long pollId) { + Poll poll = pollDomainRepository.getById(pollId); + if (!poll.isProgress()) { + throw new DTClientErrorException(ClientErrorCode.ALREADY_DONE_POLL); + } + } + + private void validateAlreadyVoted(long pollId, String participateCode) { + ParticipateCode code = new ParticipateCode(participateCode); + if (voteDomainRepository.isExists(pollId, code)) { + throw new DTClientErrorException(ClientErrorCode.ALREADY_VOTED_PARTICIPANT); + } + } + + @Transactional(readOnly = true) + public VoterPollInfoResponse getVoterPollInfo(long pollId) { + Poll poll = pollDomainRepository.getById(pollId); + VoteInfo voteInfo = voteDomainRepository.findVoteInfoByPollId(pollId); + ParticipateCode code = new ParticipateCode(UUID.randomUUID().toString()); + return new VoterPollInfoResponse(poll, voteInfo, code); + } +} diff --git a/src/main/resources/db/migration/V12__add_participate_code_unique_constraint.sql b/src/main/resources/db/migration/V12__add_participate_code_unique_constraint.sql new file mode 100644 index 00000000..315fd427 --- /dev/null +++ b/src/main/resources/db/migration/V12__add_participate_code_unique_constraint.sql @@ -0,0 +1,5 @@ +ALTER TABLE vote + CHANGE participant_code participate_code VARCHAR (255) NOT NULL; + +ALTER TABLE vote + ADD CONSTRAINT uq_vote_poll_participate UNIQUE (poll_id, participate_code); diff --git a/src/main/resources/db/migration/V13__add_bell_type_into_bell.sql b/src/main/resources/db/migration/V13__add_bell_type_into_bell.sql new file mode 100644 index 00000000..e2f1a133 --- /dev/null +++ b/src/main/resources/db/migration/V13__add_bell_type_into_bell.sql @@ -0,0 +1,2 @@ +ALTER TABLE bell + ADD COLUMN bell_type enum ('AFTER_START','BEFORE_END','AFTER_END') NOT NULL diff --git a/src/test/java/com/debatetimer/controller/BaseControllerTest.java b/src/test/java/com/debatetimer/controller/BaseControllerTest.java index 83d35dda..504c5dcd 100644 --- a/src/test/java/com/debatetimer/controller/BaseControllerTest.java +++ b/src/test/java/com/debatetimer/controller/BaseControllerTest.java @@ -2,19 +2,22 @@ import com.debatetimer.DataBaseCleaner; import com.debatetimer.client.oauth.OAuthClient; +import com.debatetimer.domain.customize.BellType; import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.Stance; +import com.debatetimer.domain.poll.VoteTeam; import com.debatetimer.dto.customize.request.BellRequest; import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; import com.debatetimer.dto.customize.request.CustomizeTableInfoCreateRequest; import com.debatetimer.dto.customize.request.CustomizeTimeBoxCreateRequest; -import com.debatetimer.fixture.CustomizeTableGenerator; -import com.debatetimer.fixture.CustomizeTimeBoxGenerator; +import com.debatetimer.dto.poll.request.VoteRequest; 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.fixture.entity.CustomizeTableEntityGenerator; +import com.debatetimer.fixture.entity.CustomizeTimeBoxEntityGenerator; +import com.debatetimer.fixture.entity.MemberGenerator; +import com.debatetimer.fixture.entity.PollEntityGenerator; +import com.debatetimer.fixture.entity.VoteEntityGenerator; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.navercorp.fixturemonkey.ArbitraryBuilder; import com.navercorp.fixturemonkey.FixtureMonkey; @@ -24,6 +27,7 @@ import io.restassured.filter.log.RequestLoggingFilter; import io.restassured.filter.log.ResponseLoggingFilter; import io.restassured.specification.RequestSpecification; +import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -42,16 +46,16 @@ public abstract class BaseControllerTest { protected MemberGenerator memberGenerator; @Autowired - protected CustomizeTableGenerator customizeTableGenerator; + protected CustomizeTableEntityGenerator customizeTableEntityGenerator; @Autowired - protected CustomizeTimeBoxGenerator customizeTimeBoxGenerator; + protected CustomizeTimeBoxEntityGenerator customizeTimeBoxEntityGenerator; @Autowired - protected PollGenerator pollGenerator; + protected PollEntityGenerator pollEntityGenerator; @Autowired - protected VoteGenerator voteGenerator; + protected VoteEntityGenerator voteEntityGenerator; @Autowired protected HeaderGenerator headerGenerator; @@ -90,6 +94,13 @@ protected ArbitraryBuilder getCustomizeTableCreateR .set("table", getCustomizeTimeBoxCreateRequestBuilder().sampleList(2)); } + protected ArbitraryBuilder getVoteRequestBuilder() { + return fixtureMonkey.giveMeBuilder(VoteRequest.class) + .set("name", "콜리") + .set("team", VoteTeam.PROS) + .set("participateCode", UUID.randomUUID().toString()); + } + private ArbitraryBuilder getCustomizeTableInfoCreateRequestBuilder() { return fixtureMonkey.giveMeBuilder(CustomizeTableInfoCreateRequest.class) .set("name", "자유 테이블") @@ -114,6 +125,7 @@ private ArbitraryBuilder getCustomizeTimeBoxCreat private ArbitraryBuilder getBellRequestBuilder() { return fixtureMonkey.giveMeBuilder(BellRequest.class) + .set("type", BellType.AFTER_START) .set("time", 30) .set("count", 1); } diff --git a/src/test/java/com/debatetimer/controller/BaseDocumentTest.java b/src/test/java/com/debatetimer/controller/BaseDocumentTest.java index 40c80cc1..0a74a3cc 100644 --- a/src/test/java/com/debatetimer/controller/BaseDocumentTest.java +++ b/src/test/java/com/debatetimer/controller/BaseDocumentTest.java @@ -13,6 +13,7 @@ import com.debatetimer.service.customize.CustomizeService; import com.debatetimer.service.member.MemberService; import com.debatetimer.service.poll.PollService; +import com.debatetimer.service.poll.VoteService; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.filter.log.RequestLoggingFilter; @@ -66,6 +67,9 @@ public abstract class BaseDocumentTest { @MockitoBean protected PollService pollService; + @MockitoBean + protected VoteService voteService; + @MockitoBean protected AuthManager authManager; diff --git a/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java b/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java index e82c3c5e..df0c91e2 100644 --- a/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java +++ b/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java @@ -154,9 +154,9 @@ class GetTable { @Test void 사용자_지정_테이블을_조회한다() { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); - customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 1); - customizeTimeBoxGenerator.generateNotExistSpeaker(bitoTable, CustomizeBoxType.NORMAL, 2); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); + customizeTimeBoxEntityGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 1); + customizeTimeBoxEntityGenerator.generateNotExistSpeaker(bitoTable, CustomizeBoxType.NORMAL, 2); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableResponse response = given() @@ -180,7 +180,7 @@ class UpdateTable { @Test void 사용자_지정_토론_테이블을_업데이트한다() { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableCreateRequest renewTableRequest = getCustomizeTableCreateRequestBuilder() .set("table[1].speaker", null) @@ -201,7 +201,7 @@ class UpdateTable { @NullAndEmptyAndBlankSource void 사용자_지정_테이블을_업데이트할때_테이블_이름은_개행문자_외_다른_글자가_포함되야한다(String tableName) { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() .set("info.name", tableName) @@ -218,7 +218,7 @@ class UpdateTable { @ParameterizedTest void 사용자_지정_테이블을_업데이트할때_테이블_주제는_null이_올_수_없다(String agenda) { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() .set("info.agenda", agenda) @@ -235,7 +235,7 @@ class UpdateTable { @NullAndEmptyAndBlankSource void 사용자_지정_테이블을_업데이트할때_찬성팀_이름은_개행문자_외_다른_글자가_포함되야한다(String prosTeamName) { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() .set("info.prosTeamName", prosTeamName) @@ -252,7 +252,7 @@ class UpdateTable { @NullAndEmptyAndBlankSource void 사용자_지정_테이블을_업데이트할때_반대팀_이름은_개행문자_외_다른_글자가_포함되야한다(String consTeamName) { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() @@ -270,7 +270,7 @@ class UpdateTable { @ParameterizedTest void 사용자_지정_테이블을_업데이트할때_타임박스_입장은_null이_올_수_없다(Stance stance) { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() .set("table[0].stance", stance) @@ -287,7 +287,7 @@ class UpdateTable { @NullAndEmptyAndBlankSource void 사용자_지정_테이블을_업데이트할때_타임박스_발언_유형은_개행문자_외_다른_글자가_포함되야한다(String speechType) { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() .set("table[0].speechType", speechType) @@ -304,7 +304,7 @@ class UpdateTable { @ParameterizedTest void 사용자_지정_테이블을_업데이트할때_타임박스_타입은_null이_올_수_없다(CustomizeBoxType boxType) { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() .set("table[0].boxType", boxType) @@ -339,9 +339,9 @@ class Debate { @Test void 사용자_지정_토론을_시작한다() { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); - customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 1); - customizeTimeBoxGenerator.generateNotExistSpeaker(bitoTable, CustomizeBoxType.NORMAL, 2); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); + customizeTimeBoxEntityGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 1); + customizeTimeBoxEntityGenerator.generateNotExistSpeaker(bitoTable, CustomizeBoxType.NORMAL, 2); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableResponse response = given() @@ -366,9 +366,9 @@ class DeleteTable { @Test void 사용자_지정_토론_테이블을_삭제한다() { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); - customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 1); - customizeTimeBoxGenerator.generateNotExistSpeaker(bitoTable, CustomizeBoxType.NORMAL, 2); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); + customizeTimeBoxEntityGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 1); + customizeTimeBoxEntityGenerator.generateNotExistSpeaker(bitoTable, CustomizeBoxType.NORMAL, 2); Headers headers = headerGenerator.generateAccessTokenHeader(bito); given() diff --git a/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java b/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java index e7545638..e2613aa0 100644 --- a/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java +++ b/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java @@ -18,6 +18,7 @@ import com.debatetimer.controller.RestDocumentationRequest; import com.debatetimer.controller.RestDocumentationResponse; import com.debatetimer.controller.Tag; +import com.debatetimer.domain.customize.BellType; import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.Stance; import com.debatetimer.dto.customize.request.BellRequest; @@ -71,8 +72,9 @@ class Save { fieldWithPath("table[].boxType").type(STRING).description("타임 박스 유형"), fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)").optional(), fieldWithPath("table[].bell").type(ARRAY).description("종소리 정보").optional(), - fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)").optional(), - fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수").optional(), + fieldWithPath("table[].bell[].type").type(STRING).description("종소리 종류"), + fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)"), + fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수"), fieldWithPath("table[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -95,8 +97,9 @@ class Save { fieldWithPath("table[].boxType").type(STRING).description("타임 박스 유형"), fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)").optional(), fieldWithPath("table[].bell").type(ARRAY).description("종소리 정보").optional(), - fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)").optional(), - fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수").optional(), + fieldWithPath("table[].bell[].type").type(STRING).description("종소리 종류"), + fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)"), + fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수"), fieldWithPath("table[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -109,9 +112,9 @@ class Save { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), null, null, "콜리"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1)), null, null, "콜리"), new CustomizeTimeBoxCreateRequest(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), null, null, "비토"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1), new BellRequest(BellType.AFTER_START, 120, 2)), null, null, "비토"), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, null, null, 360, 120, null), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, @@ -124,9 +127,9 @@ class Save { "찬성", "반대", true, true), List.of( new CustomizeTimeBoxResponse(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellResponse(90, 1)), null, null, "콜리"), + 120, List.of(new BellResponse(BellType.AFTER_START, 90, 1)), null, null, "콜리"), new CustomizeTimeBoxResponse(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellResponse(90, 1), new BellResponse(120, 2)), null, null, "비토"), + 120, List.of(new BellResponse(BellType.AFTER_START, 90, 1), new BellResponse(BellType.AFTER_START, 120, 2)), null, null, "비토"), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, null, null, 360, 120, null), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, @@ -168,9 +171,9 @@ class Save { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), null, null, "콜리"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1)), null, null, "콜리"), new CustomizeTimeBoxCreateRequest(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), null, null, "비토"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1), new BellRequest(BellType.AFTER_START, 120, 2)), null, null, "비토"), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, null, null, 360, 120, null), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, @@ -230,8 +233,9 @@ class GetTable { fieldWithPath("table[].boxType").type(STRING).description("타임 박스 유형"), fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)").optional(), fieldWithPath("table[].bell").type(ARRAY).description("종소리 정보").optional(), - fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)").optional(), - fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수").optional(), + fieldWithPath("table[].bell[].type").type(STRING).description("종소리 종류"), + fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)"), + fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수"), fieldWithPath("table[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -246,9 +250,9 @@ class GetTable { "찬성", "반대", true, true), List.of( new CustomizeTimeBoxResponse(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellResponse(90, 1)), null, null, "콜리"), + 120, List.of(new BellResponse(BellType.AFTER_START, 90, 1)), null, null, "콜리"), new CustomizeTimeBoxResponse(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellResponse(90, 1), new BellResponse(120, 2)), null, null, "비토"), + 120, List.of(new BellResponse(BellType.AFTER_START, 90, 1), new BellResponse(BellType.AFTER_START, 120, 2)), null, null, "비토"), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, null, null, 360, 120, null), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, @@ -323,8 +327,9 @@ class UpdateTable { fieldWithPath("table[].boxType").type(STRING).description("타임 박스 유형"), fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)").optional(), fieldWithPath("table[].bell").type(ARRAY).description("종소리 정보").optional(), - fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)").optional(), - fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수").optional(), + fieldWithPath("table[].bell[].type").type(STRING).description("종소리 종류"), + fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)"), + fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수"), fieldWithPath("table[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -347,8 +352,9 @@ class UpdateTable { fieldWithPath("table[].boxType").type(STRING).description("타임 박스 유형"), fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)").optional(), fieldWithPath("table[].bell").type(ARRAY).description("종소리 정보").optional(), - fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)").optional(), - fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수").optional(), + fieldWithPath("table[].bell[].type").type(STRING).description("종소리 종류"), + fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)"), + fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수"), fieldWithPath("table[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -362,9 +368,9 @@ class UpdateTable { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), null, null, "콜리"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1)), null, null, "콜리"), new CustomizeTimeBoxCreateRequest(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), null, null, "비토"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1), new BellRequest(BellType.AFTER_START, 120, 2)), null, null, "비토"), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, null, null, 360, 120, null), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, @@ -377,9 +383,9 @@ class UpdateTable { "찬성", "반대", true, true), List.of( new CustomizeTimeBoxResponse(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellResponse(90, 1)), null, null, "콜리"), + 120, List.of(new BellResponse(BellType.AFTER_START, 90, 1)), null, null, "콜리"), new CustomizeTimeBoxResponse(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellResponse(90, 1), new BellResponse(120, 2)), null, null, "비토"), + 120, List.of(new BellResponse(BellType.AFTER_START, 90, 1), new BellResponse(BellType.AFTER_START, 120, 2)), null, null, "비토"), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, null, null, 360, 120, null), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, @@ -424,9 +430,9 @@ class UpdateTable { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), null, null, "콜리"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1)), null, null, "콜리"), new CustomizeTimeBoxCreateRequest(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), null, null, "비토"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1), new BellRequest(BellType.AFTER_START, 120, 2)), null, null, "비토"), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, null, null, 360, 120, null), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, @@ -488,8 +494,9 @@ class Debate { fieldWithPath("table[].boxType").type(STRING).description("타임 박스 유형"), fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)").optional(), fieldWithPath("table[].bell").type(ARRAY).description("종소리 정보").optional(), - fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)").optional(), - fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수").optional(), + fieldWithPath("table[].bell[].type").type(STRING).description("종소리 종류"), + fieldWithPath("table[].bell[].time").type(NUMBER).description("종소리 울릴 시간(초)"), + fieldWithPath("table[].bell[].count").type(NUMBER).description("종소리 횟수"), fieldWithPath("table[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -505,9 +512,9 @@ class Debate { "찬성", "반대", true, true), List.of( new CustomizeTimeBoxResponse(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellResponse(90, 1)), null, null, "콜리"), + 120, List.of(new BellResponse(BellType.AFTER_START, 90, 1)), null, null, "콜리"), new CustomizeTimeBoxResponse(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, List.of(new BellResponse(90, 1), new BellResponse(120, 2)), null, null, "비토"), + 120, List.of(new BellResponse(BellType.AFTER_START, 90, 1), new BellResponse(BellType.AFTER_START, 120, 2)), null, null, "비토"), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, null, null, 360, 120, null), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, diff --git a/src/test/java/com/debatetimer/controller/poll/PollControllerTest.java b/src/test/java/com/debatetimer/controller/poll/PollControllerTest.java index 22487d85..ee3fad9b 100644 --- a/src/test/java/com/debatetimer/controller/poll/PollControllerTest.java +++ b/src/test/java/com/debatetimer/controller/poll/PollControllerTest.java @@ -24,7 +24,7 @@ class CreatePoll { @Test void 선거를_생성할_수_있다() { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); Headers headers = headerGenerator.generateAccessTokenHeader(member); given() @@ -42,11 +42,11 @@ 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, "커찬"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteEntityGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); Headers headers = headerGenerator.generateAccessTokenHeader(member); PollInfoResponse response = given() @@ -75,8 +75,8 @@ class FinishPoll { @Test void 선거정보를_완료상태로_변경한다() { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); Headers headers = headerGenerator.generateAccessTokenHeader(member); PollInfoResponse response = given() diff --git a/src/test/java/com/debatetimer/controller/poll/VoteControllerTest.java b/src/test/java/com/debatetimer/controller/poll/VoteControllerTest.java new file mode 100644 index 00000000..19fe3fdd --- /dev/null +++ b/src/test/java/com/debatetimer/controller/poll/VoteControllerTest.java @@ -0,0 +1,173 @@ +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.request.VoteRequest; +import com.debatetimer.dto.poll.response.VoteCreateResponse; +import com.debatetimer.dto.poll.response.VoterPollInfoResponse; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.fixture.NullAndEmptyAndBlankSource; +import io.restassured.http.ContentType; +import java.util.UUID; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.springframework.http.HttpStatus; + +class VoteControllerTest extends BaseControllerTest { + + @Nested + class GetVotersPollInfo { + + @Test + void 투표자가_선거정보를_조회할_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteEntityGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); + + VoterPollInfoResponse response = given() + .contentType(ContentType.JSON) + .pathParam("pollId", pollEntity.getId()) + .when().get("/api/polls/{pollId}/votes") + .then().statusCode(HttpStatus.OK.value()) + .extract().as(VoterPollInfoResponse.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.participateCode()).isNotBlank(), + () -> assertThat(response.prosCount()).isEqualTo(2L), + () -> assertThat(response.consCount()).isEqualTo(1L) + ); + } + } + + @Nested + class VotePoll { + + @Test + void 진행_중인_선거에_최초로_투표_할_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + VoteRequest voteRequest = getVoteRequestBuilder().sample(); + + VoteCreateResponse response = given() + .contentType(ContentType.JSON) + .body(voteRequest) + .pathParam("pollId", pollEntity.getId()) + .when().post("/api/polls/{pollId}/votes") + .then().statusCode(HttpStatus.CREATED.value()) + .extract().as(VoteCreateResponse.class); + + assertAll( + () -> assertThat(response.name()).isEqualTo(voteRequest.name()), + () -> assertThat(response.participateCode()).isEqualTo(voteRequest.participateCode()), + () -> assertThat(response.team()).isEqualTo(voteRequest.team()) + ); + } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 투표_시_이름은_널이거나_빈_문자열일_수_없다(String invalidName) { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + VoteRequest voteRequest = getVoteRequestBuilder() + .set("name", invalidName) + .sample(); + + given() + .contentType(ContentType.JSON) + .body(voteRequest) + .pathParam("pollId", pollEntity.getId()) + .when().post("/api/polls/{pollId}/votes") + .then().statusCode(HttpStatus.BAD_REQUEST.value()); + } + + @Test + void 투표_시_팀은_널일_수_없다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + VoteRequest voteRequest = getVoteRequestBuilder() + .set("team", null) + .sample(); + + given() + .contentType(ContentType.JSON) + .body(voteRequest) + .pathParam("pollId", pollEntity.getId()) + .when().post("/api/polls/{pollId}/votes") + .then().statusCode(HttpStatus.BAD_REQUEST.value()); + } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 투표_시_참여코드는_널이거나_빈_문자열일_수_없다(String participateCode) { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + VoteRequest voteRequest = getVoteRequestBuilder() + .set("participateCode", participateCode) + .sample(); + + given() + .contentType(ContentType.JSON) + .body(voteRequest) + .pathParam("pollId", pollEntity.getId()) + .when().post("/api/polls/{pollId}/votes") + .then().statusCode(HttpStatus.BAD_REQUEST.value()); + } + + @Test + void 이미_참여한_선거에_투표_할_수_없다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + String participatecode = UUID.randomUUID().toString(); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리", participatecode); + VoteRequest voteRequest = getVoteRequestBuilder() + .set("participateCode", participatecode) + .sample(); + + given() + .contentType(ContentType.JSON) + .body(voteRequest) + .pathParam("pollId", pollEntity.getId()) + .when().post("/api/polls/{pollId}/votes") + .then().statusCode(HttpStatus.BAD_REQUEST.value()); + } + + @Test + void 끝난_선거에_투표_할_수_없다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity alreadyDonePoll = pollEntityGenerator.generate(table, PollStatus.DONE); + VoteRequest voteRequest = getVoteRequestBuilder().sample(); + + given() + .contentType(ContentType.JSON) + .body(voteRequest) + .pathParam("pollId", alreadyDonePoll.getId()) + .when().post("/api/polls/{pollId}/votes") + .then().statusCode(HttpStatus.BAD_REQUEST.value()); + } + } +} diff --git a/src/test/java/com/debatetimer/controller/poll/VoteDocumentTest.java b/src/test/java/com/debatetimer/controller/poll/VoteDocumentTest.java new file mode 100644 index 00000000..998c6989 --- /dev/null +++ b/src/test/java/com/debatetimer/controller/poll/VoteDocumentTest.java @@ -0,0 +1,156 @@ +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.mockito.Mockito.doThrow; +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.poll.PollStatus; +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.dto.poll.request.VoteRequest; +import com.debatetimer.dto.poll.response.VoteCreateResponse; +import com.debatetimer.dto.poll.response.VoterPollInfoResponse; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import io.restassured.http.ContentType; +import java.util.UUID; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +public class VoteDocumentTest extends BaseDocumentTest { + + @Nested + class GetVotersPollInfo { + + private final RestDocumentationRequest requestDocument = request() + .tag(Tag.POLL_API) + .summary("투표자 - 선거 정보 조회") + .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("participateCode").type(STRING).description("참여 코드"), + fieldWithPath("totalCount").type(NUMBER).description("전체 투표 수"), + fieldWithPath("prosCount").type(NUMBER).description("찬성 투표 수"), + fieldWithPath("consCount").type(NUMBER).description("반대 투표 수") + ); + + @Test + void 투표자_선거_정보_조회() { + VoterPollInfoResponse response = new VoterPollInfoResponse( + 1L, + PollStatus.PROGRESS, + "찬성", + "반대", + UUID.randomUUID().toString(), + 3L, + 2L, + 1L + ); + doReturn(response).when(voteService).getVoterPollInfo(anyLong()); + + var document = document("vote/get", 200) + .request(requestDocument) + .response(responseDocument) + .build(); + + given(document) + .contentType(ContentType.JSON) + .pathParam("pollId", 1l) + .when().get("/api/polls/{pollId}/votes") + .then().statusCode(200); + } + } + + @Nested + class VotePoll { + + private final RestDocumentationRequest requestDocument = request() + .tag(Tag.POLL_API) + .summary("투표자 - 선거 투표") + .pathParameter( + parameterWithName("pollId").description("선거 ID") + ) + .requestBodyField( + fieldWithPath("name").type(STRING).description("투표자 이름"), + fieldWithPath("participateCode").type(STRING).description("투표 참여 코드"), + fieldWithPath("team").type(STRING).description("투표 팀") + ); + + private final RestDocumentationResponse responseDocument = response() + .responseBodyField( + fieldWithPath("id").type(NUMBER).description("투표 ID"), + fieldWithPath("name").type(STRING).description("투표자 이름"), + fieldWithPath("participateCode").type(STRING).description("투표 참여 코드"), + fieldWithPath("team").type(STRING).description("투표 팀") + ); + + @Test + void 투표자_선거_정보_조회() { + VoteRequest voteRequest = new VoteRequest("콜리", UUID.randomUUID().toString(), VoteTeam.PROS); + VoteCreateResponse response = new VoteCreateResponse( + 1L, + voteRequest.name(), + voteRequest.participateCode(), + voteRequest.team() + ); + doReturn(response).when(voteService).vote(anyLong(), any()); + + var document = document("vote/post", 201) + .request(requestDocument) + .response(responseDocument) + .build(); + + given(document) + .contentType(ContentType.JSON) + .body(voteRequest) + .pathParam("pollId", 1l) + .when().post("/api/polls/{pollId}/votes") + .then().statusCode(201); + } + + @EnumSource( + value = ClientErrorCode.class, + names = { + "ALREADY_DONE_POLL", + "ALREADY_VOTED_PARTICIPANT", + "INVALID_POLL_PARTICIPANT_CODE", + "INVALID_POLL_PARTICIPANT_NAME" + } + ) + @ParameterizedTest + void 투표자_투표_실패(ClientErrorCode clientErrorCode) { + VoteRequest voteRequest = new VoteRequest("콜리", UUID.randomUUID().toString(), VoteTeam.PROS); + doThrow(new DTClientErrorException(clientErrorCode)).when(voteService).vote(anyLong(), any()); + + var document = document("vote/post", clientErrorCode) + .request(requestDocument) + .response(ERROR_RESPONSE) + .build(); + + given(document) + .contentType(ContentType.JSON) + .body(voteRequest) + .pathParam("pollId", 1l) + .when().post("/api/polls/{pollId}/votes") + .then().statusCode(clientErrorCode.getStatus().value()); + + } + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/BellTest.java b/src/test/java/com/debatetimer/domain/customize/BellTest.java index a99f40eb..6ec93af3 100644 --- a/src/test/java/com/debatetimer/domain/customize/BellTest.java +++ b/src/test/java/com/debatetimer/domain/customize/BellTest.java @@ -6,7 +6,6 @@ import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -15,23 +14,10 @@ class BellTest { @Nested class Validate { - @Test - void 벨_시간이_음수면_생성되지_않는다() { - assertThatThrownBy(() -> new Bell(-1, 1)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_BELL_TIME.getMessage()); - } - - @Test - void 벨_시간은_0이상이어야_한다() { - assertThatCode(() -> new Bell(0, 1)) - .doesNotThrowAnyException(); - } - @ValueSource(ints = {0, Bell.MAX_BELL_COUNT + 1}) @ParameterizedTest void 벨_횟수는_정해진_횟수_바깥일_경우_생성되지_않는다(int count) { - assertThatThrownBy(() -> new Bell(1, count)) + assertThatThrownBy(() -> new Bell(BellType.AFTER_START, 1, count)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_BELL_COUNT.getMessage()); } @@ -39,7 +25,7 @@ class Validate { @ValueSource(ints = {1, Bell.MAX_BELL_COUNT}) @ParameterizedTest void 벨_횟수는_정해진_횟수_이내여야_한다(int count) { - assertThatCode(() -> new Bell(1, count)) + assertThatCode(() -> new Bell(BellType.AFTER_START, 1, count)) .doesNotThrowAnyException(); } } diff --git a/src/test/java/com/debatetimer/domain/customize/BellTypeTest.java b/src/test/java/com/debatetimer/domain/customize/BellTypeTest.java new file mode 100644 index 00000000..8500d326 --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/BellTypeTest.java @@ -0,0 +1,36 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class BellTypeTest { + + @Nested + class ValidateTime { + + @Test + void 벨_타입이_AFTER_START일때_시간은_0이상이어야_한다() { + assertThatThrownBy(() -> BellType.AFTER_START.validateTime(-1)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_BELL_TIME.getMessage()); + } + + @Test + void 벨_타입이_BEFORE_END일때_시간은_0이상이어야_한다() { + assertThatThrownBy(() -> BellType.BEFORE_END.validateTime(-1)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_BELL_TIME.getMessage()); + } + + @Test + void 벨_타입이_AFTER_END일때_시간은_0이하여야_한다() { + assertThatThrownBy(() -> BellType.AFTER_END.validateTime(1)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_BELL_TIME.getMessage()); + } + } +} diff --git a/src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java index 37ac4fba..485a7f25 100644 --- a/src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java +++ b/src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java @@ -1,10 +1,18 @@ 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.fixture.domain.BellGenerator; +import com.debatetimer.fixture.domain.CustomizeTableGenerator; +import com.debatetimer.fixture.domain.CustomizeTimeBoxGenerator; +import com.debatetimer.fixture.entity.BellEntityGenerator; +import com.debatetimer.fixture.entity.CustomizeTableEntityGenerator; +import com.debatetimer.fixture.entity.CustomizeTimeBoxEntityGenerator; +import com.debatetimer.fixture.entity.MemberGenerator; +import com.debatetimer.fixture.entity.PollEntityGenerator; +import com.debatetimer.fixture.entity.VoteEntityGenerator; +import com.debatetimer.repository.customize.BellRepository; +import com.debatetimer.repository.customize.CustomizeTableRepository; +import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import com.debatetimer.repository.poll.PollRepository; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -14,18 +22,42 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) public abstract class BaseDomainRepositoryTest { + @Autowired + protected CustomizeTableGenerator tableGenerator; + + @Autowired + protected CustomizeTimeBoxGenerator timeBoxGenerator; + + @Autowired + protected BellGenerator bellGenerator; + @Autowired protected MemberGenerator memberGenerator; @Autowired - protected CustomizeTableGenerator customizeTableGenerator; + protected CustomizeTableEntityGenerator tableEntityGenerator; + + @Autowired + protected CustomizeTimeBoxEntityGenerator timeBoxEntityGenerator; @Autowired - protected PollGenerator pollGenerator; + protected BellEntityGenerator bellEntityGenerator; @Autowired - protected VoteGenerator voteGenerator; + protected PollEntityGenerator pollEntityGenerator; + + @Autowired + protected VoteEntityGenerator voteEntityGenerator; @Autowired protected PollRepository pollRepository; + + @Autowired + protected CustomizeTableRepository tableRepository; + + @Autowired + protected CustomizeTimeBoxRepository timeBoxRepository; + + @Autowired + protected BellRepository bellRepository; } diff --git a/src/test/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepositoryTest.java new file mode 100644 index 00000000..e9108935 --- /dev/null +++ b/src/test/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepositoryTest.java @@ -0,0 +1,159 @@ +package com.debatetimer.domainrepository.customize; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.domain.customize.Bell; +import com.debatetimer.domain.customize.BellType; +import com.debatetimer.domain.customize.CustomizeBoxType; +import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.member.Member; +import com.debatetimer.domainrepository.BaseDomainRepositoryTest; +import com.debatetimer.entity.customize.BellEntity; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class CustomizeTableDomainRepositoryTest extends BaseDomainRepositoryTest { + + @Autowired + private CustomizeTableDomainRepository customizeTableDomainRepository; + + @Nested + class Save { + + @Test + void 테이블을_저장한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTable table = tableGenerator.generate(member); + List bells = bellGenerator.generate(3); + CustomizeTimeBox timeBox1 = timeBoxGenerator.generate(bells); + CustomizeTimeBox timeBox2 = timeBoxGenerator.generate(Collections.emptyList()); + + CustomizeTable savedTable = customizeTableDomainRepository.save(table, List.of(timeBox1, timeBox2)); + List timeBoxEntities = timeBoxRepository.findAllByCustomizeTable( + new CustomizeTableEntity(savedTable)); + List bellEntities = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntities); + + assertAll( + () -> assertThat(savedTable.getName()).isEqualTo(table.getName()), + () -> assertThat(timeBoxEntities).hasSize(2), + () -> assertThat(bellEntities).hasSize(3) + ); + } + } + + @Nested + class GetByIdAndMember { + + @Test + void 회원이_소유한_테이블을_가져온다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = tableEntityGenerator.generate(member); + + CustomizeTable foundTable = customizeTableDomainRepository.getByIdAndMember(table.getId(), member); + + assertThat(foundTable.getId()).isEqualTo(table.getId()); + } + } + + @Nested + class GetCustomizeTimeBoxes { + + @Test + void 테이블의_시간박스를_가져온다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity tableEntity = tableEntityGenerator.generate(member); + timeBoxEntityGenerator.generate(tableEntity, CustomizeBoxType.NORMAL, 1); + timeBoxEntityGenerator.generate(tableEntity, CustomizeBoxType.NORMAL, 2); + + List timeBoxes = customizeTableDomainRepository.getCustomizeTimeBoxes( + tableEntity.getId(), member); + + assertThat(timeBoxes).hasSize(2); + } + } + + @Nested + class Update { + + @Test + void 테이블을_수정한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity tableEntity = tableEntityGenerator.generate(member); + CustomizeTimeBoxEntity timeBox1 = timeBoxEntityGenerator.generate(tableEntity, CustomizeBoxType.NORMAL, 1); + CustomizeTimeBoxEntity timeBox2 = timeBoxEntityGenerator.generate(tableEntity, CustomizeBoxType.NORMAL, 2); + bellEntityGenerator.generate(timeBox1, BellType.AFTER_START, 10, 1); + bellEntityGenerator.generate(timeBox1, BellType.AFTER_START, 20, 1); + bellEntityGenerator.generate(timeBox2, BellType.AFTER_START, 20, 1); + + CustomizeTable table = tableGenerator.generate(member, "수정된 테이블"); + List bells = bellGenerator.generate(3); + CustomizeTimeBox timeBox = timeBoxGenerator.generate(bells, "수정"); + + CustomizeTable updatedTable = customizeTableDomainRepository.update(table, tableEntity.getId(), member, + List.of(timeBox)); + CustomizeTableEntity foundTable = tableRepository.getByIdAndMember(tableEntity.getId(), member); + List timeBoxEntities = timeBoxRepository.findAllByCustomizeTable( + new CustomizeTableEntity(updatedTable)); + List bellEntities = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntities); + + assertAll( + () -> assertThat(foundTable.getName()).isEqualTo(updatedTable.getName()), + () -> assertThat(timeBoxEntities).hasSize(1), + () -> assertThat(bellEntities).hasSize(3) + ); + } + } + + @Nested + class UpdateUsedAt { + + @Test + void 테이블의_사용_시간을_업데이트한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity tableEntity = tableEntityGenerator.generate(member); + LocalDateTime beforeUsedAt = tableEntity.getUsedAt(); + + CustomizeTable customizeTable = customizeTableDomainRepository.updateUsedAt(tableEntity.getId(), member); + + assertAll( + () -> assertThat(customizeTable.getId()).isEqualTo(tableEntity.getId()), + () -> assertThat(customizeTable.getUsedAt()).isAfter(beforeUsedAt) + ); + } + } + + @Nested + class Delete { + + @Test + void 테이블을_삭제한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = tableEntityGenerator.generate(member); + CustomizeTimeBoxEntity timeBox1 = timeBoxEntityGenerator.generate(table, CustomizeBoxType.NORMAL, 1); + CustomizeTimeBoxEntity timeBox2 = timeBoxEntityGenerator.generate(table, CustomizeBoxType.NORMAL, 2); + bellEntityGenerator.generate(timeBox1, BellType.AFTER_START, 10, 1); + bellEntityGenerator.generate(timeBox1, BellType.AFTER_START, 20, 1); + bellEntityGenerator.generate(timeBox2, BellType.AFTER_START, 20, 1); + + customizeTableDomainRepository.delete(table.getId(), member); + Optional foundTable = tableRepository.findById(table.getId()); + List timeBoxEntities = timeBoxRepository.findAllByCustomizeTable(table); + List bellEntities = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntities); + + assertAll( + () -> assertThat(foundTable).isEmpty(), + () -> assertThat(timeBoxEntities).isEmpty(), + () -> assertThat(bellEntities).isEmpty() + ); + } + } +} diff --git a/src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java index c97849e4..48a65773 100644 --- a/src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java +++ b/src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java @@ -25,7 +25,7 @@ class Create { @Test void 선거를_생성한다() { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); + CustomizeTableEntity table = tableEntityGenerator.generate(member); Poll poll = new Poll(table.getId(), member.getId(), "찬성", "반대", "주제"); Poll createdPoll = pollDomainRepository.create(poll); @@ -41,8 +41,8 @@ class GetByIdAndMemberId { @Test void 회원이_개최한_선거를_가져온다() { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + CustomizeTableEntity table = tableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); Poll foundPoll = pollDomainRepository.getByIdAndMemberId(pollEntity.getId(), member.getId()); @@ -63,8 +63,8 @@ class FinishPoll { @Test void 선거를_완료_상태로_변경한다() { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + CustomizeTableEntity table = tableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); Poll updatedPoll = pollDomainRepository.finishPoll(pollEntity.getId(), member.getId()); diff --git a/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java index 068e782e..1aea13bb 100644 --- a/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java +++ b/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java @@ -1,15 +1,21 @@ package com.debatetimer.domainrepository.poll; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.ParticipateCode; import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domain.poll.Vote; 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 com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import java.util.UUID; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -25,11 +31,11 @@ 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, "커찬"); + CustomizeTableEntity table = tableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteEntityGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); VoteInfo voteInfo = voteDomainRepository.findVoteInfoByPollId(pollEntity.getId()); @@ -42,4 +48,60 @@ class GetVoteInfo { } } + @Nested + class isExists { + + @Test + void 이미_참여한_투표인지_알_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = tableEntityGenerator.generate(member); + PollEntity alreadyParticipatedPoll = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + PollEntity notYetParticipatedPoll = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + ParticipateCode participateCode = new ParticipateCode(UUID.randomUUID().toString()); + voteEntityGenerator.generate(alreadyParticipatedPoll, VoteTeam.PROS, "콜리", participateCode.getValue()); + + boolean participated = voteDomainRepository.isExists(alreadyParticipatedPoll.getId(), participateCode); + boolean notYetParticipated = voteDomainRepository.isExists(notYetParticipatedPoll.getId(), + participateCode); + + assertAll( + () -> assertThat(participated).isTrue(), + () -> assertThat(notYetParticipated).isFalse() + ); + } + } + + @Nested + class Save { + + @Test + void 투표를_저장할_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = tableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + Vote vote = new Vote(pollEntity.getId(), VoteTeam.PROS, "콜리", UUID.randomUUID().toString()); + + Vote savedVote = voteDomainRepository.save(vote); + + assertAll( + () -> assertThat(savedVote.getName().getValue()).isEqualTo(vote.getName().getValue()), + () -> assertThat(savedVote.getCode().getValue()).isEqualTo(vote.getCode().getValue()), + () -> assertThat(savedVote.getTeam()).isEqualTo(vote.getTeam()) + ); + } + + @Test + void 중복_투표할_수_없다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = tableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + String participateCode = UUID.randomUUID().toString(); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리", participateCode); + Vote vote = new Vote(pollEntity.getId(), VoteTeam.PROS, "콜리", participateCode); + + assertThatThrownBy(() -> voteDomainRepository.save(vote)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.ALREADY_VOTED_PARTICIPANT.getMessage()); + } + } } diff --git a/src/test/java/com/debatetimer/entity/customize/BellEntityTest.java b/src/test/java/com/debatetimer/entity/customize/BellEntityTest.java index 73535499..c0483f9f 100644 --- a/src/test/java/com/debatetimer/entity/customize/BellEntityTest.java +++ b/src/test/java/com/debatetimer/entity/customize/BellEntityTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; +import com.debatetimer.domain.customize.BellType; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import org.junit.jupiter.api.Nested; @@ -16,7 +17,7 @@ class Validate { @Test void 벨_시간은_0이상이어야_한다() { - assertThatThrownBy(() -> new BellEntity(null, -1, 1)) + assertThatThrownBy(() -> new BellEntity(null, BellType.AFTER_END, -1, 1)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_BELL_TIME.getMessage()); } @@ -24,7 +25,7 @@ class Validate { @ValueSource(ints = {0, BellEntity.MAX_BELL_COUNT + 1}) @ParameterizedTest void 벨_횟수는_정해진_횟수_이내여야_한다(int count) { - assertThatThrownBy(() -> new BellEntity(null, 1, count)) + assertThatThrownBy(() -> new BellEntity(null, BellType.AFTER_END, 1, count)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_BELL_COUNT.getMessage()); } diff --git a/src/test/java/com/debatetimer/exception/decoder/H2ErrorDecoderTest.java b/src/test/java/com/debatetimer/exception/decoder/H2ErrorDecoderTest.java new file mode 100644 index 00000000..147ff327 --- /dev/null +++ b/src/test/java/com/debatetimer/exception/decoder/H2ErrorDecoderTest.java @@ -0,0 +1,51 @@ +package com.debatetimer.exception.decoder; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.sql.SQLException; +import org.hibernate.exception.ConstraintViolationException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.dao.DataIntegrityViolationException; + +class H2ErrorDecoderTest { + + private H2ErrorDecoder errorDecoder; + + + @BeforeEach + void setUp() { + errorDecoder = new H2ErrorDecoder(); + } + + @Nested + class isUniqueError { + + @Test + void 유니크_제약조건_에러를_판단할_수_있다() { + SQLException uniqueError = new SQLException("유니크 에러", H2ErrorDecoder.UNIQUE_CONSTRAINT_VIOLATION_SQL_STATE); + ConstraintViolationException uniqueViolation = new ConstraintViolationException("유니크 에러", uniqueError, + "vote_poll_id_participate_code"); + DataIntegrityViolationException uniqueException = new DataIntegrityViolationException("유니크 에러", + uniqueViolation); + + boolean isUniqueError = errorDecoder.isUniqueConstraintViolation(uniqueException); + + assertThat(isUniqueError).isTrue(); + } + + @Test + void 유니크_제약조건_에러가_아님을_판단한다() { + SQLException notUniqueError = new SQLException("다른 에러", "23000"); + ConstraintViolationException notUniqueViolation = new ConstraintViolationException("기타 에러", notUniqueError, + "some_constraint"); + DataIntegrityViolationException extraException = new DataIntegrityViolationException("에러", + notUniqueViolation); + + boolean isNotUniqueError = errorDecoder.isUniqueConstraintViolation(extraException); + + assertThat(isNotUniqueError).isFalse(); + } + } +} diff --git a/src/test/java/com/debatetimer/exception/decoder/MySqlErrorDecoderTest.java b/src/test/java/com/debatetimer/exception/decoder/MySqlErrorDecoderTest.java new file mode 100644 index 00000000..f49d5978 --- /dev/null +++ b/src/test/java/com/debatetimer/exception/decoder/MySqlErrorDecoderTest.java @@ -0,0 +1,54 @@ +package com.debatetimer.exception.decoder; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.sql.SQLException; +import org.hibernate.exception.ConstraintViolationException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.dao.DataIntegrityViolationException; + +class MySqlErrorDecoderTest { + + private MySqlErrorDecoder errorDecoder; + + + @BeforeEach + void setUp() { + errorDecoder = new MySqlErrorDecoder(); + } + + @Nested + class isUniqueError { + + @Test + void 유니크_제약조건_에러를_판단할_수_있다() { + SQLException uniqueError = new SQLException("유니크 에러", + MySqlErrorDecoder.MYSQL_UNIQUE_VIOLATION, + MySqlErrorDecoder.MYSQL_DUP_ERROR_CODE + ); + ConstraintViolationException uniqueViolation = new ConstraintViolationException("유니크 에러", uniqueError, + "vote_poll_id_participate_code"); + DataIntegrityViolationException uniqueException = new DataIntegrityViolationException("유니크 에러", + uniqueViolation); + + boolean isUniqueError = errorDecoder.isUniqueConstraintViolation(uniqueException); + + assertThat(isUniqueError).isTrue(); + } + + @Test + void 유니크_제약조건_에러가_아님을_판단한다() { + SQLException notUniqueError = new SQLException("다른 에러", "32050", 1234); + ConstraintViolationException notUniqueViolation = new ConstraintViolationException("기타 에러", notUniqueError, + "some_constraint"); + DataIntegrityViolationException extraException = new DataIntegrityViolationException("에러", + notUniqueViolation); + + boolean isNotUniqueError = errorDecoder.isUniqueConstraintViolation(extraException); + + assertThat(isNotUniqueError).isFalse(); + } + } +} diff --git a/src/test/java/com/debatetimer/fixture/domain/BellGenerator.java b/src/test/java/com/debatetimer/fixture/domain/BellGenerator.java new file mode 100644 index 00000000..4db99724 --- /dev/null +++ b/src/test/java/com/debatetimer/fixture/domain/BellGenerator.java @@ -0,0 +1,23 @@ +package com.debatetimer.fixture.domain; + +import com.debatetimer.domain.customize.Bell; +import com.debatetimer.domain.customize.BellType; +import java.util.ArrayList; +import java.util.List; +import org.springframework.stereotype.Component; + +@Component +public class BellGenerator { + + public List generate(int size) { + List bells = new ArrayList<>(); + for (int i = 0; i < size; i++) { + bells.add(generate()); + } + return bells; + } + + private Bell generate() { + return new Bell(BellType.AFTER_START, 10, 1); + } +} diff --git a/src/test/java/com/debatetimer/fixture/domain/CustomizeTableGenerator.java b/src/test/java/com/debatetimer/fixture/domain/CustomizeTableGenerator.java new file mode 100644 index 00000000..3efb05cc --- /dev/null +++ b/src/test/java/com/debatetimer/fixture/domain/CustomizeTableGenerator.java @@ -0,0 +1,36 @@ +package com.debatetimer.fixture.domain; + +import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.member.Member; +import java.time.LocalDateTime; +import org.springframework.stereotype.Component; + +@Component +public class CustomizeTableGenerator { + + public CustomizeTable generate(Member member) { + return new CustomizeTable( + member, + "토론 테이블", + "주제", + "찬성", + "반대", + false, + false, + LocalDateTime.now() + ); + } + + public CustomizeTable generate(Member member, String name) { + return new CustomizeTable( + member, + name, + "주제", + "찬성", + "반대", + false, + false, + LocalDateTime.now() + ); + } +} diff --git a/src/test/java/com/debatetimer/fixture/domain/CustomizeTimeBoxGenerator.java b/src/test/java/com/debatetimer/fixture/domain/CustomizeTimeBoxGenerator.java new file mode 100644 index 00000000..35bc429e --- /dev/null +++ b/src/test/java/com/debatetimer/fixture/domain/CustomizeTimeBoxGenerator.java @@ -0,0 +1,32 @@ +package com.debatetimer.fixture.domain; + +import com.debatetimer.domain.customize.Bell; +import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.NormalTimeBox; +import com.debatetimer.domain.customize.Stance; +import java.util.List; +import org.springframework.stereotype.Component; + +@Component +public class CustomizeTimeBoxGenerator { + + public CustomizeTimeBox generate(List bells) { + return new NormalTimeBox( + Stance.PROS, + "입론", + "콜리", + 10, + bells + ); + } + + public CustomizeTimeBox generate(List bells, String speechType) { + return new NormalTimeBox( + Stance.PROS, + speechType, + "콜리", + 10, + bells + ); + } +} diff --git a/src/test/java/com/debatetimer/fixture/BellGenerator.java b/src/test/java/com/debatetimer/fixture/entity/BellEntityGenerator.java similarity index 52% rename from src/test/java/com/debatetimer/fixture/BellGenerator.java rename to src/test/java/com/debatetimer/fixture/entity/BellEntityGenerator.java index 1c2392ce..53f5d421 100644 --- a/src/test/java/com/debatetimer/fixture/BellGenerator.java +++ b/src/test/java/com/debatetimer/fixture/entity/BellEntityGenerator.java @@ -1,21 +1,22 @@ -package com.debatetimer.fixture; +package com.debatetimer.fixture.entity; +import com.debatetimer.domain.customize.BellType; import com.debatetimer.entity.customize.BellEntity; import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.repository.customize.BellRepository; import org.springframework.stereotype.Component; @Component -public class BellGenerator { +public class BellEntityGenerator { private final BellRepository bellRepository; - public BellGenerator(BellRepository bellRepository) { + public BellEntityGenerator(BellRepository bellRepository) { this.bellRepository = bellRepository; } - public BellEntity generate(CustomizeTimeBoxEntity timeBox, int time, int count) { - BellEntity bell = new BellEntity(timeBox, time, count); + public BellEntity generate(CustomizeTimeBoxEntity timeBox, BellType type, int time, int count) { + BellEntity bell = new BellEntity(timeBox, type, time, count); return bellRepository.save(bell); } } diff --git a/src/test/java/com/debatetimer/fixture/CustomizeTableGenerator.java b/src/test/java/com/debatetimer/fixture/entity/CustomizeTableEntityGenerator.java similarity index 84% rename from src/test/java/com/debatetimer/fixture/CustomizeTableGenerator.java rename to src/test/java/com/debatetimer/fixture/entity/CustomizeTableEntityGenerator.java index 80a7b8dc..93ec96aa 100644 --- a/src/test/java/com/debatetimer/fixture/CustomizeTableGenerator.java +++ b/src/test/java/com/debatetimer/fixture/entity/CustomizeTableEntityGenerator.java @@ -1,4 +1,4 @@ -package com.debatetimer.fixture; +package com.debatetimer.fixture.entity; import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; @@ -8,11 +8,11 @@ import org.springframework.stereotype.Component; @Component -public class CustomizeTableGenerator { +public class CustomizeTableEntityGenerator { private final CustomizeTableRepository customizeTableRepository; - public CustomizeTableGenerator(CustomizeTableRepository customizeTableRepository) { + public CustomizeTableEntityGenerator(CustomizeTableRepository customizeTableRepository) { this.customizeTableRepository = customizeTableRepository; } diff --git a/src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java b/src/test/java/com/debatetimer/fixture/entity/CustomizeTimeBoxEntityGenerator.java similarity index 88% rename from src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java rename to src/test/java/com/debatetimer/fixture/entity/CustomizeTimeBoxEntityGenerator.java index e068269d..7b2ca578 100644 --- a/src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java +++ b/src/test/java/com/debatetimer/fixture/entity/CustomizeTimeBoxEntityGenerator.java @@ -1,4 +1,4 @@ -package com.debatetimer.fixture; +package com.debatetimer.fixture.entity; import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.Stance; @@ -8,11 +8,11 @@ import org.springframework.stereotype.Component; @Component -public class CustomizeTimeBoxGenerator { +public class CustomizeTimeBoxEntityGenerator { private final CustomizeTimeBoxRepository customizeTimeBoxRepository; - public CustomizeTimeBoxGenerator(CustomizeTimeBoxRepository customizeTimeBoxRepository) { + public CustomizeTimeBoxEntityGenerator(CustomizeTimeBoxRepository customizeTimeBoxRepository) { this.customizeTimeBoxRepository = customizeTimeBoxRepository; } diff --git a/src/test/java/com/debatetimer/fixture/MemberGenerator.java b/src/test/java/com/debatetimer/fixture/entity/MemberGenerator.java similarity index 92% rename from src/test/java/com/debatetimer/fixture/MemberGenerator.java rename to src/test/java/com/debatetimer/fixture/entity/MemberGenerator.java index d0aa97bb..69dc56a3 100644 --- a/src/test/java/com/debatetimer/fixture/MemberGenerator.java +++ b/src/test/java/com/debatetimer/fixture/entity/MemberGenerator.java @@ -1,4 +1,4 @@ -package com.debatetimer.fixture; +package com.debatetimer.fixture.entity; import com.debatetimer.domain.member.Member; import com.debatetimer.repository.member.MemberRepository; diff --git a/src/test/java/com/debatetimer/fixture/PollGenerator.java b/src/test/java/com/debatetimer/fixture/entity/PollEntityGenerator.java similarity index 85% rename from src/test/java/com/debatetimer/fixture/PollGenerator.java rename to src/test/java/com/debatetimer/fixture/entity/PollEntityGenerator.java index 24059cba..5f01a4b5 100644 --- a/src/test/java/com/debatetimer/fixture/PollGenerator.java +++ b/src/test/java/com/debatetimer/fixture/entity/PollEntityGenerator.java @@ -1,4 +1,4 @@ -package com.debatetimer.fixture; +package com.debatetimer.fixture.entity; import com.debatetimer.domain.poll.Poll; import com.debatetimer.domain.poll.PollStatus; @@ -8,11 +8,11 @@ import org.springframework.stereotype.Component; @Component -public class PollGenerator { +public class PollEntityGenerator { private final PollRepository pollRepository; - public PollGenerator(final PollRepository pollRepository) { + public PollEntityGenerator(final PollRepository pollRepository) { this.pollRepository = pollRepository; } diff --git a/src/test/java/com/debatetimer/fixture/VoteGenerator.java b/src/test/java/com/debatetimer/fixture/entity/VoteEntityGenerator.java similarity index 63% rename from src/test/java/com/debatetimer/fixture/VoteGenerator.java rename to src/test/java/com/debatetimer/fixture/entity/VoteEntityGenerator.java index 9efdd80c..b083ce45 100644 --- a/src/test/java/com/debatetimer/fixture/VoteGenerator.java +++ b/src/test/java/com/debatetimer/fixture/entity/VoteEntityGenerator.java @@ -1,4 +1,4 @@ -package com.debatetimer.fixture; +package com.debatetimer.fixture.entity; import com.debatetimer.domain.poll.VoteTeam; import com.debatetimer.entity.poll.PollEntity; @@ -8,16 +8,20 @@ import org.springframework.stereotype.Component; @Component -public class VoteGenerator { +public class VoteEntityGenerator { private final VoteRepository voteRepository; - public VoteGenerator(VoteRepository voteRepository) { + public VoteEntityGenerator(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 generate(pollEntity, team, name, UUID.randomUUID().toString()); + } + + public VoteEntity generate(PollEntity pollEntity, VoteTeam team, String name, String code) { + VoteEntity vote = new VoteEntity(null, pollEntity, team, name, code); 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 87b5404b..ab973a1e 100644 --- a/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java @@ -1,10 +1,10 @@ package com.debatetimer.repository; import com.debatetimer.config.JpaAuditingConfig; -import com.debatetimer.fixture.BellGenerator; -import com.debatetimer.fixture.CustomizeTableGenerator; -import com.debatetimer.fixture.CustomizeTimeBoxGenerator; -import com.debatetimer.fixture.MemberGenerator; +import com.debatetimer.fixture.entity.BellEntityGenerator; +import com.debatetimer.fixture.entity.CustomizeTableEntityGenerator; +import com.debatetimer.fixture.entity.CustomizeTimeBoxEntityGenerator; +import com.debatetimer.fixture.entity.MemberGenerator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; @@ -12,9 +12,9 @@ @Import({ JpaAuditingConfig.class, MemberGenerator.class, - CustomizeTableGenerator.class, - CustomizeTimeBoxGenerator.class, - BellGenerator.class, + CustomizeTableEntityGenerator.class, + CustomizeTimeBoxEntityGenerator.class, + BellEntityGenerator.class, }) @DataJpaTest public abstract class BaseRepositoryTest { @@ -23,11 +23,11 @@ public abstract class BaseRepositoryTest { protected MemberGenerator memberGenerator; @Autowired - protected CustomizeTableGenerator customizeTableGenerator; + protected CustomizeTableEntityGenerator customizeTableEntityGenerator; @Autowired - protected CustomizeTimeBoxGenerator customizeTimeBoxGenerator; + protected CustomizeTimeBoxEntityGenerator customizeTimeBoxEntityGenerator; @Autowired - protected BellGenerator bellGenerator; + protected BellEntityGenerator bellEntityGenerator; } diff --git a/src/test/java/com/debatetimer/repository/customize/BellRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/BellRepositoryTest.java index 6dbb6919..7747fb03 100644 --- a/src/test/java/com/debatetimer/repository/customize/BellRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/BellRepositoryTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import com.debatetimer.domain.customize.BellType; import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.member.Member; import com.debatetimer.entity.customize.CustomizeTableEntity; @@ -24,15 +25,15 @@ class DeleteAllByTable { @Test void 특정_테이블에_해당하는_벨을_삭제한다() { Member member = memberGenerator.generate("chan@gmail.com"); - CustomizeTableEntity deleteBellTable = customizeTableGenerator.generate(member); - CustomizeTableEntity otherTable = customizeTableGenerator.generate(member); - CustomizeTimeBoxEntity deleteBellTimeBox = customizeTimeBoxGenerator.generate(deleteBellTable, + CustomizeTableEntity deleteBellTable = customizeTableEntityGenerator.generate(member); + CustomizeTableEntity otherTable = customizeTableEntityGenerator.generate(member); + CustomizeTimeBoxEntity deleteBellTimeBox = customizeTimeBoxEntityGenerator.generate(deleteBellTable, CustomizeBoxType.NORMAL, 1); - CustomizeTimeBoxEntity otherTimeBox = customizeTimeBoxGenerator.generate(otherTable, + CustomizeTimeBoxEntity otherTimeBox = customizeTimeBoxEntityGenerator.generate(otherTable, CustomizeBoxType.NORMAL, 1); - bellGenerator.generate(deleteBellTimeBox, 45, 1); - bellGenerator.generate(deleteBellTimeBox, 60, 1); - bellGenerator.generate(otherTimeBox, 45, 1); + bellEntityGenerator.generate(deleteBellTimeBox, BellType.AFTER_START, 45, 1); + bellEntityGenerator.generate(deleteBellTimeBox, BellType.AFTER_START, 60, 1); + bellEntityGenerator.generate(otherTimeBox, BellType.AFTER_START, 45, 1); bellRepository.deleteAllByTable(deleteBellTable.getId()); diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTableEntityRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTableEntityRepositoryTest.java index ebc3e626..428ae069 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTableEntityRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTableEntityRepositoryTest.java @@ -25,9 +25,9 @@ class FindAllByMember { void 특정_회원의_테이블만_조회한다() { Member chan = memberGenerator.generate("default@gmail.com"); Member bito = memberGenerator.generate("default2@gmail.com"); - CustomizeTableEntity chanTable1 = customizeTableGenerator.generate(chan); - CustomizeTableEntity chanTable2 = customizeTableGenerator.generate(chan); - customizeTableGenerator.generate(bito); + CustomizeTableEntity chanTable1 = customizeTableEntityGenerator.generate(chan); + CustomizeTableEntity chanTable2 = customizeTableEntityGenerator.generate(chan); + customizeTableEntityGenerator.generate(bito); List foundKeoChanTables = tableRepository.findAllByMember(chan); @@ -41,7 +41,7 @@ class GetByIdAndMember { @Test void 특정_회원의_테이블을_조회한다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(chan); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(chan); CustomizeTableEntity foundTable = tableRepository.getByIdAndMember(table.getId(), chan); @@ -51,7 +51,7 @@ class GetByIdAndMember { @Test void 존재하지_않는_테이블을_조회하면_예외를_던진다() { Member chan = memberGenerator.generate("default@gmail.com"); - customizeTableGenerator.generate(chan); + customizeTableEntityGenerator.generate(chan); long nonExistTableId = 99999999L; assertThatThrownBy(() -> tableRepository.getByIdAndMember(nonExistTableId, chan)) diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java index eb94339f..5fcf6b39 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java @@ -25,12 +25,14 @@ class FindAllByCustomizeTableEntity { void 특정_테이블의_타임박스를_모두_조회한다() { Member chan = memberGenerator.generate("default@gmail.com"); Member bito = memberGenerator.generate("default2@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); - CustomizeTimeBoxEntity chanBox1 = customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); - CustomizeTimeBoxEntity chanBox2 = customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); - customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 2); - customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 2); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); + CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); + CustomizeTimeBoxEntity chanBox1 = customizeTimeBoxEntityGenerator.generate(chanTable, + CustomizeBoxType.NORMAL, 1); + CustomizeTimeBoxEntity chanBox2 = customizeTimeBoxEntityGenerator.generate(chanTable, + CustomizeBoxType.NORMAL, 2); + customizeTimeBoxEntityGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 2); + customizeTimeBoxEntityGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 2); List foundBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(chanTable); @@ -44,9 +46,9 @@ class DeleteAllByTable { @Test void 특정_테이블의_타임박스를_모두_삭제한다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); - customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); + customizeTimeBoxEntityGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); + customizeTimeBoxEntityGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); customizeTimeBoxRepository.deleteAllByTable(chanTable.getId()); @@ -57,11 +59,11 @@ class DeleteAllByTable { @Test void 특정_테이블의_타임_박스를_삭제해도_다른_테이블의_타임_박스는_삭제되지_않는다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity filledTable = customizeTableGenerator.generate(chan); - customizeTimeBoxGenerator.generate(filledTable, CustomizeBoxType.NORMAL, 1); - customizeTimeBoxGenerator.generate(filledTable, CustomizeBoxType.NORMAL, 2); - CustomizeTableEntity deletedTable = customizeTableGenerator.generate(chan); - customizeTimeBoxGenerator.generate(deletedTable, CustomizeBoxType.NORMAL, 1); + CustomizeTableEntity filledTable = customizeTableEntityGenerator.generate(chan); + customizeTimeBoxEntityGenerator.generate(filledTable, CustomizeBoxType.NORMAL, 1); + customizeTimeBoxEntityGenerator.generate(filledTable, CustomizeBoxType.NORMAL, 2); + CustomizeTableEntity deletedTable = customizeTableEntityGenerator.generate(chan); + customizeTimeBoxEntityGenerator.generate(deletedTable, CustomizeBoxType.NORMAL, 1); customizeTimeBoxRepository.deleteAllByTable(deletedTable.getId()); @@ -72,7 +74,7 @@ class DeleteAllByTable { @Test void 테이블의_타임_박스가_없을_경우_타임_박스_삭제_시_예외가_발생하지_않는다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity emptyTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity emptyTable = customizeTableEntityGenerator.generate(chan); assertThatCode(() -> customizeTimeBoxRepository.deleteAllByTable(emptyTable.getId())) .doesNotThrowAnyException(); diff --git a/src/test/java/com/debatetimer/service/BaseServiceTest.java b/src/test/java/com/debatetimer/service/BaseServiceTest.java index a68e418d..3bd0a8c1 100644 --- a/src/test/java/com/debatetimer/service/BaseServiceTest.java +++ b/src/test/java/com/debatetimer/service/BaseServiceTest.java @@ -1,12 +1,12 @@ package com.debatetimer.service; import com.debatetimer.DataBaseCleaner; -import com.debatetimer.fixture.BellGenerator; -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.fixture.entity.BellEntityGenerator; +import com.debatetimer.fixture.entity.CustomizeTableEntityGenerator; +import com.debatetimer.fixture.entity.CustomizeTimeBoxEntityGenerator; +import com.debatetimer.fixture.entity.MemberGenerator; +import com.debatetimer.fixture.entity.PollEntityGenerator; +import com.debatetimer.fixture.entity.VoteEntityGenerator; import com.debatetimer.repository.customize.BellRepository; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; @@ -41,19 +41,19 @@ public abstract class BaseServiceTest { protected MemberGenerator memberGenerator; @Autowired - protected CustomizeTableGenerator customizeTableGenerator; + protected CustomizeTableEntityGenerator customizeTableEntityGenerator; @Autowired - protected CustomizeTimeBoxGenerator customizeTimeBoxGenerator; + protected CustomizeTimeBoxEntityGenerator customizeTimeBoxEntityGenerator; @Autowired - protected BellGenerator bellGenerator; + protected BellEntityGenerator bellEntityGenerator; @Autowired - protected PollGenerator pollGenerator; + protected PollEntityGenerator pollEntityGenerator; @Autowired - protected VoteGenerator voteGenerator; + protected VoteEntityGenerator voteEntityGenerator; protected void runAtSameTime(int count, Runnable task) throws InterruptedException { List threads = IntStream.range(0, count) diff --git a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java index 410c0042..664a50c5 100644 --- a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java +++ b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; +import com.debatetimer.domain.customize.BellType; import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.Stance; import com.debatetimer.domain.member.Member; @@ -12,6 +13,7 @@ import com.debatetimer.dto.customize.request.CustomizeTableInfoCreateRequest; import com.debatetimer.dto.customize.request.CustomizeTimeBoxCreateRequest; import com.debatetimer.dto.customize.response.CustomizeTableResponse; +import com.debatetimer.entity.customize.BellEntity; import com.debatetimer.entity.customize.CustomizeTableEntity; import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.exception.custom.DTClientErrorException; @@ -40,19 +42,23 @@ class Save { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1)), 60, null, "발언자1"), new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1), + new BellRequest(BellType.AFTER_START, 120, 2)), 60, null, "발언자2") ) ); CustomizeTableResponse savedTableResponse = customizeService.save(customizeTableCreateRequest, chan); CustomizeTableEntity foundTable = customizeTableRepository.getByIdAndMember(savedTableResponse.id(), chan); - List foundTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(foundTable); + List foundTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( + foundTable); + List foundBells = bellRepository.findAllByCustomizeTimeBoxIn(foundTimeBoxes); assertAll( () -> assertThat(foundTable.getName()).isEqualTo(customizeTableCreateRequest.info().name()), - () -> assertThat(foundTimeBoxes).hasSize(customizeTableCreateRequest.table().size()) + () -> assertThat(foundTimeBoxes).hasSize(customizeTableCreateRequest.table().size()), + () -> assertThat(foundBells).hasSize(3) ); } } @@ -63,15 +69,20 @@ class FindTable { @Test void 사용자_지정_토론_테이블을_조회한다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); - customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); + CustomizeTimeBoxEntity customizeTimeBox = customizeTimeBoxEntityGenerator.generate( + chanTable, CustomizeBoxType.NORMAL, 1); + customizeTimeBoxEntityGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); + bellEntityGenerator.generate(customizeTimeBox, BellType.AFTER_START, 1, 1); + bellEntityGenerator.generate(customizeTimeBox, BellType.AFTER_START, 1, 2); CustomizeTableResponse foundResponse = customizeService.findTable(chanTable.getId(), chan); assertAll( () -> assertThat(foundResponse.id()).isEqualTo(chanTable.getId()), - () -> assertThat(foundResponse.table()).hasSize(2) + () -> assertThat(foundResponse.table()).hasSize(2), + () -> assertThat(foundResponse.table().get(0).bell()).hasSize(2), + () -> assertThat(foundResponse.table().get(1).bell()).isNull() ); } @@ -79,7 +90,7 @@ class FindTable { void 회원_소유가_아닌_테이블_조회_시_예외를_발생시킨다() { Member chan = memberGenerator.generate("default@gmail.com"); Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); long chanTableId = chanTable.getId(); assertThatThrownBy(() -> customizeService.findTable(chanTableId, coli)) @@ -94,27 +105,31 @@ class UpdateTable { @Test void 사용자_지정_토론_테이블을_수정한다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); CustomizeTableCreateRequest renewTableRequest = new CustomizeTableCreateRequest( new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1)), 60, null, "발언자1"), new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1), + new BellRequest(BellType.AFTER_START, 120, 2)), 60, null, "발언자2") ) ); customizeService.updateTable(renewTableRequest, chanTable.getId(), chan); CustomizeTableEntity updatedTable = customizeTableRepository.getByIdAndMember(chanTable.getId(), chan); - List updatedTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(updatedTable); + List updatedTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( + updatedTable); + List bells = bellRepository.findAllByCustomizeTimeBoxIn(updatedTimeBoxes); assertAll( () -> assertThat(updatedTable.getId()).isEqualTo(chanTable.getId()), () -> assertThat(updatedTable.getName()).isEqualTo(renewTableRequest.info().name()), - () -> assertThat(updatedTimeBoxes).hasSize(renewTableRequest.table().size()) + () -> assertThat(updatedTimeBoxes).hasSize(renewTableRequest.table().size()), + () -> assertThat(bells).hasSize(3) ); } @@ -122,16 +137,17 @@ class UpdateTable { void 회원_소유가_아닌_테이블_수정_시_예외를_발생시킨다() { Member chan = memberGenerator.generate("default@gmail.com"); Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); long chanTableId = chanTable.getId(); CustomizeTableCreateRequest renewTableRequest = new CustomizeTableCreateRequest( new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1)), 60, null, "발언자1"), new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1), + new BellRequest(BellType.AFTER_START, 120, 2)), 60, null, "발언자2") ) ); @@ -143,15 +159,16 @@ class UpdateTable { @Test void 테이블_정보_수정을_동시에_요청할_때_동시에_처리하지_않는다() throws InterruptedException { Member member = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); CustomizeTableCreateRequest request = new CustomizeTableCreateRequest( new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1)), 60, null, "발언자1"), new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") + 120, List.of(new BellRequest(BellType.AFTER_START, 90, 1), + new BellRequest(BellType.AFTER_START, 120, 2)), 60, null, "발언자2") ) ); @@ -167,7 +184,7 @@ class UpdateUsedAt { @Test void 사용자_지정_토론_테이블의_사용_시각을_최신화한다() { Member member = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); LocalDateTime beforeUsedAt = table.getUsedAt(); customizeService.updateUsedAt(table.getId(), member); @@ -183,7 +200,7 @@ class UpdateUsedAt { void 회원_소유가_아닌_테이블_수정_시_예외를_발생시킨다() { Member chan = memberGenerator.generate("default@gmail.com"); Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); long chanTableId = chanTable.getId(); assertThatThrownBy(() -> customizeService.updateUsedAt(chanTableId, coli)) @@ -198,19 +215,21 @@ class DeleteTable { @Test void 사용자_지정_토론_테이블을_삭제한다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); - customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); + customizeTimeBoxEntityGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); + customizeTimeBoxEntityGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); customizeService.deleteTable(chanTable.getId(), chan); Optional foundTable = customizeTableRepository.findById(chanTable.getId()); List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( chanTable); + List bells = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxes); assertAll( () -> assertThat(foundTable).isEmpty(), - () -> assertThat(timeBoxes).isEmpty() + () -> assertThat(timeBoxes).isEmpty(), + () -> assertThat(bells).isEmpty() ); } @@ -218,7 +237,7 @@ class DeleteTable { void 회원_소유가_아닌_테이블_삭제_시_예외를_발생시킨다() { Member chan = memberGenerator.generate("default@gmail.com"); Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); long chanTableId = chanTable.getId(); assertThatThrownBy(() -> customizeService.deleteTable(chanTableId, coli)) diff --git a/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java b/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java deleted file mode 100644 index 1e0b58b7..00000000 --- a/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java +++ /dev/null @@ -1,243 +0,0 @@ -package com.debatetimer.service.customize; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -import com.debatetimer.domain.customize.CustomizeBoxType; -import com.debatetimer.domain.customize.Stance; -import com.debatetimer.domain.member.Member; -import com.debatetimer.dto.customize.request.BellRequest; -import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; -import com.debatetimer.dto.customize.request.CustomizeTableInfoCreateRequest; -import com.debatetimer.dto.customize.request.CustomizeTimeBoxCreateRequest; -import com.debatetimer.dto.customize.response.CustomizeTableResponse; -import com.debatetimer.entity.customize.BellEntity; -import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import com.debatetimer.service.BaseServiceTest; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -class CustomizeServiceV2Test extends BaseServiceTest { - - @Autowired - private CustomizeServiceV2 customizeService; - - @Nested - class Save { - - @Test - void 사용자_지정_토론_테이블을_생성한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableCreateRequest customizeTableCreateRequest = new CustomizeTableCreateRequest( - new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", - "반대", true, true), - List.of( - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") - ) - ); - - CustomizeTableResponse savedTableResponse = customizeService.save(customizeTableCreateRequest, chan); - CustomizeTableEntity foundTable = customizeTableRepository.getByIdAndMember(savedTableResponse.id(), chan); - List foundTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( - foundTable); - List foundBells = bellRepository.findAllByCustomizeTimeBoxIn(foundTimeBoxes); - - assertAll( - () -> assertThat(foundTable.getName()).isEqualTo(customizeTableCreateRequest.info().name()), - () -> assertThat(foundTimeBoxes).hasSize(customizeTableCreateRequest.table().size()), - () -> assertThat(foundBells).hasSize(3) - ); - } - } - - @Nested - class FindTable { - - @Test - void 사용자_지정_토론_테이블을_조회한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - CustomizeTimeBoxEntity customizeTimeBox = customizeTimeBoxGenerator.generate( - chanTable, CustomizeBoxType.NORMAL, 1); - customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); - bellGenerator.generate(customizeTimeBox, 1, 1); - bellGenerator.generate(customizeTimeBox, 1, 2); - - CustomizeTableResponse foundResponse = customizeService.findTable(chanTable.getId(), chan); - - assertAll( - () -> assertThat(foundResponse.id()).isEqualTo(chanTable.getId()), - () -> assertThat(foundResponse.table()).hasSize(2), - () -> assertThat(foundResponse.table().get(0).bell()).hasSize(2), - () -> assertThat(foundResponse.table().get(1).bell()).isNull() - ); - } - - @Test - void 회원_소유가_아닌_테이블_조회_시_예외를_발생시킨다() { - Member chan = memberGenerator.generate("default@gmail.com"); - Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - long chanTableId = chanTable.getId(); - - assertThatThrownBy(() -> customizeService.findTable(chanTableId, coli)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); - } - } - - @Nested - class UpdateTable { - - @Test - void 사용자_지정_토론_테이블을_수정한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - CustomizeTableCreateRequest renewTableRequest = new CustomizeTableCreateRequest( - new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", - "반대", true, true), - List.of( - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") - ) - ); - - customizeService.updateTable(renewTableRequest, chanTable.getId(), chan); - - CustomizeTableEntity updatedTable = customizeTableRepository.getByIdAndMember(chanTable.getId(), chan); - List updatedTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( - updatedTable); - List bells = bellRepository.findAllByCustomizeTimeBoxIn(updatedTimeBoxes); - - assertAll( - () -> assertThat(updatedTable.getId()).isEqualTo(chanTable.getId()), - () -> assertThat(updatedTable.getName()).isEqualTo(renewTableRequest.info().name()), - () -> assertThat(updatedTimeBoxes).hasSize(renewTableRequest.table().size()), - () -> assertThat(bells).hasSize(3) - ); - } - - @Test - void 회원_소유가_아닌_테이블_수정_시_예외를_발생시킨다() { - Member chan = memberGenerator.generate("default@gmail.com"); - Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - long chanTableId = chanTable.getId(); - CustomizeTableCreateRequest renewTableRequest = new CustomizeTableCreateRequest( - new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", - "반대", true, true), - List.of( - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") - ) - ); - - assertThatThrownBy(() -> customizeService.updateTable(renewTableRequest, chanTableId, coli)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); - } - - @Test - void 테이블_정보_수정을_동시에_요청할_때_동시에_처리하지_않는다() throws InterruptedException { - Member member = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - CustomizeTableCreateRequest request = new CustomizeTableCreateRequest( - new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", - "반대", true, true), - List.of( - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") - ) - ); - - runAtSameTime(2, () -> customizeService.updateTable(request, table.getId(), member)); - - assertThat(customizeTimeBoxRepository.findAllByCustomizeTable(table)).hasSize(2); - } - } - - @Nested - class UpdateUsedAt { - - @Test - void 사용자_지정_토론_테이블의_사용_시각을_최신화한다() { - Member member = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - LocalDateTime beforeUsedAt = table.getUsedAt(); - - customizeService.updateUsedAt(table.getId(), member); - - CustomizeTableEntity updatedTable = customizeTableRepository.getByIdAndMember(table.getId(), member); - assertAll( - () -> assertThat(updatedTable.getId()).isEqualTo(table.getId()), - () -> assertThat(updatedTable.getUsedAt()).isAfter(beforeUsedAt) - ); - } - - @Test - void 회원_소유가_아닌_테이블_수정_시_예외를_발생시킨다() { - Member chan = memberGenerator.generate("default@gmail.com"); - Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - long chanTableId = chanTable.getId(); - - assertThatThrownBy(() -> customizeService.updateUsedAt(chanTableId, coli)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); - } - } - - @Nested - class DeleteTable { - - @Test - void 사용자_지정_토론_테이블을_삭제한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); - customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); - - customizeService.deleteTable(chanTable.getId(), chan); - - Optional foundTable = customizeTableRepository.findById(chanTable.getId()); - List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( - chanTable); - List bells = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxes); - - assertAll( - () -> assertThat(foundTable).isEmpty(), - () -> assertThat(timeBoxes).isEmpty(), - () -> assertThat(bells).isEmpty() - ); - } - - @Test - void 회원_소유가_아닌_테이블_삭제_시_예외를_발생시킨다() { - Member chan = memberGenerator.generate("default@gmail.com"); - Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - long chanTableId = chanTable.getId(); - - assertThatThrownBy(() -> customizeService.deleteTable(chanTableId, coli)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); - } - } -} diff --git a/src/test/java/com/debatetimer/service/member/MemberServiceTest.java b/src/test/java/com/debatetimer/service/member/MemberServiceTest.java index d78c50e7..185476e9 100644 --- a/src/test/java/com/debatetimer/service/member/MemberServiceTest.java +++ b/src/test/java/com/debatetimer/service/member/MemberServiceTest.java @@ -52,8 +52,8 @@ class GetTables { @Test void 회원의_전체_토론_시간표를_조회한다() { Member member = memberGenerator.generate("default@gmail.com"); - customizeTableGenerator.generate(member); - customizeTableGenerator.generate(member); + customizeTableEntityGenerator.generate(member); + customizeTableEntityGenerator.generate(member); TableResponses response = memberService.getTables(member.getId()); @@ -63,7 +63,7 @@ class GetTables { @Test void 회원의_전체_토론_시간표는_정해진_순서대로_반환한다() throws InterruptedException { Member member = memberGenerator.generate("default@gmail.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); Thread.sleep(1); TableResponses response = memberService.getTables(member.getId()); diff --git a/src/test/java/com/debatetimer/service/poll/PollServiceTest.java b/src/test/java/com/debatetimer/service/poll/PollServiceTest.java index fd5c96c2..4dd3e913 100644 --- a/src/test/java/com/debatetimer/service/poll/PollServiceTest.java +++ b/src/test/java/com/debatetimer/service/poll/PollServiceTest.java @@ -27,7 +27,7 @@ class CreatePoll { @Test void 선거를_생성한다() { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); PollCreateResponse createdPoll = pollService.create(table.getId(), member); @@ -42,11 +42,11 @@ 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, "커찬"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteEntityGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); PollInfoResponse pollInfo = pollService.getPollInfo(table.getId(), member); @@ -68,8 +68,8 @@ class FinishPoll { @Test void 선거를_완료상태로_변경한다() { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - pollGenerator.generate(table, PollStatus.PROGRESS); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + pollEntityGenerator.generate(table, PollStatus.PROGRESS); PollInfoResponse pollInfo = pollService.finishPoll(table.getId(), member); diff --git a/src/test/java/com/debatetimer/service/poll/VoteServiceTest.java b/src/test/java/com/debatetimer/service/poll/VoteServiceTest.java new file mode 100644 index 00000000..e2f26ecb --- /dev/null +++ b/src/test/java/com/debatetimer/service/poll/VoteServiceTest.java @@ -0,0 +1,121 @@ +package com.debatetimer.service.poll; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +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.request.VoteRequest; +import com.debatetimer.dto.poll.response.VoteCreateResponse; +import com.debatetimer.dto.poll.response.VoterPollInfoResponse; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.repository.poll.VoteRepository; +import com.debatetimer.service.BaseServiceTest; +import java.util.UUID; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class VoteServiceTest extends BaseServiceTest { + + @Autowired + private VoteService voteService; + + @Autowired + private VoteRepository voteRepository; + + @Nested + class Vote { + + @Test + void 진행_중인_선거에_최초로_투표_할_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + String participateCode = UUID.randomUUID().toString(); + VoteRequest voteRequest = new VoteRequest("콜리", participateCode, VoteTeam.PROS); + + VoteCreateResponse response = voteService.vote(pollEntity.getId(), voteRequest); + + assertAll( + () -> assertThat(response.name()).isEqualTo(voteRequest.name()), + () -> assertThat(response.participateCode()).isEqualTo(voteRequest.participateCode()), + () -> assertThat(response.team()).isEqualTo(voteRequest.team()) + ); + } + + @Test + void 이미_참여한_선거에_투표_할_수_없다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + String participateCode = UUID.randomUUID().toString(); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리", participateCode); + VoteRequest voteRequest = new VoteRequest("콜리", participateCode, VoteTeam.PROS); + + assertThatThrownBy(() -> voteService.vote(pollEntity.getId(), voteRequest)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.ALREADY_VOTED_PARTICIPANT.getMessage()); + } + + @Test + void 투표_동시성_이슈에_단일_표만_유효하게_취급한다() throws InterruptedException { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + String participateCode = UUID.randomUUID().toString(); + VoteRequest voteRequest = new VoteRequest("콜리", participateCode, VoteTeam.PROS); + + runAtSameTime(2, () -> voteService.vote(pollEntity.getId(), voteRequest)); + + long voteCount = voteRepository.count(); + assertThat(voteCount).isEqualTo(1); + } + + @Test + void 끝난_선거에_투표_할_수_없다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity alreadyDonePoll = pollEntityGenerator.generate(table, PollStatus.DONE); + String participateCode = UUID.randomUUID().toString(); + VoteRequest voteRequest = new VoteRequest("콜리", participateCode, VoteTeam.PROS); + + assertThatThrownBy(() -> voteService.vote(alreadyDonePoll.getId(), voteRequest)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.ALREADY_DONE_POLL.getMessage()); + } + } + + @Nested + class GetVoterPollInfo { + + @Test + void 투표자가_선거정보를_조회할_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteEntityGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); + + VoterPollInfoResponse response = voteService.getVoterPollInfo(pollEntity.getId()); + + 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.participateCode()).isNotBlank(), + () -> assertThat(response.prosCount()).isEqualTo(2L), + () -> assertThat(response.consCount()).isEqualTo(1L) + ); + } + } +}