diff --git a/src/main/java/com/debatetimer/config/sharing/WebSocketAuthMemberResolver.java b/src/main/java/com/debatetimer/config/sharing/WebSocketAuthMemberResolver.java index de16ac9f..19c76364 100644 --- a/src/main/java/com/debatetimer/config/sharing/WebSocketAuthMemberResolver.java +++ b/src/main/java/com/debatetimer/config/sharing/WebSocketAuthMemberResolver.java @@ -34,7 +34,7 @@ public Object resolveArgument(MethodParameter parameter, Message message) { throw new DTClientErrorException(ClientErrorCode.UNAUTHORIZED_MEMBER); } - String email = authManager.resolveAccessToken(token); + String email = authManager.resolveChairmanToken(token); return authService.getMember(email); } } diff --git a/src/main/java/com/debatetimer/config/sharing/WebSocketConfig.java b/src/main/java/com/debatetimer/config/sharing/WebSocketConfig.java index b2ee31a6..aeb9c819 100644 --- a/src/main/java/com/debatetimer/config/sharing/WebSocketConfig.java +++ b/src/main/java/com/debatetimer/config/sharing/WebSocketConfig.java @@ -1,11 +1,14 @@ package com.debatetimer.config.sharing; import com.debatetimer.config.CorsProperties; +import java.time.Duration; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @@ -15,6 +18,12 @@ @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + private static final long SERVER_TO_CLIENT_HEARTBEAT_DURATION = Duration.ofSeconds(10).toMillis(); + private static final long CLIENT_TO_SERVER_HEARTBEAT_DURATION = Duration.ofSeconds(10).toMillis(); + private static final long SOCKJS_HEART_BEAT_DURATION = Duration.ofSeconds(10).toMillis(); + private static final String HEART_BEAT_THREAD_PREFIX = "wss-heartbeat-"; + private static final int HEART_BEAT_THREAD_COUNT = 1; + private final CorsProperties corsProperties; private final WebSocketAuthMemberResolver webSocketAuthMemberResolver; @@ -25,7 +34,9 @@ public void addArgumentResolvers(List resolvers) @Override public void configureMessageBroker(MessageBrokerRegistry registry) { - registry.enableSimpleBroker("/room", "/chairman"); + registry.enableSimpleBroker("/room", "/chairman") + .setHeartbeatValue(new long[]{SERVER_TO_CLIENT_HEARTBEAT_DURATION, CLIENT_TO_SERVER_HEARTBEAT_DURATION}) + .setTaskScheduler(heartBeatScheduler()); registry.setApplicationDestinationPrefixes("/app"); } @@ -33,6 +44,16 @@ public void configureMessageBroker(MessageBrokerRegistry registry) { public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") .setAllowedOriginPatterns(corsProperties.getCorsOrigin()) - .withSockJS(); + .withSockJS() + .setHeartbeatTime(SOCKJS_HEART_BEAT_DURATION); + } + + @Bean + public ThreadPoolTaskScheduler heartBeatScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(HEART_BEAT_THREAD_COUNT); + scheduler.setThreadNamePrefix(HEART_BEAT_THREAD_PREFIX); + scheduler.initialize(); + return scheduler; } } diff --git a/src/main/java/com/debatetimer/controller/sharing/SharingRestController.java b/src/main/java/com/debatetimer/controller/sharing/SharingRestController.java new file mode 100644 index 00000000..6c99c3ba --- /dev/null +++ b/src/main/java/com/debatetimer/controller/sharing/SharingRestController.java @@ -0,0 +1,29 @@ +package com.debatetimer.controller.sharing; + +import com.debatetimer.controller.auth.AuthMember; +import com.debatetimer.controller.tool.jwt.AuthManager; +import com.debatetimer.domain.member.Member; +import com.debatetimer.dto.sharing.response.ChairmanTokenResponse; +import com.debatetimer.service.customize.CustomizeService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class SharingRestController { + + private final CustomizeService customizeService; + private final AuthManager authManager; + + @GetMapping("/api/share/{tableId}/chairman-token") + public ChairmanTokenResponse issueChairmanToken( + @AuthMember Member member, + @PathVariable("tableId") long tableId + ) { + long debateTime = customizeService.findDebateTime(tableId, member); + String chairmanToken = authManager.issueChairmanToken(member, debateTime * 2); + return new ChairmanTokenResponse(chairmanToken); + } +} diff --git a/src/main/java/com/debatetimer/controller/sharing/SharingController.java b/src/main/java/com/debatetimer/controller/sharing/SharingWebSocketController.java similarity index 84% rename from src/main/java/com/debatetimer/controller/sharing/SharingController.java rename to src/main/java/com/debatetimer/controller/sharing/SharingWebSocketController.java index 0216a15b..92ca689d 100644 --- a/src/main/java/com/debatetimer/controller/sharing/SharingController.java +++ b/src/main/java/com/debatetimer/controller/sharing/SharingWebSocketController.java @@ -1,5 +1,7 @@ package com.debatetimer.controller.sharing; +import com.debatetimer.controller.auth.AuthMember; +import com.debatetimer.domain.member.Member; import com.debatetimer.dto.sharing.request.SharingRequest; import com.debatetimer.dto.sharing.response.SharingResponse; import com.debatetimer.service.sharing.SharingService; @@ -13,17 +15,17 @@ @Controller @RequiredArgsConstructor -public class SharingController { +public class SharingWebSocketController { private final SharingService sharingService; @MessageMapping("/event/{roomId}") @SendTo("/room/{roomId}") public SharingResponse share( + @AuthMember Member member, @DestinationVariable(value = "roomId") long roomId, @Valid @Payload SharingRequest request ) { - return sharingService.share(request); } } diff --git a/src/main/java/com/debatetimer/controller/tool/jwt/AuthManager.java b/src/main/java/com/debatetimer/controller/tool/jwt/AuthManager.java index a614224f..13ae9c42 100644 --- a/src/main/java/com/debatetimer/controller/tool/jwt/AuthManager.java +++ b/src/main/java/com/debatetimer/controller/tool/jwt/AuthManager.java @@ -1,5 +1,6 @@ package com.debatetimer.controller.tool.jwt; +import com.debatetimer.domain.member.Member; import com.debatetimer.dto.member.JwtTokenResponse; import com.debatetimer.dto.member.MemberInfo; import java.time.Duration; @@ -20,6 +21,11 @@ public JwtTokenResponse issueToken(MemberInfo memberInfo) { return new JwtTokenResponse(accessToken, refreshToken, refreshTokenExpiration); } + public String issueChairmanToken(Member member, long expiration) { + MemberInfo memberInfo = new MemberInfo(member.getEmail()); + return jwtTokenProvider.createChairmanToken(memberInfo, expiration); + } + public JwtTokenResponse reissueToken(String refreshToken) { String email = jwtTokenResolver.resolveRefreshToken(refreshToken); MemberInfo memberInfo = new MemberInfo(email); @@ -37,4 +43,8 @@ public String resolveAccessToken(String accessToken) { public String resolveRefreshToken(String refreshToken) { return jwtTokenResolver.resolveRefreshToken(refreshToken); } + + public String resolveChairmanToken(String chairmanToken) { + return jwtTokenResolver.resolveChairmanToken(chairmanToken); + } } diff --git a/src/main/java/com/debatetimer/controller/tool/jwt/JwtTokenProvider.java b/src/main/java/com/debatetimer/controller/tool/jwt/JwtTokenProvider.java index ffd28bca..9f3ef75b 100644 --- a/src/main/java/com/debatetimer/controller/tool/jwt/JwtTokenProvider.java +++ b/src/main/java/com/debatetimer/controller/tool/jwt/JwtTokenProvider.java @@ -23,6 +23,10 @@ public String createRefreshToken(MemberInfo memberInfo) { return createToken(memberInfo, refreshTokenExpiration, TokenType.REFRESH_TOKEN); } + public String createChairmanToken(MemberInfo memberInfo, long expirationSeconds) { + return createToken(memberInfo, Duration.ofSeconds(expirationSeconds), TokenType.CHAIRMAN_TOKEN); + } + private String createToken(MemberInfo memberInfo, Duration expiration, TokenType tokenType) { Date now = new Date(); Date expiredDate = new Date(now.getTime() + expiration.toMillis()); diff --git a/src/main/java/com/debatetimer/controller/tool/jwt/JwtTokenResolver.java b/src/main/java/com/debatetimer/controller/tool/jwt/JwtTokenResolver.java index de5d33f7..15e0d186 100644 --- a/src/main/java/com/debatetimer/controller/tool/jwt/JwtTokenResolver.java +++ b/src/main/java/com/debatetimer/controller/tool/jwt/JwtTokenResolver.java @@ -23,6 +23,10 @@ public String resolveRefreshToken(String refreshToken) { return resolveToken(refreshToken, TokenType.REFRESH_TOKEN); } + public String resolveChairmanToken(String chairmanToken) { + return resolveToken(chairmanToken, TokenType.CHAIRMAN_TOKEN); + } + private String resolveToken(String token, TokenType tokenType) { try { Claims claims = Jwts.parserBuilder() diff --git a/src/main/java/com/debatetimer/controller/tool/jwt/TokenType.java b/src/main/java/com/debatetimer/controller/tool/jwt/TokenType.java index 1112e4b9..9d3a4d0c 100644 --- a/src/main/java/com/debatetimer/controller/tool/jwt/TokenType.java +++ b/src/main/java/com/debatetimer/controller/tool/jwt/TokenType.java @@ -3,5 +3,6 @@ public enum TokenType { ACCESS_TOKEN, - REFRESH_TOKEN + REFRESH_TOKEN, + CHAIRMAN_TOKEN } diff --git a/src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java index 2c2e96e7..0132389d 100644 --- a/src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java +++ b/src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java @@ -61,6 +61,11 @@ public List getCustomizeTimeBoxes(long tableId, Member member) return toCustomizeTimeBoxes(timeBoxEntityList, bellEntityList); } + @Transactional(readOnly = true) + public long getTotalTimeBoxTimes(long tableId) { + return timeBoxRepository.sumTimeByTableId(tableId); + } + private List toCustomizeTimeBoxes( List timeBoxEntities, List bellEntities diff --git a/src/main/java/com/debatetimer/dto/sharing/response/ChairmanTokenResponse.java b/src/main/java/com/debatetimer/dto/sharing/response/ChairmanTokenResponse.java new file mode 100644 index 00000000..c10fc79e --- /dev/null +++ b/src/main/java/com/debatetimer/dto/sharing/response/ChairmanTokenResponse.java @@ -0,0 +1,7 @@ +package com.debatetimer.dto.sharing.response; + +public record ChairmanTokenResponse( + String chairmanToken +) { + +} diff --git a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java index 1a0e524d..f7059b1b 100644 --- a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java @@ -13,6 +13,9 @@ public interface CustomizeTimeBoxRepository extends Repository findAllByCustomizeTable(CustomizeTableEntity table); + @Query("SELECT COALESCE(SUM(ctb.time), 0) FROM CustomizeTimeBoxEntity ctb WHERE ctb.customizeTable.id = :tableId") + long sumTimeByTableId(long tableId); + @Query("DELETE FROM CustomizeTimeBoxEntity ctb WHERE ctb.customizeTable.id = :tableId") @Modifying(clearAutomatically = true, flushAutomatically = true) void deleteAllByTable(long tableId); diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeService.java b/src/main/java/com/debatetimer/service/customize/CustomizeService.java index 1670036f..d143b908 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeService.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeService.java @@ -32,6 +32,12 @@ public CustomizeTableResponse findTable(long tableId, Member member) { return new CustomizeTableResponse(table, timeBoxes); } + @Transactional(readOnly = true) + public long findDebateTime(long tableId, Member member) { + CustomizeTable customizeTable = customizeTableDomainRepository.getByIdAndMember(tableId, member); + return customizeTableDomainRepository.getTotalTimeBoxTimes(customizeTable.getId()); + } + @Transactional public CustomizeTableResponse updateTable( CustomizeTableCreateRequest tableCreateRequest, diff --git a/src/main/java/com/debatetimer/service/sharing/SharingService.java b/src/main/java/com/debatetimer/service/sharing/SharingService.java index 8002052f..863981af 100644 --- a/src/main/java/com/debatetimer/service/sharing/SharingService.java +++ b/src/main/java/com/debatetimer/service/sharing/SharingService.java @@ -5,9 +5,11 @@ import com.debatetimer.dto.sharing.response.SharingResponse; import com.debatetimer.dto.sharing.response.TimerEventDataResponse; import java.util.Optional; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service +@RequiredArgsConstructor public class SharingService { public SharingResponse share(SharingRequest request) { diff --git a/src/test/java/com/debatetimer/controller/BaseDocumentTest.java b/src/test/java/com/debatetimer/controller/BaseDocumentTest.java index bbac2b81..3c139034 100644 --- a/src/test/java/com/debatetimer/controller/BaseDocumentTest.java +++ b/src/test/java/com/debatetimer/controller/BaseDocumentTest.java @@ -15,6 +15,7 @@ import com.debatetimer.service.organization.OrganizationService; import com.debatetimer.service.poll.PollService; import com.debatetimer.service.poll.VoteService; +import com.debatetimer.service.sharing.SharingService; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.filter.log.RequestLoggingFilter; @@ -71,6 +72,9 @@ public abstract class BaseDocumentTest { @MockitoBean protected VoteService voteService; + @MockitoBean + protected SharingService sharingService; + @MockitoBean protected OrganizationService organizationService; diff --git a/src/test/java/com/debatetimer/controller/Tag.java b/src/test/java/com/debatetimer/controller/Tag.java index 5da09bb2..12ac3339 100644 --- a/src/test/java/com/debatetimer/controller/Tag.java +++ b/src/test/java/com/debatetimer/controller/Tag.java @@ -6,6 +6,7 @@ public enum Tag { PARLIAMENTARY_API("Parliamentary Table API"), TIME_BASED_API("Time Based Table API"), CUSTOMIZE_API("Customize Table API"), + SHARING_API("Sharing API"), POLL_API("Poll API"), ORGANIZATION_API("Organization API"); diff --git a/src/test/java/com/debatetimer/controller/sharing/SharingDocumentTest.java b/src/test/java/com/debatetimer/controller/sharing/SharingDocumentTest.java new file mode 100644 index 00000000..5c75e706 --- /dev/null +++ b/src/test/java/com/debatetimer/controller/sharing/SharingDocumentTest.java @@ -0,0 +1,59 @@ +package com.debatetimer.controller.sharing; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; + +import com.debatetimer.controller.BaseDocumentTest; +import com.debatetimer.controller.RestDocumentationRequest; +import com.debatetimer.controller.RestDocumentationResponse; +import com.debatetimer.controller.Tag; +import com.debatetimer.domain.member.Member; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; + +public class SharingDocumentTest extends BaseDocumentTest { + + @Nested + class IssueChairmanToken { + + private final RestDocumentationRequest requestDocument = request() + .tag(Tag.SHARING_API) + .summary("사회자용 토큰 발급") + .requestHeader(headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰")) + .pathParameter(parameterWithName("tableId").description("테이블 id")); + + private final RestDocumentationResponse responseDocument = response() + .responseBodyField( + fieldWithPath("chairmanToken").type(STRING).description("사회자용 토큰") + ); + + @Test + void 사회자_용_토큰_생성_성공() { + long requestTableId = 1L; + long debateTime = 500L; + doReturn(debateTime).when(customizeService) + .findDebateTime(eq(requestTableId), any(Member.class)); + doReturn("testToken").when(authManager) + .issueChairmanToken(any(Member.class), eq(debateTime * 2)); + + var document = document("sharing/get", 200) + .request(requestDocument) + .response(responseDocument) + .build(); + + given(document) + .contentType(ContentType.JSON) + .headers(EXIST_MEMBER_HEADER) + .pathParam("tableId", String.valueOf(requestTableId)) + .when().get("/api/share/{tableId}/chairman-token") + .then().statusCode(200); + } + } +} diff --git a/src/test/java/com/debatetimer/controller/sharing/SharingControllerTest.java b/src/test/java/com/debatetimer/controller/sharing/SharingWebSocketControllerTest.java similarity index 76% rename from src/test/java/com/debatetimer/controller/sharing/SharingControllerTest.java rename to src/test/java/com/debatetimer/controller/sharing/SharingWebSocketControllerTest.java index d644f907..88775492 100644 --- a/src/test/java/com/debatetimer/controller/sharing/SharingControllerTest.java +++ b/src/test/java/com/debatetimer/controller/sharing/SharingWebSocketControllerTest.java @@ -21,7 +21,7 @@ import org.junit.jupiter.params.provider.NullSource; import org.springframework.messaging.simp.stomp.StompHeaders; -class SharingControllerTest extends BaseStompTest { +class SharingWebSocketControllerTest extends BaseStompTest { @Nested class Share { @@ -31,7 +31,7 @@ class Share { long roomId = 1L; MessageFrameHandler handler = new MessageFrameHandler<>(SharingResponse.class); Member member = memberGenerator.generate("example@email.com"); - StompHeaders headers = headerGenerator.generateAccessTokenHeader("/app/event/" + roomId, member); + StompHeaders headers = headerGenerator.generateChairmanTokenHeader("/app/event/" + roomId, member); SharingRequest request = new SharingRequest( TimerEventType.NEXT, new TimerEventInfoRequest( @@ -58,12 +58,34 @@ class Share { ); } + @Test + void 사회자가_아니면_이벤트를_발행할_수_없다() throws ExecutionException, InterruptedException, TimeoutException { + long roomId = 1L; + MessageFrameHandler handler = new MessageFrameHandler<>(SharingResponse.class); + Member member = memberGenerator.generate("example@email.com"); + SharingRequest request = new SharingRequest( + TimerEventType.NEXT, + new TimerEventInfoRequest( + CustomizeBoxType.NORMAL, + null, + 2, + 30 + ) + ); + stompSession.subscribe("/room/" + roomId, handler); //청중의 구독 + stompSession.send("/app/event/" + roomId, request); //사회자의 이벤트 발생 + + assertThatThrownBy(() -> handler.getCompletableFuture() + .get(2L, TimeUnit.SECONDS)) + .isInstanceOf(TimeoutException.class); + } + @Test void 사회자가_발생시킨_토론_종료_이벤트를_청중이_공유받는다() throws ExecutionException, InterruptedException, TimeoutException { long roomId = 1L; MessageFrameHandler handler = new MessageFrameHandler<>(SharingResponse.class); Member member = memberGenerator.generate("example@email.com"); - StompHeaders headers = headerGenerator.generateAccessTokenHeader("/app/event/" + roomId, member); + StompHeaders headers = headerGenerator.generateChairmanTokenHeader("/app/event/" + roomId, member); SharingRequest request = new SharingRequest(TimerEventType.FINISHED, null); stompSession.subscribe("/room/" + roomId, handler); //청중의 구독 @@ -84,7 +106,7 @@ class Share { long roomId = 1L; MessageFrameHandler handler = new MessageFrameHandler<>(SharingResponse.class); Member member = memberGenerator.generate("example@email.com"); - StompHeaders headers = headerGenerator.generateAccessTokenHeader("/app/event/" + roomId, member); + StompHeaders headers = headerGenerator.generateChairmanTokenHeader("/app/event/" + roomId, member); SharingRequest request = new SharingRequest( TimerEventType.NEXT, new TimerEventInfoRequest( diff --git a/src/test/java/com/debatetimer/fixture/HeaderGenerator.java b/src/test/java/com/debatetimer/fixture/HeaderGenerator.java index 70362980..5a76eaac 100644 --- a/src/test/java/com/debatetimer/fixture/HeaderGenerator.java +++ b/src/test/java/com/debatetimer/fixture/HeaderGenerator.java @@ -23,11 +23,11 @@ public Headers generateAccessTokenHeader(Member member) { return new Headers(new Header(HttpHeaders.AUTHORIZATION, accessToken)); } - public StompHeaders generateAccessTokenHeader(String destination, Member member) { - String accessToken = jwtTokenProvider.createAccessToken(new MemberInfo(member)); + public StompHeaders generateChairmanTokenHeader(String destination, Member member) { + String chairmanToken = jwtTokenProvider.createChairmanToken(new MemberInfo(member), 5L); StompHeaders stompHeaders = new StompHeaders(); stompHeaders.setDestination(destination); - stompHeaders.add(HttpHeaders.AUTHORIZATION, accessToken); + stompHeaders.add(HttpHeaders.AUTHORIZATION, chairmanToken); return stompHeaders; } } diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java index 5fcf6b39..93268c2d 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java @@ -80,4 +80,22 @@ class DeleteAllByTable { .doesNotThrowAnyException(); } } + + @Nested + class SumTimeByTableId { + + @Test + void 특정_테이블의_타임_박스시간의_합을_반환한다() { + Member chan = memberGenerator.generate("default@gmail.com"); + CustomizeTableEntity debateTable = customizeTableEntityGenerator.generate(chan); + CustomizeTimeBoxEntity timeBox1 = customizeTimeBoxEntityGenerator.generate(debateTable, + CustomizeBoxType.NORMAL, 1, 10); + CustomizeTimeBoxEntity timeBox2 = customizeTimeBoxEntityGenerator.generate(debateTable, + CustomizeBoxType.NORMAL, 2, 20); + + long summedTimeByTableId = customizeTimeBoxRepository.sumTimeByTableId(debateTable.getId()); + + assertThat(summedTimeByTableId).isEqualTo(timeBox1.getTime() + timeBox2.getTime()); + } + } } diff --git a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java index 664a50c5..3013abeb 100644 --- a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java +++ b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java @@ -99,6 +99,34 @@ class FindTable { } } + @Nested + class FindDebateTime { + + @Test + void 사용자_지정_토론_테이블의_총_토론_시간을_조회한다() { + Member chan = memberGenerator.generate("default@gmail.com"); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); + customizeTimeBoxEntityGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1, 120); + customizeTimeBoxEntityGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2, 180); + + long debateTime = customizeService.findDebateTime(chanTable.getId(), chan); + + assertThat(debateTime).isEqualTo(300); + } + + @Test + void 회원_소유가_아닌_테이블_조회_시_예외를_발생시킨다() { + Member chan = memberGenerator.generate("default@gmail.com"); + Member coli = memberGenerator.generate("default2@gmail.com"); + CustomizeTableEntity chanTable = customizeTableEntityGenerator.generate(chan); + long chanTableId = chanTable.getId(); + + assertThatThrownBy(() -> customizeService.findDebateTime(chanTableId, coli)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); + } + } + @Nested class UpdateTable {