Skip to content

Commit

Permalink
merge: 투표 시 라운드 진행 검증 추가 #96
Browse files Browse the repository at this point in the history
  • Loading branch information
jhon3242 authored Aug 3, 2024
2 parents c5e057b + 290fd60 commit f7b89ee
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 9 deletions.
15 changes: 15 additions & 0 deletions backend/src/main/java/ddangkong/config/ClockConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ddangkong.config;

import java.time.Clock;
import java.time.ZoneId;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ClockConfig {

@Bean
public Clock clock() {
return Clock.system(ZoneId.of("Asia/Seoul"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ddangkong.domain.BaseEntity;
import ddangkong.domain.balance.content.BalanceContent;
import ddangkong.domain.balance.content.Category;
import ddangkong.exception.BadRequestException;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
Expand All @@ -11,6 +12,8 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import java.time.LocalDateTime;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -35,6 +38,19 @@ public class RoomContent extends BaseEntity {
@Column(nullable = false)
private int round;

private LocalDateTime roundEndedAt;

@Column(nullable = false)
private boolean isUsed;

public boolean isRoundOver(LocalDateTime currentTime) {
return currentTime.isAfter(getRoundEndedAt());
}

public boolean isNotSameContentId(Long contentId) {
return !Objects.equals(getContentId(), contentId);
}

public Long getContentId() {
return balanceContent.getId();
}
Expand All @@ -50,4 +66,11 @@ public String getContentName() {
public int getTotalRound() {
return room.getTotalRound();
}

public LocalDateTime getRoundEndedAt() {
if (roundEndedAt == null) {
throw new BadRequestException("라운드 종료 시간이 설정되지 않습니다.");
}
return roundEndedAt;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import ddangkong.domain.member.Member;
import ddangkong.domain.member.MemberRepository;
import ddangkong.exception.BadRequestException;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
Expand All @@ -41,8 +43,11 @@ public class BalanceVoteService {

private final RoomRepository roomRepository;

private final Clock clock;

@Transactional
public BalanceVoteResponse createBalanceVote(BalanceVoteRequest request, Long roomId, Long contentId) {
validateRoundEnded(roomId, contentId);
BalanceOption balanceOption = findValidOption(request.optionId(), contentId);
Member member = findValidMember(request.memberId(), roomId);

Expand All @@ -51,6 +56,19 @@ public BalanceVoteResponse createBalanceVote(BalanceVoteRequest request, Long ro
return new BalanceVoteResponse(savedBalanceVote);
}

private void validateRoundEnded(Long roomId, Long contentId) {
RoomContent roomContent = findValidRoomContent(roomId);
if (roomContent.isNotSameContentId(contentId) || roomContent.isRoundOver(LocalDateTime.now(clock))) {
throw new BadRequestException("유효하지 않은 라운드에는 투표할 수 없습니다.");
}
}

private RoomContent findValidRoomContent(Long roomId) {
Room room = roomRepository.getById(roomId);
return roomContentRepository.findByRoomAndRound(room, room.getCurrentRound())
.orElseThrow(() -> new BadRequestException("해당 방의 현재 진행중인 질문이 존재하지 않습니다."));
}

private BalanceOption findValidOption(Long optionId, Long contentId) {
return balanceOptionRepository.findByIdAndBalanceContentId(optionId, contentId)
.orElseThrow(() -> new BadRequestException("해당 질문의 선택지가 존재하지 않습니다."));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
import ddangkong.controller.BaseControllerTest;
import ddangkong.controller.balance.vote.dto.BalanceVoteRequest;
import ddangkong.controller.balance.vote.dto.BalanceVoteResponse;
import ddangkong.support.config.TestClockConfig;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;

@Import(TestClockConfig.class)
class BalanceVoteControllerTest extends BaseControllerTest {

@Nested
Expand All @@ -21,7 +24,7 @@ class 투표_생성 {
private static final BalanceVoteResponse EXPECTED_RESPONSE = new BalanceVoteResponse(1L);

@Test
void 현재_방의_질문을_조회할__있다() {
void 현재_방에서_투표할__있다() {
// given & when
BalanceVoteResponse actual = RestAssured.given().log().all()
.body(NORMAL_REQUEST).contentType(ContentType.JSON)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
import ddangkong.controller.balance.vote.dto.BalanceVoteResultResponse;
import ddangkong.exception.BadRequestException;
import ddangkong.service.BaseServiceTest;
import ddangkong.support.config.TestClockConfig;
import java.util.List;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;

@Import(TestClockConfig.class)
class BalanceVoteServiceTest extends BaseServiceTest {

@Autowired
Expand Down Expand Up @@ -45,8 +48,8 @@ class 투표_생성 {
@Test
void 질문에_해당하는_선택지가_아닌_경우_예외를_던진다() {
// given
Long optionId = 1L;
Long contentId = 2L;
Long optionId = 3L;
Long contentId = 1L;
Long memberId = 1L;
Long roomId = 1L;

Expand All @@ -63,14 +66,44 @@ class 투표_생성 {
Long optionId = 1L;
Long contentId = 1L;
Long memberId = 1L;
Long roomId = 2L;
Long roomId = 3L;

// when & then
assertThatThrownBy(() -> balanceVoteService.createBalanceVote(
new BalanceVoteRequest(memberId, optionId), roomId, contentId))
.isInstanceOf(BadRequestException.class)
.hasMessage("해당 방의 멤버가 존재하지 않습니다.");
}

@Test
void 투표_시간이_지난_이후_투표__예외를_던진다() {
// given
Long optionId = 3L;
Long contentId = 2L;
Long memberId = 1L;
Long roomId = 1L;

// when & then
assertThatThrownBy(() -> balanceVoteService.createBalanceVote(
new BalanceVoteRequest(memberId, optionId), roomId, contentId))
.isInstanceOf(BadRequestException.class)
.hasMessage("유효하지 않은 라운드에는 투표할 수 없습니다.");
}

@Test
void 아직_진행하지_않은_컨텐츠에_투표__예외를_던진다() {
// given
Long optionId = 5L;
Long contentId = 3L;
Long memberId = 1L;
Long roomId = 1L;

// when & then
assertThatThrownBy(() -> balanceVoteService.createBalanceVote(
new BalanceVoteRequest(memberId, optionId), roomId, contentId))
.isInstanceOf(BadRequestException.class)
.hasMessage("유효하지 않은 라운드에는 투표할 수 없습니다.");
}
}

@Nested
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ddangkong.support.config;

import java.time.Clock;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;

@TestConfiguration
public class TestClockConfig {

@Primary
@Bean
public Clock testClock() {
String dateTimeString = "2024-07-18 20:00:02.000";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
LocalDateTime customDateTime = LocalDateTime.parse(dateTimeString, formatter);
ZonedDateTime customZonedDateTime = customDateTime.atZone(ZoneId.of("Asia/Seoul"));
Instant customInstant = customZonedDateTime.toInstant();
return Clock.fixed(customInstant, ZoneId.of("Asia/Seoul"));
}
}
10 changes: 5 additions & 5 deletions backend/src/test/resources/init-test.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ VALUES ('EXAMPLE', '민초 vs 반민초'),
('EXAMPLE', '월 200 백수 vs 월 500 직장인'),
('EXAMPLE', '다음 중 여행가고 싶은 곳은?');

INSERT INTO room_content (room_id, balance_content_id, round, created_at)
VALUES (1, 2, 1, '2024-07-18 19:50:00.000'),
(1, 1, 2, '2024-07-18 20:00:00.000'),
(1, 3, 3, '2024-07-18 20:00:00.000'),
(3, 1, 1, '2024-07-18 19:51:00.000');
INSERT INTO room_content (room_id, balance_content_id, round, created_at, round_ended_at, is_used)
VALUES (1, 2, 1, '2024-07-18 19:50:00.000', '2024-07-18 19:50:32.000', false),
(1, 1, 2, '2024-07-18 19:50:00.000', '2024-07-18 20:00:32.000', false),
(1, 3, 3, '2024-07-18 19:50:00.000', null, false),
(3, 1, 1, '2024-07-18 20:00:00.000', '2024-07-18 20:00:32.000', false);

INSERT INTO balance_option (name, balance_content_id)
VALUES ('민초', 1),
Expand Down

0 comments on commit f7b89ee

Please sign in to comment.