From 2ae1eaa0ad0fa5d518878a842e8ac06f97ee85fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B1=B4=EC=9A=B0?= Date: Tue, 13 May 2025 15:33:10 +0900 Subject: [PATCH 01/27] =?UTF-8?q?[CHORE]=20HttpClientException=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aop/logging/ClientLoggingAspect.java | 41 ++++++--- .../aop/logging/ControllerLoggingAspect.java | 29 +----- .../aop/logging/LoggingAspect.java | 24 ++++- .../debatetimer/client/oauth/OAuthClient.java | 7 +- .../exception/OAuthErrorResponse.java | 12 +++ .../custom/DTOAuthClientException.java | 15 ++++ .../exception/errorcode/ClientErrorCode.java | 2 + .../exception/errorcode/OAuthErrorCode.java | 26 ++++++ .../exception/errorcode/ServerErrorCode.java | 3 +- .../handler/GlobalExceptionHandler.java | 7 ++ .../handler/OAuthClientErrorHandler.java | 32 +++++++ .../client/oauth/OAuthClientTest.java | 90 +++++++++++++++++++ 12 files changed, 243 insertions(+), 45 deletions(-) create mode 100644 src/main/java/com/debatetimer/exception/OAuthErrorResponse.java create mode 100644 src/main/java/com/debatetimer/exception/custom/DTOAuthClientException.java create mode 100644 src/main/java/com/debatetimer/exception/errorcode/OAuthErrorCode.java create mode 100644 src/main/java/com/debatetimer/exception/handler/OAuthClientErrorHandler.java create mode 100644 src/test/java/com/debatetimer/client/oauth/OAuthClientTest.java diff --git a/src/main/java/com/debatetimer/aop/logging/ClientLoggingAspect.java b/src/main/java/com/debatetimer/aop/logging/ClientLoggingAspect.java index ca5cb7c2..5bf587c8 100644 --- a/src/main/java/com/debatetimer/aop/logging/ClientLoggingAspect.java +++ b/src/main/java/com/debatetimer/aop/logging/ClientLoggingAspect.java @@ -1,5 +1,7 @@ package com.debatetimer.aop.logging; +import com.debatetimer.exception.custom.DTOAuthClientException; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; @@ -12,7 +14,6 @@ @Component public class ClientLoggingAspect extends LoggingAspect { - private static final String CLIENT_REQUEST_TIME_KEY = "clientRequestTime"; @Pointcut("@within(com.debatetimer.aop.logging.LoggingClient)") public void loggingClients() { @@ -20,14 +21,16 @@ public void loggingClients() { @Around("loggingClients()") public Object loggingControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - setMdc(CLIENT_REQUEST_TIME_KEY, System.currentTimeMillis()); - logClientRequest(proceedingJoinPoint); - - Object responseBody = proceedingJoinPoint.proceed(); - - logClientResponse(proceedingJoinPoint); - removeMdc(CLIENT_REQUEST_TIME_KEY); - return responseBody; + try { + logClientRequest(proceedingJoinPoint); + Object responseBody = proceedingJoinPoint.proceed(); + logClientResponse(proceedingJoinPoint); + return responseBody; + } catch (DTOAuthClientException exception) { + logClientErrorRequest(proceedingJoinPoint); + logClientErrorResponse(exception.getErrorResponse()); + throw exception; + } } private void logClientRequest(ProceedingJoinPoint joinPoint) { @@ -39,9 +42,23 @@ private void logClientRequest(ProceedingJoinPoint joinPoint) { private void logClientResponse(ProceedingJoinPoint joinPoint) { String clientName = joinPoint.getSignature().getDeclaringType().getSimpleName(); String methodName = joinPoint.getSignature().getName(); - long latency = getLatency(CLIENT_REQUEST_TIME_KEY); - log.info("Client Response Logging - Client Name: {} | MethodName: {} | Latency: {}ms", - clientName, methodName, latency); + log.info("Client Response Logging - Client Name: {} | MethodName: {}", clientName, methodName); + } + + private void logClientErrorRequest(ProceedingJoinPoint proceedingJoinPoint) { + HttpServletRequest request = getHttpServletRequest(); + String requestParameters = getRequestParameters(proceedingJoinPoint); + String uri = request.getRequestURI(); + String httpMethod = request.getMethod(); + log.info("Client Request Error Logging: {} {} parameters - {}", httpMethod, uri, requestParameters); + } + + private void logClientErrorResponse(String responseBody) { + HttpServletRequest request = getHttpServletRequest(); + String uri = request.getRequestURI(); + String httpMethod = request.getMethod(); + log.info("Client Response Error Logging - Client Name: {} | MethodName: {} | response_body: {}", + httpMethod, uri, responseBody); } } diff --git a/src/main/java/com/debatetimer/aop/logging/ControllerLoggingAspect.java b/src/main/java/com/debatetimer/aop/logging/ControllerLoggingAspect.java index c4645ee5..8c591488 100644 --- a/src/main/java/com/debatetimer/aop/logging/ControllerLoggingAspect.java +++ b/src/main/java/com/debatetimer/aop/logging/ControllerLoggingAspect.java @@ -2,19 +2,13 @@ import jakarta.servlet.http.HttpServletRequest; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.CodeSignature; import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; @Slf4j @Aspect @@ -22,7 +16,6 @@ public class ControllerLoggingAspect extends LoggingAspect { private static final String REQUEST_ID_KEY = "requestId"; - private static final String START_TIME_KEY = "startTime"; @Pointcut("@within(org.springframework.web.bind.annotation.RestController)") public void allController() { @@ -31,13 +24,12 @@ public void allController() { @Around("allController()") public Object loggingControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { setMdc(REQUEST_ID_KEY, UUID.randomUUID().toString()); - setMdc(START_TIME_KEY, System.currentTimeMillis()); logControllerRequest(proceedingJoinPoint); Object responseBody = proceedingJoinPoint.proceed(); logControllerResponse(responseBody); - removeMdc(START_TIME_KEY); + removeMdc(REQUEST_ID_KEY); return responseBody; } @@ -53,23 +45,6 @@ private void logControllerResponse(Object responseBody) { HttpServletRequest request = getHttpServletRequest(); String uri = request.getRequestURI(); String httpMethod = request.getMethod(); - long latency = getLatency(START_TIME_KEY); - log.info("Response Logging: {} {} Body: {} latency - {}ms", httpMethod, uri, responseBody, latency); - } - - private HttpServletRequest getHttpServletRequest() { - ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); - return requestAttributes.getRequest(); - } - - private String getRequestParameters(JoinPoint joinPoint) { - CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature(); - String[] parameterNames = codeSignature.getParameterNames(); - Object[] args = joinPoint.getArgs(); - Map params = new HashMap<>(); - for (int i = 0; i < parameterNames.length; i++) { - params.put(parameterNames[i], args[i]); - } - return params.toString(); + log.info("Response Logging: {} {} Body: {}", httpMethod, uri, responseBody); } } diff --git a/src/main/java/com/debatetimer/aop/logging/LoggingAspect.java b/src/main/java/com/debatetimer/aop/logging/LoggingAspect.java index 07c2c7d3..45af339f 100644 --- a/src/main/java/com/debatetimer/aop/logging/LoggingAspect.java +++ b/src/main/java/com/debatetimer/aop/logging/LoggingAspect.java @@ -1,7 +1,14 @@ package com.debatetimer.aop.logging; +import jakarta.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.reflect.CodeSignature; import org.slf4j.MDC; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; @Slf4j public abstract class LoggingAspect { @@ -14,8 +21,19 @@ protected final void removeMdc(String key) { MDC.remove(key); } - protected final long getLatency(String startTimeKey) { - long startTime = Long.parseLong(MDC.get(startTimeKey)); - return System.currentTimeMillis() - startTime; + protected HttpServletRequest getHttpServletRequest() { + ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + return requestAttributes.getRequest(); + } + + protected String getRequestParameters(JoinPoint joinPoint) { + CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature(); + String[] parameterNames = codeSignature.getParameterNames(); + Object[] args = joinPoint.getArgs(); + Map params = new HashMap<>(); + for (int i = 0; i < parameterNames.length; i++) { + params.put(parameterNames[i], args[i]); + } + return params.toString(); } } diff --git a/src/main/java/com/debatetimer/client/oauth/OAuthClient.java b/src/main/java/com/debatetimer/client/oauth/OAuthClient.java index 3e041f3e..ed10dcf4 100644 --- a/src/main/java/com/debatetimer/client/oauth/OAuthClient.java +++ b/src/main/java/com/debatetimer/client/oauth/OAuthClient.java @@ -4,7 +4,9 @@ import com.debatetimer.dto.member.MemberCreateRequest; import com.debatetimer.dto.member.MemberInfo; import com.debatetimer.dto.member.OAuthToken; +import com.debatetimer.exception.handler.OAuthClientErrorHandler; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.client.RestClient; @@ -17,8 +19,8 @@ public class OAuthClient { private final RestClient restClient; private final OAuthProperties oauthProperties; - public OAuthClient(OAuthProperties oauthProperties) { - this.restClient = RestClient.create(); + public OAuthClient(RestClient.Builder restClientBuilder, OAuthProperties oauthProperties) { + this.restClient = restClientBuilder.build(); this.oauthProperties = oauthProperties; } @@ -28,6 +30,7 @@ public OAuthToken requestToken(MemberCreateRequest request) { .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body(oauthProperties.createTokenRequestBody(request)) .retrieve() + .onStatus(HttpStatusCode::isError, new OAuthClientErrorHandler()) .body(OAuthToken.class); } diff --git a/src/main/java/com/debatetimer/exception/OAuthErrorResponse.java b/src/main/java/com/debatetimer/exception/OAuthErrorResponse.java new file mode 100644 index 00000000..f1a90158 --- /dev/null +++ b/src/main/java/com/debatetimer/exception/OAuthErrorResponse.java @@ -0,0 +1,12 @@ +package com.debatetimer.exception; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public record OAuthErrorResponse( + String error, + String errorDescription +) { + +} diff --git a/src/main/java/com/debatetimer/exception/custom/DTOAuthClientException.java b/src/main/java/com/debatetimer/exception/custom/DTOAuthClientException.java new file mode 100644 index 00000000..b7ca9830 --- /dev/null +++ b/src/main/java/com/debatetimer/exception/custom/DTOAuthClientException.java @@ -0,0 +1,15 @@ +package com.debatetimer.exception.custom; + +import com.debatetimer.exception.errorcode.OAuthErrorCode; +import lombok.Getter; + +@Getter +public class DTOAuthClientException extends DTErrorResponseException { + + private final String errorResponse; + + public DTOAuthClientException(OAuthErrorCode code, String errorResponse) { + super(code.getMessage(), code.getStatus()); + this.errorResponse = errorResponse; + } +} diff --git a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java index 9f7da726..39cbdceb 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 { MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 회원이 존재하지 않습니다"), + INVALID_OAUTH_REQUEST(HttpStatus.BAD_REQUEST, "구글 OAuth 요청 파라미터 값이 잘못되었습니다."), + FIELD_ERROR(HttpStatus.BAD_REQUEST, "입력이 잘못되었습니다."), URL_PARAMETER_ERROR(HttpStatus.BAD_REQUEST, "입력이 잘못되었습니다."), METHOD_ARGUMENT_TYPE_MISMATCH(HttpStatus.BAD_REQUEST, "입력한 값의 타입이 잘못되었습니다."), diff --git a/src/main/java/com/debatetimer/exception/errorcode/OAuthErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/OAuthErrorCode.java new file mode 100644 index 00000000..1abb1877 --- /dev/null +++ b/src/main/java/com/debatetimer/exception/errorcode/OAuthErrorCode.java @@ -0,0 +1,26 @@ +package com.debatetimer.exception.errorcode; + +import java.util.Optional; +import java.util.stream.Stream; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum OAuthErrorCode { + + INVALID_REDIRECT_URI("redirect_uri_mismatch", HttpStatus.BAD_REQUEST, "구글 OAuth redirect_uri가 잘못되었습니다"), + INVALID_AUTHORIZATION_CODE("invalid_grant", HttpStatus.BAD_REQUEST, "구글 OAuth 인증 코드가 잘못되었습니다."), + ; + + private final String error; + private final HttpStatus status; + private final String message; + + public static Optional mapTo(String error) { + return Stream.of(values()) + .filter(errorCode -> error.equals(errorCode.getError())) + .findAny(); + } +} diff --git a/src/main/java/com/debatetimer/exception/errorcode/ServerErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/ServerErrorCode.java index 007b0ec2..1bd205f5 100644 --- a/src/main/java/com/debatetimer/exception/errorcode/ServerErrorCode.java +++ b/src/main/java/com/debatetimer/exception/errorcode/ServerErrorCode.java @@ -7,7 +7,8 @@ public enum ServerErrorCode implements ResponseErrorCode { INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 오류가 발생했습니다. 관리자에게 문의하세요."), - EXCEL_EXPORT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "엑셀 변환 과정에서 오류가 발생하였습니다"); + EXCEL_EXPORT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "엑셀 변환 과정에서 오류가 발생하였습니다"), + OAUTH_REQUEST_FAILED_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "구글 OAuth 요청 과정에 오류가 발생하였습니다"); private final HttpStatus status; private final String message; diff --git a/src/main/java/com/debatetimer/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/debatetimer/exception/handler/GlobalExceptionHandler.java index aa9627d4..e5d107e4 100644 --- a/src/main/java/com/debatetimer/exception/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/debatetimer/exception/handler/GlobalExceptionHandler.java @@ -3,6 +3,7 @@ import com.debatetimer.client.notifier.ErrorNotifier; import com.debatetimer.exception.ErrorResponse; import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.custom.DTOAuthClientException; import com.debatetimer.exception.custom.DTServerErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import com.debatetimer.exception.errorcode.ResponseErrorCode; @@ -100,6 +101,12 @@ public ResponseEntity handleMissingRequestCookieException(Missing return toResponse(ClientErrorCode.NO_COOKIE_FOUND); } + @ExceptionHandler(DTOAuthClientException.class) + public ResponseEntity handleOAuthClientException(DTOAuthClientException exception) { + logClientError(exception); + return toResponse(exception.getHttpStatus(), exception.getMessage()); + } + @ExceptionHandler(DTClientErrorException.class) public ResponseEntity handleClientException(DTClientErrorException exception) { logClientError(exception); diff --git a/src/main/java/com/debatetimer/exception/handler/OAuthClientErrorHandler.java b/src/main/java/com/debatetimer/exception/handler/OAuthClientErrorHandler.java new file mode 100644 index 00000000..9e2cada8 --- /dev/null +++ b/src/main/java/com/debatetimer/exception/handler/OAuthClientErrorHandler.java @@ -0,0 +1,32 @@ +package com.debatetimer.exception.handler; + +import com.debatetimer.exception.OAuthErrorResponse; +import com.debatetimer.exception.custom.DTOAuthClientException; +import com.debatetimer.exception.custom.DTServerErrorException; +import com.debatetimer.exception.errorcode.OAuthErrorCode; +import com.debatetimer.exception.errorcode.ServerErrorCode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.Optional; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClient.ResponseSpec.ErrorHandler; + +@Component +public class OAuthClientErrorHandler implements ErrorHandler { + + @Override + public void handle(HttpRequest request, ClientHttpResponse response) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + OAuthErrorResponse errorResponse = objectMapper.readValue(response.getBody(), OAuthErrorResponse.class); + + Optional oauthClientErrorCode = OAuthErrorCode.mapTo(errorResponse.error()); + + if (oauthClientErrorCode.isPresent()) { + String responseBody = objectMapper.writeValueAsString(errorResponse); + throw new DTOAuthClientException(oauthClientErrorCode.get(), responseBody); + } + throw new DTServerErrorException(ServerErrorCode.OAUTH_REQUEST_FAILED_ERROR); + } +} diff --git a/src/test/java/com/debatetimer/client/oauth/OAuthClientTest.java b/src/test/java/com/debatetimer/client/oauth/OAuthClientTest.java new file mode 100644 index 00000000..84c47a92 --- /dev/null +++ b/src/test/java/com/debatetimer/client/oauth/OAuthClientTest.java @@ -0,0 +1,90 @@ +package com.debatetimer.client.oauth; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; + +import com.debatetimer.dto.member.MemberCreateRequest; +import com.debatetimer.exception.custom.DTOAuthClientException; +import com.debatetimer.exception.custom.DTServerErrorException; +import com.debatetimer.exception.errorcode.OAuthErrorCode; +import com.debatetimer.exception.errorcode.ServerErrorCode; +import org.junit.jupiter.api.BeforeEach; +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; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; +import org.springframework.http.HttpMethod; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.test.web.client.ResponseCreator; +import org.springframework.test.web.client.response.MockRestResponseCreators; +import org.springframework.web.client.RestClient; + +@RestClientTest(OAuthClient.class) +class OAuthClientTest { + + @Autowired + private MockRestServiceServer mockServer; + + @Autowired + private RestClient.Builder restClientBuilder; + + @Autowired + private OAuthProperties oAuthProperties; + + private OAuthClient oAuthClient; + + @BeforeEach + void setUp() { + this.mockServer = MockRestServiceServer.bindTo(restClientBuilder).build(); + this.oAuthClient = new OAuthClient(restClientBuilder, oAuthProperties); + } + + @Nested + class RequestTokenExceptionHandling { + + @ParameterizedTest + @ValueSource(strings = { + "{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}", + "{\"error\":\"invalid_grant\",\"error_description\":\"Malformed auth code.\"}", + }) + void 잘못된_인증코드로_토큰_발급요청_시_클라이언트_에러를_반환한다(String invalidCodeResponse) { + MemberCreateRequest memberCreateRequest = new MemberCreateRequest("invalid_code", "redirect_uri"); + setMockserver(MockRestResponseCreators.withBadRequest().body(invalidCodeResponse)); + + assertThatThrownBy(() -> oAuthClient.requestToken(memberCreateRequest)) + .isInstanceOf(DTOAuthClientException.class) + .hasMessage(OAuthErrorCode.INVALID_AUTHORIZATION_CODE.getMessage()); + } + + @Test + void 잘못된_리다이렉트_uri로_토큰_발급요청_시_oauth_클라이언트_에러를_반환한다() { + MemberCreateRequest memberCreateRequest = new MemberCreateRequest("code", "invalid_uri"); + String invalidRedirectUrlResponse = "{\"error\":\"redirect_uri_mismatch\",\"error_description\":\"Bad Request\"}"; + setMockserver(MockRestResponseCreators.withBadRequest().body(invalidRedirectUrlResponse)); + + assertThatThrownBy(() -> oAuthClient.requestToken(memberCreateRequest)) + .isInstanceOf(DTOAuthClientException.class) + .hasMessage(OAuthErrorCode.INVALID_REDIRECT_URI.getMessage()); + } + + @Test + void 클라이언트_인증_에러는_서버에러를_반환한다() { + MemberCreateRequest memberCreateRequest = new MemberCreateRequest("code", "redirect_url"); + String otherErrorResponse = "{\"error\":\"invalid_client\",\"error_description\":\"The OAuth client was not found.\"}"; + setMockserver(MockRestResponseCreators.withUnauthorizedRequest().body(otherErrorResponse)); + + assertThatThrownBy(() -> oAuthClient.requestToken(memberCreateRequest)) + .isInstanceOf(DTServerErrorException.class) + .hasMessage(ServerErrorCode.OAUTH_REQUEST_FAILED_ERROR.getMessage()); + } + + private void setMockserver(ResponseCreator responseCreator) { + mockServer.expect(requestTo("https://oauth2.googleapis.com/token")) + .andExpect(method(HttpMethod.POST)) + .andRespond(responseCreator); + } + } +} From 7764ce51d8b28ce816ee65b5e210e14705f0919b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B1=B4=EC=9A=B0?= Date: Wed, 14 May 2025 21:29:00 +0900 Subject: [PATCH 02/27] =?UTF-8?q?[CHORE]=20=EC=9D=98=ED=9A=8C=EC=8B=9D=20?= =?UTF-8?q?=ED=86=A0=EB=A1=A0=20api=20=EC=A0=9C=EA=B1=B0=20(#172)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ParliamentaryController.java | 88 ---- .../parliamentary/ParliamentaryBoxType.java | 22 - .../parliamentary/ParliamentaryTable.java | 46 -- .../parliamentary/ParliamentaryTimeBox.java | 81 --- .../dto/member/TableResponses.java | 20 +- .../ParliamentaryTableCreateRequest.java | 26 - .../ParliamentaryTableInfoCreateRequest.java | 22 - .../ParliamentaryTimeBoxCreateRequest.java | 26 - .../ParliamentaryTableInfoResponse.java | 18 - .../response/ParliamentaryTableResponse.java | 29 -- .../ParliamentaryTimeBoxResponse.java | 16 - .../ParliamentaryTableRepository.java | 25 - .../ParliamentaryTimeBoxRepository.java | 34 -- .../service/member/MemberService.java | 8 +- .../parliamentary/ParliamentaryService.java | 101 ---- .../exporter/ParliamentaryBoxTypeView.java | 30 -- .../ParliamentaryTableExcelExporter.java | 208 -------- ...rliamentaryTableExportMessageResolver.java | 54 -- .../V8__drop_parliamnetary_table_and_box.sql | 2 + .../controller/BaseControllerTest.java | 12 - .../controller/BaseDocumentTest.java | 4 - .../member/MemberControllerTest.java | 4 +- .../ParliamentaryControllerTest.java | 158 ------ .../ParliamentaryDocumentTest.java | 463 ------------------ .../com/debatetimer/domain/TimeBoxesTest.java | 21 +- .../parliamentary/ParliamentaryTableTest.java | 21 - .../ParliamentaryTimeBoxTest.java | 77 --- .../fixture/ParliamentaryTableGenerator.java | 27 - .../ParliamentaryTimeBoxGenerator.java | 30 -- .../repository/BaseRepositoryTest.java | 10 - .../ParliamentaryTableRepositoryTest.java | 58 --- .../ParliamentaryTimeBoxRepositoryTest.java | 39 -- .../debatetimer/service/BaseServiceTest.java | 16 - .../service/member/MemberServiceTest.java | 10 +- .../ParliamentaryServiceTest.java | 207 -------- .../ParliamentaryBoxTypeViewTest.java | 22 - 36 files changed, 23 insertions(+), 2012 deletions(-) delete mode 100644 src/main/java/com/debatetimer/controller/parliamentary/ParliamentaryController.java delete mode 100644 src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryBoxType.java delete mode 100644 src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryTable.java delete mode 100644 src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryTimeBox.java delete mode 100644 src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTableCreateRequest.java delete mode 100644 src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTableInfoCreateRequest.java delete mode 100644 src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTimeBoxCreateRequest.java delete mode 100644 src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTableInfoResponse.java delete mode 100644 src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTableResponse.java delete mode 100644 src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTimeBoxResponse.java delete mode 100644 src/main/java/com/debatetimer/repository/parliamentary/ParliamentaryTableRepository.java delete mode 100644 src/main/java/com/debatetimer/repository/parliamentary/ParliamentaryTimeBoxRepository.java delete mode 100644 src/main/java/com/debatetimer/service/parliamentary/ParliamentaryService.java delete mode 100644 src/main/java/com/debatetimer/view/exporter/ParliamentaryBoxTypeView.java delete mode 100644 src/main/java/com/debatetimer/view/exporter/ParliamentaryTableExcelExporter.java delete mode 100644 src/main/java/com/debatetimer/view/exporter/ParliamentaryTableExportMessageResolver.java create mode 100644 src/main/resources/db/migration/V8__drop_parliamnetary_table_and_box.sql delete mode 100644 src/test/java/com/debatetimer/controller/parliamentary/ParliamentaryControllerTest.java delete mode 100644 src/test/java/com/debatetimer/controller/parliamentary/ParliamentaryDocumentTest.java delete mode 100644 src/test/java/com/debatetimer/domain/parliamentary/ParliamentaryTableTest.java delete mode 100644 src/test/java/com/debatetimer/domain/parliamentary/ParliamentaryTimeBoxTest.java delete mode 100644 src/test/java/com/debatetimer/fixture/ParliamentaryTableGenerator.java delete mode 100644 src/test/java/com/debatetimer/fixture/ParliamentaryTimeBoxGenerator.java delete mode 100644 src/test/java/com/debatetimer/repository/parliamentary/ParliamentaryTableRepositoryTest.java delete mode 100644 src/test/java/com/debatetimer/repository/parliamentary/ParliamentaryTimeBoxRepositoryTest.java delete mode 100644 src/test/java/com/debatetimer/service/parliamentary/ParliamentaryServiceTest.java delete mode 100644 src/test/java/com/debatetimer/view/exporter/ParliamentaryBoxTypeViewTest.java diff --git a/src/main/java/com/debatetimer/controller/parliamentary/ParliamentaryController.java b/src/main/java/com/debatetimer/controller/parliamentary/ParliamentaryController.java deleted file mode 100644 index 9a2176f2..00000000 --- a/src/main/java/com/debatetimer/controller/parliamentary/ParliamentaryController.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.debatetimer.controller.parliamentary; - -import com.debatetimer.controller.auth.AuthMember; -import com.debatetimer.controller.tool.export.ExcelExport; -import com.debatetimer.domain.member.Member; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTableCreateRequest; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTableResponse; -import com.debatetimer.service.parliamentary.ParliamentaryService; -import com.debatetimer.view.exporter.ParliamentaryTableExcelExporter; -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import org.springframework.core.io.InputStreamResource; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -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 ParliamentaryController { - - private final ParliamentaryService parliamentaryService; - private final ParliamentaryTableExcelExporter parliamentaryTableExcelExporter; - - @PostMapping("/api/table/parliamentary") - @ResponseStatus(HttpStatus.CREATED) - public ParliamentaryTableResponse save( - @Valid @RequestBody ParliamentaryTableCreateRequest tableCreateRequest, - @AuthMember Member member - ) { - return parliamentaryService.save(tableCreateRequest, member); - } - - @GetMapping("/api/table/parliamentary/{tableId}") - @ResponseStatus(HttpStatus.OK) - public ParliamentaryTableResponse getTable( - @PathVariable Long tableId, - @AuthMember Member member - ) { - return parliamentaryService.findTable(tableId, member); - } - - @PutMapping("/api/table/parliamentary/{tableId}") - @ResponseStatus(HttpStatus.OK) - public ParliamentaryTableResponse updateTable( - @Valid @RequestBody ParliamentaryTableCreateRequest tableCreateRequest, - @PathVariable Long tableId, - @AuthMember Member member - ) { - return parliamentaryService.updateTable(tableCreateRequest, tableId, member); - } - - @PatchMapping("/api/table/parliamentary/{tableId}/debate") - @ResponseStatus(HttpStatus.OK) - public ParliamentaryTableResponse debate( - @PathVariable Long tableId, - @AuthMember Member member - ) { - return parliamentaryService.updateUsedAt(tableId, member); - } - - @DeleteMapping("/api/table/parliamentary/{tableId}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void deleteTable( - @PathVariable Long tableId, - @AuthMember Member member - ) { - parliamentaryService.deleteTable(tableId, member); - } - - @GetMapping("/api/table/parliamentary/export/{tableId}") - @ExcelExport - public ResponseEntity export( - @AuthMember Member member, - @PathVariable Long tableId - ) { - ParliamentaryTableResponse foundTable = parliamentaryService.findTableById(tableId, member.getId()); - InputStreamResource excelStream = parliamentaryTableExcelExporter.export(foundTable); - return ResponseEntity.ok(excelStream); - } -} diff --git a/src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryBoxType.java b/src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryBoxType.java deleted file mode 100644 index c0131db7..00000000 --- a/src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryBoxType.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.debatetimer.domain.parliamentary; - -import com.debatetimer.domain.Stance; -import java.util.Set; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public enum ParliamentaryBoxType { - - OPENING(Set.of(Stance.PROS, Stance.CONS)), - REBUTTAL(Set.of(Stance.PROS, Stance.CONS)), - CROSS(Set.of(Stance.PROS, Stance.CONS)), - CLOSING(Set.of(Stance.PROS, Stance.CONS)), - TIME_OUT(Set.of(Stance.NEUTRAL)), - ; - - private final Set availableStances; - - public boolean isAvailable(Stance stance) { - return availableStances.contains(stance); - } -} diff --git a/src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryTable.java b/src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryTable.java deleted file mode 100644 index 08041d58..00000000 --- a/src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryTable.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.debatetimer.domain.parliamentary; - -import com.debatetimer.domain.DebateTable; -import com.debatetimer.domain.member.Member; -import com.debatetimer.dto.member.TableType; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Entity -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ParliamentaryTable extends DebateTable { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - public ParliamentaryTable( - Member member, - String name, - String agenda, - boolean warningBell, - boolean finishBell - ) { - super(member, name, agenda, warningBell, finishBell); - } - - @Override - public long getId() { - return id; - } - - @Override - public TableType getType() { - return TableType.PARLIAMENTARY; - } - - public void update(ParliamentaryTable renewTable) { - updateTable(renewTable); - } -} diff --git a/src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryTimeBox.java b/src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryTimeBox.java deleted file mode 100644 index a987552d..00000000 --- a/src/main/java/com/debatetimer/domain/parliamentary/ParliamentaryTimeBox.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.debatetimer.domain.parliamentary; - -import com.debatetimer.domain.DebateTimeBox; -import com.debatetimer.domain.Stance; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.validation.constraints.NotNull; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Entity -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ParliamentaryTimeBox extends DebateTimeBox { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "table_id") - private ParliamentaryTable parliamentaryTable; - - @NotNull - @Enumerated(EnumType.STRING) - private ParliamentaryBoxType type; - - public ParliamentaryTimeBox( - ParliamentaryTable parliamentaryTable, - int sequence, - Stance stance, - ParliamentaryBoxType type, - int time, - Integer speaker - ) { - super(sequence, stance, time, convertToSpeaker(speaker)); - validate(stance, type); - validateSpeakerNumber(speaker); - - this.parliamentaryTable = parliamentaryTable; - this.type = type; - } - - private static String convertToSpeaker(Integer speakerNumber) { - if (speakerNumber == null) { - return null; - } - return String.valueOf(speakerNumber); - } - - private void validate(Stance stance, ParliamentaryBoxType boxType) { - if (!boxType.isAvailable(stance)) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_STANCE); - } - } - - private void validateSpeakerNumber(Integer speaker) { - if (speaker != null && speaker <= 0) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SPEAKER); - } - } - - public Integer getSpeakerNumber() { - String speaker = getSpeaker(); - if (speaker == null || speaker.isBlank() || speaker.equals("null")) { - return null; - } - return Integer.parseInt(speaker); - } -} diff --git a/src/main/java/com/debatetimer/dto/member/TableResponses.java b/src/main/java/com/debatetimer/dto/member/TableResponses.java index 51cbde50..9ee7968a 100644 --- a/src/main/java/com/debatetimer/dto/member/TableResponses.java +++ b/src/main/java/com/debatetimer/dto/member/TableResponses.java @@ -2,10 +2,9 @@ import com.debatetimer.domain.DebateTable; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; import java.util.Comparator; import java.util.List; -import java.util.stream.Stream; +import java.util.stream.Collectors; public record TableResponses(List tables) { @@ -13,21 +12,10 @@ public record TableResponses(List tables) { .comparing(DebateTable::getUsedAt) .reversed(); - public TableResponses( - List parliamentaryTables, - List customizeTables - ) { - this(toTableResponses(parliamentaryTables, customizeTables)); - } - - private static List toTableResponses( - List parliamentaryTables, - List customizeTables - ) { - return Stream.of(parliamentaryTables, customizeTables) - .flatMap(List::stream) + public static TableResponses from(List customizeTables) { + return customizeTables.stream() .sorted(DEBATE_TABLE_COMPARATOR) .map(TableResponse::new) - .toList(); + .collect(Collectors.collectingAndThen(Collectors.toList(), TableResponses::new)); } } diff --git a/src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTableCreateRequest.java b/src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTableCreateRequest.java deleted file mode 100644 index 9e86c58a..00000000 --- a/src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTableCreateRequest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.debatetimer.dto.parliamentary.request; - -import com.debatetimer.domain.TimeBoxes; -import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.domain.parliamentary.ParliamentaryTimeBox; -import jakarta.validation.Valid; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public record ParliamentaryTableCreateRequest( - @Valid ParliamentaryTableInfoCreateRequest info, - @Valid List table -) { - - public ParliamentaryTable toTable(Member member) { - return info.toTable(member); - } - - public TimeBoxes toTimeBoxes(ParliamentaryTable parliamentaryTable) { - return IntStream.range(0, table.size()) - .mapToObj(i -> table.get(i).toTimeBox(parliamentaryTable, i + 1)) - .collect(Collectors.collectingAndThen(Collectors.toList(), TimeBoxes::new)); - } -} diff --git a/src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTableInfoCreateRequest.java b/src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTableInfoCreateRequest.java deleted file mode 100644 index 764fd62b..00000000 --- a/src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTableInfoCreateRequest.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.debatetimer.dto.parliamentary.request; - -import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; - -public record ParliamentaryTableInfoCreateRequest( - @NotBlank - String name, - - @NotNull - String agenda, - - boolean warningBell, - boolean finishBell -) { - - public ParliamentaryTable toTable(Member member) { - return new ParliamentaryTable(member, name, agenda, warningBell, finishBell); - } -} diff --git a/src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTimeBoxCreateRequest.java b/src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTimeBoxCreateRequest.java deleted file mode 100644 index 7cf15f01..00000000 --- a/src/main/java/com/debatetimer/dto/parliamentary/request/ParliamentaryTimeBoxCreateRequest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.debatetimer.dto.parliamentary.request; - -import com.debatetimer.domain.Stance; -import com.debatetimer.domain.parliamentary.ParliamentaryBoxType; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.domain.parliamentary.ParliamentaryTimeBox; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Positive; - -public record ParliamentaryTimeBoxCreateRequest( - @NotNull - Stance stance, - - @NotNull - ParliamentaryBoxType type, - - @Positive - int time, - - Integer speakerNumber -) { - - public ParliamentaryTimeBox toTimeBox(ParliamentaryTable parliamentaryTable, int sequence) { - return new ParliamentaryTimeBox(parliamentaryTable, sequence, stance, type, time, speakerNumber); - } -} diff --git a/src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTableInfoResponse.java b/src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTableInfoResponse.java deleted file mode 100644 index 7894432f..00000000 --- a/src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTableInfoResponse.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.debatetimer.dto.parliamentary.response; - -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.dto.member.TableType; - -public record ParliamentaryTableInfoResponse(String name, TableType type, String agenda, boolean warningBell, - boolean finishBell) { - - public ParliamentaryTableInfoResponse(ParliamentaryTable parliamentaryTable) { - this( - parliamentaryTable.getName(), - TableType.PARLIAMENTARY, - parliamentaryTable.getAgenda(), - parliamentaryTable.isWarningBell(), - parliamentaryTable.isFinishBell() - ); - } -} diff --git a/src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTableResponse.java b/src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTableResponse.java deleted file mode 100644 index 0d4dbb63..00000000 --- a/src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTableResponse.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.debatetimer.dto.parliamentary.response; - -import com.debatetimer.domain.TimeBoxes; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.domain.parliamentary.ParliamentaryTimeBox; -import java.util.List; - -public record ParliamentaryTableResponse(long id, ParliamentaryTableInfoResponse info, - List table) { - - public ParliamentaryTableResponse( - ParliamentaryTable parliamentaryTable, - TimeBoxes parliamentaryTimeBoxes - ) { - this( - parliamentaryTable.getId(), - new ParliamentaryTableInfoResponse(parliamentaryTable), - toTimeBoxResponses(parliamentaryTimeBoxes) - ); - } - - private static List toTimeBoxResponses(TimeBoxes timeBoxes) { - List parliamentaryTimeBoxes = timeBoxes.getTimeBoxes(); - return parliamentaryTimeBoxes - .stream() - .map(ParliamentaryTimeBoxResponse::new) - .toList(); - } -} diff --git a/src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTimeBoxResponse.java b/src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTimeBoxResponse.java deleted file mode 100644 index d30cfade..00000000 --- a/src/main/java/com/debatetimer/dto/parliamentary/response/ParliamentaryTimeBoxResponse.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.debatetimer.dto.parliamentary.response; - -import com.debatetimer.domain.Stance; -import com.debatetimer.domain.parliamentary.ParliamentaryBoxType; -import com.debatetimer.domain.parliamentary.ParliamentaryTimeBox; - -public record ParliamentaryTimeBoxResponse(Stance stance, ParliamentaryBoxType type, int time, Integer speakerNumber) { - - public ParliamentaryTimeBoxResponse(ParliamentaryTimeBox parliamentaryTimeBox) { - this(parliamentaryTimeBox.getStance(), - parliamentaryTimeBox.getType(), - parliamentaryTimeBox.getTime(), - parliamentaryTimeBox.getSpeakerNumber() - ); - } -} diff --git a/src/main/java/com/debatetimer/repository/parliamentary/ParliamentaryTableRepository.java b/src/main/java/com/debatetimer/repository/parliamentary/ParliamentaryTableRepository.java deleted file mode 100644 index f60ca55b..00000000 --- a/src/main/java/com/debatetimer/repository/parliamentary/ParliamentaryTableRepository.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.debatetimer.repository.parliamentary; - -import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import java.util.List; -import java.util.Optional; -import org.springframework.data.repository.Repository; - -public interface ParliamentaryTableRepository extends Repository { - - ParliamentaryTable save(ParliamentaryTable parliamentaryTable); - - Optional findById(long id); - - default ParliamentaryTable getById(long tableId) { - return findById(tableId) - .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); - } - - List findAllByMember(Member member); - - void delete(ParliamentaryTable table); -} diff --git a/src/main/java/com/debatetimer/repository/parliamentary/ParliamentaryTimeBoxRepository.java b/src/main/java/com/debatetimer/repository/parliamentary/ParliamentaryTimeBoxRepository.java deleted file mode 100644 index 63120aa6..00000000 --- a/src/main/java/com/debatetimer/repository/parliamentary/ParliamentaryTimeBoxRepository.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.debatetimer.repository.parliamentary; - -import com.debatetimer.domain.TimeBoxes; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.domain.parliamentary.ParliamentaryTimeBox; -import java.util.List; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.Repository; -import org.springframework.transaction.annotation.Transactional; - -public interface ParliamentaryTimeBoxRepository extends Repository { - - ParliamentaryTimeBox save(ParliamentaryTimeBox timeBox); - - @Transactional - default List saveAll(List timeBoxes) { - return timeBoxes.stream() - .map(this::save) - .toList(); - } - - List findAllByParliamentaryTable(ParliamentaryTable table); - - default TimeBoxes findTableTimeBoxes(ParliamentaryTable table) { - List timeBoxes = findAllByParliamentaryTable(table); - return new TimeBoxes<>(timeBoxes); - } - - @Query("DELETE FROM ParliamentaryTimeBox ptb WHERE ptb IN :timeBoxes") - @Modifying(clearAutomatically = true, flushAutomatically = true) - @Transactional - void deleteAll(List timeBoxes); -} diff --git a/src/main/java/com/debatetimer/service/member/MemberService.java b/src/main/java/com/debatetimer/service/member/MemberService.java index 210b2067..f5634e8b 100644 --- a/src/main/java/com/debatetimer/service/member/MemberService.java +++ b/src/main/java/com/debatetimer/service/member/MemberService.java @@ -2,13 +2,11 @@ import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; import com.debatetimer.dto.member.MemberCreateResponse; import com.debatetimer.dto.member.MemberInfo; import com.debatetimer.dto.member.TableResponses; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.member.MemberRepository; -import com.debatetimer.repository.parliamentary.ParliamentaryTableRepository; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -19,15 +17,13 @@ public class MemberService { private final MemberRepository memberRepository; - private final ParliamentaryTableRepository parliamentaryTableRepository; private final CustomizeTableRepository customizeTableRepository; @Transactional(readOnly = true) public TableResponses getTables(long memberId) { Member member = memberRepository.getById(memberId); - List parliamentaryTables = parliamentaryTableRepository.findAllByMember(member); - List customizeTables = customizeTableRepository.findAllByMember(member); - return new TableResponses(parliamentaryTables, customizeTables); + List memberTables = customizeTableRepository.findAllByMember(member); + return TableResponses.from(memberTables); } @Transactional diff --git a/src/main/java/com/debatetimer/service/parliamentary/ParliamentaryService.java b/src/main/java/com/debatetimer/service/parliamentary/ParliamentaryService.java deleted file mode 100644 index 7b58a615..00000000 --- a/src/main/java/com/debatetimer/service/parliamentary/ParliamentaryService.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.debatetimer.service.parliamentary; - -import com.debatetimer.domain.TimeBoxes; -import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.domain.parliamentary.ParliamentaryTimeBox; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTableCreateRequest; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTableResponse; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import com.debatetimer.repository.parliamentary.ParliamentaryTableRepository; -import com.debatetimer.repository.parliamentary.ParliamentaryTimeBoxRepository; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -public class ParliamentaryService { - - private final ParliamentaryTableRepository tableRepository; - private final ParliamentaryTimeBoxRepository timeBoxRepository; - - @Transactional - public ParliamentaryTableResponse save(ParliamentaryTableCreateRequest tableCreateRequest, Member member) { - ParliamentaryTable table = tableCreateRequest.toTable(member); - ParliamentaryTable savedTable = tableRepository.save(table); - - TimeBoxes savedTimeBoxes = saveTimeBoxes(tableCreateRequest, savedTable); - return new ParliamentaryTableResponse(savedTable, savedTimeBoxes); - } - - @Transactional(readOnly = true) - public ParliamentaryTableResponse findTable(long tableId, Member member) { - ParliamentaryTable table = getOwnerTable(tableId, member.getId()); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); - return new ParliamentaryTableResponse(table, timeBoxes); - } - - @Transactional(readOnly = true) - public ParliamentaryTableResponse findTableById(long tableId, long id) { - ParliamentaryTable table = getOwnerTable(tableId, id); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); - return new ParliamentaryTableResponse(table, timeBoxes); - } - - @Transactional(isolation = Isolation.SERIALIZABLE) - public ParliamentaryTableResponse updateTable( - ParliamentaryTableCreateRequest tableCreateRequest, - long tableId, - Member member - ) { - ParliamentaryTable existingTable = getOwnerTable(tableId, member.getId()); - ParliamentaryTable renewedTable = tableCreateRequest.toTable(member); - existingTable.update(renewedTable); - - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(existingTable); - timeBoxRepository.deleteAll(timeBoxes.getTimeBoxes()); - TimeBoxes savedTimeBoxes = saveTimeBoxes(tableCreateRequest, existingTable); - return new ParliamentaryTableResponse(existingTable, savedTimeBoxes); - } - - @Transactional - public ParliamentaryTableResponse updateUsedAt(long tableId, Member member) { - ParliamentaryTable table = getOwnerTable(tableId, member.getId()); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); - table.updateUsedAt(); - return new ParliamentaryTableResponse(table, timeBoxes); - } - - @Transactional - public void deleteTable(long tableId, Member member) { - ParliamentaryTable table = getOwnerTable(tableId, member.getId()); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); - timeBoxRepository.deleteAll(timeBoxes.getTimeBoxes()); - tableRepository.delete(table); - } - - private TimeBoxes saveTimeBoxes( - ParliamentaryTableCreateRequest tableCreateRequest, - ParliamentaryTable table - ) { - TimeBoxes timeBoxes = tableCreateRequest.toTimeBoxes(table); - List savedTimeBoxes = timeBoxRepository.saveAll(timeBoxes.getTimeBoxes()); - return new TimeBoxes<>(savedTimeBoxes); - } - - private ParliamentaryTable getOwnerTable(long tableId, long memberId) { - ParliamentaryTable foundTable = tableRepository.getById(tableId); - validateOwn(foundTable, memberId); - return foundTable; - } - - private void validateOwn(ParliamentaryTable table, long memberId) { - if (!table.isOwner(memberId)) { - throw new DTClientErrorException(ClientErrorCode.NOT_TABLE_OWNER); - } - } -} diff --git a/src/main/java/com/debatetimer/view/exporter/ParliamentaryBoxTypeView.java b/src/main/java/com/debatetimer/view/exporter/ParliamentaryBoxTypeView.java deleted file mode 100644 index 0cb7c60f..00000000 --- a/src/main/java/com/debatetimer/view/exporter/ParliamentaryBoxTypeView.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.debatetimer.view.exporter; - -import com.debatetimer.domain.parliamentary.ParliamentaryBoxType; -import com.debatetimer.exception.custom.DTServerErrorException; -import com.debatetimer.exception.errorcode.ServerErrorCode; -import java.util.stream.Stream; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public enum ParliamentaryBoxTypeView { - OPENING_VIEW(ParliamentaryBoxType.OPENING, "입론"), - REBUTTAL_VIEW(ParliamentaryBoxType.REBUTTAL, "반론"), - CROSS(ParliamentaryBoxType.CROSS, "교차 질의"), - CLOSING(ParliamentaryBoxType.CLOSING, "최종 발언"), - TIME_OUT(ParliamentaryBoxType.TIME_OUT, "작전 시간"), - ; - - private final ParliamentaryBoxType boxType; - private final String viewMessage; - - public static String mapView(ParliamentaryBoxType target) { - return Stream.of(values()) - .filter(value -> value.boxType == target) - .findAny() - .orElseThrow(() -> new DTServerErrorException(ServerErrorCode.EXCEL_EXPORT_ERROR)) - .viewMessage; - } -} diff --git a/src/main/java/com/debatetimer/view/exporter/ParliamentaryTableExcelExporter.java b/src/main/java/com/debatetimer/view/exporter/ParliamentaryTableExcelExporter.java deleted file mode 100644 index d3ea6be9..00000000 --- a/src/main/java/com/debatetimer/view/exporter/ParliamentaryTableExcelExporter.java +++ /dev/null @@ -1,208 +0,0 @@ -package com.debatetimer.view.exporter; - -import com.debatetimer.domain.Stance; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTableInfoResponse; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTableResponse; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTimeBoxResponse; -import com.debatetimer.exception.custom.DTServerErrorException; -import com.debatetimer.exception.errorcode.ServerErrorCode; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.FillPatternType; -import org.apache.poi.ss.usermodel.Font; -import org.apache.poi.ss.usermodel.HorizontalAlignment; -import org.apache.poi.ss.usermodel.IndexedColors; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.VerticalAlignment; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.util.CellRangeAddress; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.springframework.core.io.InputStreamResource; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class ParliamentaryTableExcelExporter { - - private static Font NORMAL; - private static Font BOLD; - private static Font BOLD_AND_WHITE; - - private static CellStyle HEADER_STYLE; - private static CellStyle HEADER_CELL_STYLE; - private static CellStyle PROS_HEADER_STYLE; - private static CellStyle CONS_HEADER_STYLE; - private static CellStyle PROS_STYLE; - private static CellStyle CONS_STYLE; - private static CellStyle NEUTRAL_STYLE; - - private static final String NAME_HEADER = "테이블 이름"; - private static final String TYPE_HEADER = "형식"; - private static final String PARLIAMENTARY_HEADER_BODY = "의회식 토론"; - private static final String AGENDA_HEADER = "토론 주제"; - private static final String PROS_HEADER = "찬성"; - private static final String CONS_HEADER = "반대"; - - private static final int PROS_COLUMN_NUMBER = 0; - private static final int CONS_COLUMN_NUMBER = 1; - private static final int NAME_HEADER_ROW_NUMBER = 1; - private static final int TYPE_HEADER_ROW_NUMBER = 2; - private static final int AGENDA_HEADER_ROW_NUMBER = 3; - private static final int TABLE_HEADER_ROW_NUMBER = 5; - private static final int TIME_BOX_FIRST_ROW_NUMBER = 6; - private static final int END_COLUMN_NUMBER = 3; - private static final int WIDTH_SIZE = 10000; - - private final ParliamentaryTableExportMessageResolver messageResolver; - - private static Font createFont(Workbook workbook, boolean bold, IndexedColors fontColor) { - Font font = workbook.createFont(); - font.setColor(fontColor.getIndex()); - font.setBold(bold); - return font; - } - - private static CellStyle createGroundColor( - Workbook workBook, - IndexedColors color, - Font cellFont, - HorizontalAlignment align - ) { - CellStyle style = workBook.createCellStyle(); - style.setFillForegroundColor(color.getIndex()); - style.setFillPattern(FillPatternType.SOLID_FOREGROUND); - style.setFont(cellFont); - style.setAlignment(align); - style.setVerticalAlignment(VerticalAlignment.CENTER); - return style; - } - - private static void initializeFont(Workbook workbook) { - NORMAL = createFont(workbook, false, IndexedColors.BLACK); - BOLD = createFont(workbook, true, IndexedColors.BLACK); - BOLD_AND_WHITE = createFont(workbook, true, IndexedColors.WHITE); - } - - private static void initializeStyle(Workbook workbook) { - HEADER_STYLE = createGroundColor(workbook, IndexedColors.LIGHT_YELLOW, BOLD, HorizontalAlignment.LEFT); - HEADER_CELL_STYLE = createGroundColor(workbook, IndexedColors.LIGHT_YELLOW, NORMAL, HorizontalAlignment.LEFT); - PROS_HEADER_STYLE = createGroundColor(workbook, IndexedColors.CORNFLOWER_BLUE, BOLD, - HorizontalAlignment.CENTER); - CONS_HEADER_STYLE = createGroundColor(workbook, IndexedColors.CORAL, BOLD, HorizontalAlignment.CENTER); - PROS_STYLE = createGroundColor(workbook, IndexedColors.LIGHT_CORNFLOWER_BLUE, NORMAL, - HorizontalAlignment.CENTER); - CONS_STYLE = createGroundColor(workbook, IndexedColors.ROSE, NORMAL, HorizontalAlignment.CENTER); - NEUTRAL_STYLE = createGroundColor(workbook, IndexedColors.GREY_50_PERCENT, BOLD_AND_WHITE, - HorizontalAlignment.CENTER); - } - - public InputStreamResource export( - ParliamentaryTableResponse parliamentaryTableResponse - ) { - ParliamentaryTableInfoResponse tableInfo = parliamentaryTableResponse.info(); - List timeBoxes = parliamentaryTableResponse.table(); - - Workbook workbook = new XSSFWorkbook(); - Sheet sheet = workbook.createSheet(tableInfo.name()); - initializeFont(workbook); - initializeStyle(workbook); - - createHeader(sheet, NAME_HEADER_ROW_NUMBER, NAME_HEADER, tableInfo.name()); - createHeader(sheet, TYPE_HEADER_ROW_NUMBER, TYPE_HEADER, PARLIAMENTARY_HEADER_BODY); - createHeader(sheet, AGENDA_HEADER_ROW_NUMBER, AGENDA_HEADER, tableInfo.agenda()); - - createTableHeader(sheet); - createTimeBoxRows(timeBoxes, sheet); - setColumnWidth(sheet); - return writeToInputStream(workbook); - } - - private InputStreamResource writeToInputStream(Workbook workbook) { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - workbook.write(outputStream); - workbook.close(); - ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); - return new InputStreamResource(inputStream); - } catch (IOException e) { - throw new DTServerErrorException(ServerErrorCode.EXCEL_EXPORT_ERROR); - } - } - - private void createHeader( - Sheet sheet, - int rowNumber, - String header, - String headerBody - ) { - Row row = sheet.createRow(rowNumber); - createCell(row, 0, header, HEADER_STYLE); - createCell(row, 1, headerBody, HEADER_CELL_STYLE); - } - - private void createTableHeader(Sheet sheet) { - Row row = sheet.createRow(TABLE_HEADER_ROW_NUMBER); - createCell(row, PROS_COLUMN_NUMBER, PROS_HEADER, PROS_HEADER_STYLE); - createCell(row, CONS_COLUMN_NUMBER, CONS_HEADER, CONS_HEADER_STYLE); - } - - private void createCell(Row row, int index, String value, CellStyle style) { - Cell cell = row.createCell(index); - cell.setCellStyle(style); - cell.setCellValue(value); - } - - private void setColumnWidth(Sheet sheet) { - for (int i = 0; i < END_COLUMN_NUMBER; i++) { - sheet.setColumnWidth(i, WIDTH_SIZE); - } - } - - private void createTimeBoxRows(List timeBoxes, Sheet sheet) { - for (int i = 0; i < timeBoxes.size(); i++) { - createTimeBoxRow(sheet, i + TIME_BOX_FIRST_ROW_NUMBER, timeBoxes.get(i)); - } - } - - private void createTimeBoxRow(Sheet sheet, int rowNumber, ParliamentaryTimeBoxResponse timeBox) { - Row row = sheet.createRow(rowNumber); - String timeBoxMessage = messageResolver.resolveBoxMessage(timeBox); - Stance stance = timeBox.stance(); - createRowByStance(sheet, row, stance, rowNumber, timeBoxMessage); - } - - private void createRowByStance(Sheet sheet, Row row, Stance stance, int index, String message) { - switch (stance) { - case NEUTRAL: - Cell neturalCell = row.createCell(0); - neturalCell.setCellStyle(NEUTRAL_STYLE); - neturalCell.setCellValue(message); - sheet.addMergedRegion(new CellRangeAddress(index, index, 0, 1)); // A1:B1 셀 병합 - break; - - case PROS: - setProsAndConsCellstyle(row); - Cell prosCell = row.getCell(PROS_COLUMN_NUMBER); - prosCell.setCellValue(message); - break; - - case CONS: - setProsAndConsCellstyle(row); - Cell consCell = row.getCell(CONS_COLUMN_NUMBER); - consCell.setCellValue(message); - break; - } - } - - private void setProsAndConsCellstyle(Row row) { - Cell prosCell = row.createCell(PROS_COLUMN_NUMBER); - prosCell.setCellStyle(PROS_STYLE); - Cell consCell = row.createCell(CONS_COLUMN_NUMBER); - consCell.setCellStyle(CONS_STYLE); - } -} diff --git a/src/main/java/com/debatetimer/view/exporter/ParliamentaryTableExportMessageResolver.java b/src/main/java/com/debatetimer/view/exporter/ParliamentaryTableExportMessageResolver.java deleted file mode 100644 index 08971443..00000000 --- a/src/main/java/com/debatetimer/view/exporter/ParliamentaryTableExportMessageResolver.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.debatetimer.view.exporter; - -import com.debatetimer.domain.parliamentary.ParliamentaryBoxType; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTimeBoxResponse; -import org.springframework.stereotype.Component; - -@Component -public class ParliamentaryTableExportMessageResolver { - - private static final String SPEAKER_SUFFIX = "번 토론자"; - private static final String MINUTES_MESSAGE = "분"; - private static final String SECOND_MESSAGE = "초"; - private static final String TIME_MESSAGE_PREFIX = "("; - private static final String TIME_MESSAGE_SUFFIX = ")"; - private static final String MESSAGE_DELIMITER = "/"; - private static final String SPACE = " "; - - public String resolveBoxMessage(ParliamentaryTimeBoxResponse timeBox) { - String defaultMessage = resolveDefaultMessage(timeBox); - ParliamentaryBoxType type = timeBox.type(); - if (type == ParliamentaryBoxType.TIME_OUT) { - return defaultMessage; - } - return defaultMessage - + MESSAGE_DELIMITER - + resolveSpeakerMessage(timeBox.speakerNumber()); - } - - private String resolveDefaultMessage(ParliamentaryTimeBoxResponse timeBox) { - ParliamentaryBoxType boxType = timeBox.type(); - return ParliamentaryBoxTypeView.mapView(boxType) - + resolveTimeMessage(timeBox.time()); - } - - private String resolveTimeMessage(int totalSecond) { - StringBuilder messageBuilder = new StringBuilder(); - int minutes = totalSecond / 60; - int second = totalSecond % 60; - - if (minutes != 0) { - messageBuilder.append(minutes + MINUTES_MESSAGE + SPACE); - } - if (second != 0) { - messageBuilder.append(second + SECOND_MESSAGE); - } - return TIME_MESSAGE_PREFIX - + messageBuilder - + TIME_MESSAGE_SUFFIX; - } - - private String resolveSpeakerMessage(int speakerNumber) { - return speakerNumber + SPEAKER_SUFFIX; - } -} diff --git a/src/main/resources/db/migration/V8__drop_parliamnetary_table_and_box.sql b/src/main/resources/db/migration/V8__drop_parliamnetary_table_and_box.sql new file mode 100644 index 00000000..f6b11d4b --- /dev/null +++ b/src/main/resources/db/migration/V8__drop_parliamnetary_table_and_box.sql @@ -0,0 +1,2 @@ +drop table parliamentary_time_box; +drop table parliamentary_table; diff --git a/src/test/java/com/debatetimer/controller/BaseControllerTest.java b/src/test/java/com/debatetimer/controller/BaseControllerTest.java index b0f6de9e..7ff30539 100644 --- a/src/test/java/com/debatetimer/controller/BaseControllerTest.java +++ b/src/test/java/com/debatetimer/controller/BaseControllerTest.java @@ -6,11 +6,8 @@ import com.debatetimer.fixture.CustomizeTimeBoxGenerator; import com.debatetimer.fixture.HeaderGenerator; import com.debatetimer.fixture.MemberGenerator; -import com.debatetimer.fixture.ParliamentaryTableGenerator; -import com.debatetimer.fixture.ParliamentaryTimeBoxGenerator; import com.debatetimer.fixture.TokenGenerator; import com.debatetimer.repository.customize.CustomizeTableRepository; -import com.debatetimer.repository.parliamentary.ParliamentaryTableRepository; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.filter.log.RequestLoggingFilter; @@ -27,21 +24,12 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public abstract class BaseControllerTest { - @Autowired - protected ParliamentaryTableRepository parliamentaryTableRepository; - @Autowired protected CustomizeTableRepository customizeTableRepository; @Autowired protected MemberGenerator memberGenerator; - @Autowired - protected ParliamentaryTableGenerator parliamentaryTableGenerator; - - @Autowired - protected ParliamentaryTimeBoxGenerator parliamentaryTimeBoxGenerator; - @Autowired protected CustomizeTableGenerator customizeTableGenerator; diff --git a/src/test/java/com/debatetimer/controller/BaseDocumentTest.java b/src/test/java/com/debatetimer/controller/BaseDocumentTest.java index 4a457f05..206c8cda 100644 --- a/src/test/java/com/debatetimer/controller/BaseDocumentTest.java +++ b/src/test/java/com/debatetimer/controller/BaseDocumentTest.java @@ -12,7 +12,6 @@ import com.debatetimer.service.auth.AuthService; import com.debatetimer.service.customize.CustomizeService; import com.debatetimer.service.member.MemberService; -import com.debatetimer.service.parliamentary.ParliamentaryService; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.filter.log.RequestLoggingFilter; @@ -57,9 +56,6 @@ public abstract class BaseDocumentTest { @MockitoBean protected MemberService memberService; - @MockitoBean - protected ParliamentaryService parliamentaryService; - @MockitoBean protected CustomizeService customizeService; diff --git a/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java b/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java index 0028d5aa..45604e67 100644 --- a/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java +++ b/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java @@ -6,7 +6,6 @@ import com.debatetimer.controller.BaseControllerTest; import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; import com.debatetimer.dto.member.MemberCreateRequest; import com.debatetimer.dto.member.MemberInfo; import com.debatetimer.dto.member.OAuthToken; @@ -24,7 +23,6 @@ class GetTables { @Test void 회원의_전체_토론_시간표를_조회한다() { Member member = memberGenerator.generate("default@gmail.com"); - parliamentaryTableRepository.save(new ParliamentaryTable(member, "토론 시간표 A", "주제", false, false)); customizeTableRepository.save(new CustomizeTable(member, "커스텀 테이블", "주제", false, false, "찬성", "반대")); @@ -37,7 +35,7 @@ class GetTables { .then().statusCode(200) .extract().as(TableResponses.class); - assertThat(response.tables()).hasSize(2); + assertThat(response.tables()).hasSize(1); } } diff --git a/src/test/java/com/debatetimer/controller/parliamentary/ParliamentaryControllerTest.java b/src/test/java/com/debatetimer/controller/parliamentary/ParliamentaryControllerTest.java deleted file mode 100644 index 6e38e278..00000000 --- a/src/test/java/com/debatetimer/controller/parliamentary/ParliamentaryControllerTest.java +++ /dev/null @@ -1,158 +0,0 @@ -package com.debatetimer.controller.parliamentary; - -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.Stance; -import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryBoxType; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTableCreateRequest; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTableInfoCreateRequest; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTimeBoxCreateRequest; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTableResponse; -import io.restassured.http.ContentType; -import io.restassured.http.Headers; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -class ParliamentaryControllerTest extends BaseControllerTest { - - @Nested - class Save { - - @Test - void 의회식_테이블을_생성한다() { - Member bito = memberGenerator.generate("default@gmail.com"); - ParliamentaryTableCreateRequest request = new ParliamentaryTableCreateRequest( - new ParliamentaryTableInfoCreateRequest("비토 테이블", "주제", true, true), - List.of( - new ParliamentaryTimeBoxCreateRequest(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxCreateRequest(Stance.CONS, ParliamentaryBoxType.OPENING, 3, null) - ) - ); - Headers headers = headerGenerator.generateAccessTokenHeader(bito); - - ParliamentaryTableResponse response = given() - .contentType(ContentType.JSON) - .headers(headers) - .body(request) - .when().post("/api/table/parliamentary") - .then().statusCode(201) - .extract().as(ParliamentaryTableResponse.class); - - assertAll( - () -> assertThat(response.info().name()).isEqualTo(request.info().name()), - () -> assertThat(response.table()).hasSize(request.table().size()) - ); - } - } - - @Nested - class GetTable { - - @Test - void 의회식_테이블을_조회한다() { - Member bito = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable bitoTable = parliamentaryTableGenerator.generate(bito); - parliamentaryTimeBoxGenerator.generate(bitoTable, 1); - parliamentaryTimeBoxGenerator.generateNotExistSpeaker(bitoTable, 2); - Headers headers = headerGenerator.generateAccessTokenHeader(bito); - - ParliamentaryTableResponse response = given() - .contentType(ContentType.JSON) - .pathParam("tableId", bitoTable.getId()) - .headers(headers) - .when().get("/api/table/parliamentary/{tableId}") - .then().statusCode(200) - .extract().as(ParliamentaryTableResponse.class); - - assertAll( - () -> assertThat(response.id()).isEqualTo(bitoTable.getId()), - () -> assertThat(response.table()).hasSize(2) - ); - } - } - - @Nested - class UpdateTable { - - @Test - void 의회식_토론_테이블을_업데이트한다() { - Member bito = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable bitoTable = parliamentaryTableGenerator.generate(bito); - ParliamentaryTableCreateRequest renewTableRequest = new ParliamentaryTableCreateRequest( - new ParliamentaryTableInfoCreateRequest("비토 테이블", "주제", true, true), - List.of( - new ParliamentaryTimeBoxCreateRequest(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxCreateRequest(Stance.CONS, ParliamentaryBoxType.OPENING, 3, null) - ) - ); - Headers headers = headerGenerator.generateAccessTokenHeader(bito); - - ParliamentaryTableResponse response = given() - .contentType(ContentType.JSON) - .pathParam("tableId", bitoTable.getId()) - .headers(headers) - .body(renewTableRequest) - .when().put("/api/table/parliamentary/{tableId}") - .then().statusCode(200) - .extract().as(ParliamentaryTableResponse.class); - - assertAll( - () -> assertThat(response.id()).isEqualTo(bitoTable.getId()), - () -> assertThat(response.info().name()).isEqualTo(renewTableRequest.info().name()), - () -> assertThat(response.table()).hasSize(renewTableRequest.table().size()) - ); - } - } - - @Nested - class Debate { - - @Test - void 의회식_토론을_진행한다() { - Member bito = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable bitoTable = parliamentaryTableGenerator.generate(bito); - parliamentaryTimeBoxGenerator.generate(bitoTable, 1); - parliamentaryTimeBoxGenerator.generateNotExistSpeaker(bitoTable, 2); - Headers headers = headerGenerator.generateAccessTokenHeader(bito); - - ParliamentaryTableResponse response = given() - .contentType(ContentType.JSON) - .pathParam("tableId", bitoTable.getId()) - .headers(headers) - .when().patch("/api/table/parliamentary/{tableId}/debate") - .then().statusCode(200) - .extract().as(ParliamentaryTableResponse.class); - - assertAll( - () -> assertThat(response.id()).isEqualTo(bitoTable.getId()), - () -> assertThat(response.info().name()).isEqualTo(bitoTable.getName()), - () -> assertThat(response.table()).hasSize(2) - ); - } - } - - @Nested - class DeleteTable { - - @Test - void 의회식_토론_테이블을_삭제한다() { - Member bito = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable bitoTable = parliamentaryTableGenerator.generate(bito); - parliamentaryTimeBoxGenerator.generate(bitoTable, 1); - parliamentaryTimeBoxGenerator.generateNotExistSpeaker(bitoTable, 2); - Headers headers = headerGenerator.generateAccessTokenHeader(bito); - - given() - .contentType(ContentType.JSON) - .pathParam("tableId", bitoTable.getId()) - .headers(headers) - .when().delete("/api/table/parliamentary/{tableId}") - .then().statusCode(204); - } - } -} diff --git a/src/test/java/com/debatetimer/controller/parliamentary/ParliamentaryDocumentTest.java b/src/test/java/com/debatetimer/controller/parliamentary/ParliamentaryDocumentTest.java deleted file mode 100644 index 0e2ef73b..00000000 --- a/src/test/java/com/debatetimer/controller/parliamentary/ParliamentaryDocumentTest.java +++ /dev/null @@ -1,463 +0,0 @@ -package com.debatetimer.controller.parliamentary; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.payload.JsonFieldType.ARRAY; -import static org.springframework.restdocs.payload.JsonFieldType.BOOLEAN; -import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; -import static org.springframework.restdocs.payload.JsonFieldType.OBJECT; -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.Stance; -import com.debatetimer.domain.parliamentary.ParliamentaryBoxType; -import com.debatetimer.dto.member.TableType; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTableCreateRequest; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTableInfoCreateRequest; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTimeBoxCreateRequest; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTableInfoResponse; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTableResponse; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTimeBoxResponse; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import io.restassured.http.ContentType; -import java.util.List; -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; -import org.springframework.http.HttpHeaders; - -public class ParliamentaryDocumentTest extends BaseDocumentTest { - - @Nested - class Save { - - private final RestDocumentationRequest requestDocument = request() - .tag(Tag.PARLIAMENTARY_API) - .summary("새로운 의회식 토론 시간표 생성") - .requestHeader( - headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") - ) - .requestBodyField( - fieldWithPath("info").type(OBJECT).description("토론 테이블 정보"), - fieldWithPath("info.name").type(STRING).description("테이블 이름"), - fieldWithPath("info.agenda").type(STRING).description("토론 주제"), - fieldWithPath("info.warningBell").type(BOOLEAN).description("30초 종소리 유무"), - fieldWithPath("info.finishBell").type(BOOLEAN).description("발언 종료 종소리 유무"), - fieldWithPath("table").type(ARRAY).description("토론 테이블 구성"), - fieldWithPath("table[].stance").type(STRING).description("입장"), - fieldWithPath("table[].type").type(STRING).description("발언 유형"), - fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)"), - fieldWithPath("table[].speakerNumber").type(NUMBER).description("발언자 번호").optional() - ); - - private final RestDocumentationResponse responseDocument = response() - .responseBodyField( - fieldWithPath("id").type(NUMBER).description("테이블 ID"), - fieldWithPath("info").type(OBJECT).description("토론 테이블 정보"), - fieldWithPath("info.name").type(STRING).description("테이블 이름"), - fieldWithPath("info.type").type(STRING).description("토론 형식"), - fieldWithPath("info.agenda").type(STRING).description("토론 주제"), - fieldWithPath("info.warningBell").type(BOOLEAN).description("30초 종소리 유무"), - fieldWithPath("info.finishBell").type(BOOLEAN).description("발언 종료 종소리 유무"), - fieldWithPath("table").type(ARRAY).description("토론 테이블 구성"), - fieldWithPath("table[].stance").type(STRING).description("입장"), - fieldWithPath("table[].type").type(STRING).description("발언 유형"), - fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)"), - fieldWithPath("table[].speakerNumber").type(NUMBER).description("발언자 번호").optional() - ); - - @Test - void 의회식_테이블_생성_성공() { - ParliamentaryTableCreateRequest request = new ParliamentaryTableCreateRequest( - new ParliamentaryTableInfoCreateRequest("비토 테이블 1", "토론 주제", true, true), - List.of( - new ParliamentaryTimeBoxCreateRequest(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxCreateRequest(Stance.CONS, ParliamentaryBoxType.OPENING, 3, 1) - ) - ); - ParliamentaryTableResponse response = new ParliamentaryTableResponse( - 5L, - new ParliamentaryTableInfoResponse("비토 테이블 1", TableType.PARLIAMENTARY, "토론 주제", true, true), - List.of( - new ParliamentaryTimeBoxResponse(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxResponse(Stance.CONS, ParliamentaryBoxType.OPENING, 3, 1) - ) - ); - doReturn(response).when(parliamentaryService).save(eq(request), any()); - - var document = document("parliamentary/post", 201) - .request(requestDocument) - .response(responseDocument) - .build(); - - given(document) - .contentType(ContentType.JSON) - .headers(EXIST_MEMBER_HEADER) - .body(request) - .when().post("/api/table/parliamentary") - .then().statusCode(201); - } - - @EnumSource( - value = ClientErrorCode.class, - names = { - "INVALID_TABLE_NAME_LENGTH", - "INVALID_TABLE_NAME_FORM", - "INVALID_TABLE_TIME", - "INVALID_TIME_BOX_SEQUENCE", - "INVALID_TIME_BOX_SPEAKER", - "INVALID_TIME_BOX_TIME", - "INVALID_TIME_BOX_STANCE", - "INVALID_TIME_BOX_FORMAT" - } - ) - @ParameterizedTest - void 의회식_테이블_생성_실패(ClientErrorCode errorCode) { - ParliamentaryTableCreateRequest request = new ParliamentaryTableCreateRequest( - new ParliamentaryTableInfoCreateRequest("비토 테이블 1", "토론 주제", true, true), - List.of( - new ParliamentaryTimeBoxCreateRequest(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxCreateRequest(Stance.CONS, ParliamentaryBoxType.OPENING, 3, 1) - ) - ); - doThrow(new DTClientErrorException(errorCode)).when(parliamentaryService).save(eq(request), any()); - - var document = document("parliamentary/post", errorCode) - .request(requestDocument) - .response(ERROR_RESPONSE) - .build(); - - given(document) - .contentType(ContentType.JSON) - .headers(EXIST_MEMBER_HEADER) - .body(request) - .when().post("/api/table/parliamentary") - .then().statusCode(errorCode.getStatus().value()); - } - } - - @Nested - class GetTable { - - private final RestDocumentationRequest requestDocument = request() - .summary("의회식 토론 시간표 조회") - .tag(Tag.PARLIAMENTARY_API) - .requestHeader( - headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") - ) - .pathParameter( - parameterWithName("tableId").description("테이블 ID") - ); - - private final RestDocumentationResponse responseDocument = response() - .responseBodyField( - fieldWithPath("id").type(NUMBER).description("테이블 ID"), - fieldWithPath("info").type(OBJECT).description("토론 테이블 정보"), - fieldWithPath("info.name").type(STRING).description("테이블 이름"), - fieldWithPath("info.type").type(STRING).description("토론 형식"), - fieldWithPath("info.agenda").type(STRING).description("토론 주제"), - fieldWithPath("info.warningBell").type(BOOLEAN).description("30초 종소리 유무"), - fieldWithPath("info.finishBell").type(BOOLEAN).description("발언 종료 종소리 유무"), - fieldWithPath("table").type(ARRAY).description("토론 테이블 구성"), - fieldWithPath("table[].stance").type(STRING).description("입장"), - fieldWithPath("table[].type").type(STRING).description("발언 유형"), - fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)"), - fieldWithPath("table[].speakerNumber").type(NUMBER).description("발언자 번호").optional() - ); - - @Test - void 의회식_테이블_조회_성공() { - long tableId = 5L; - ParliamentaryTableResponse response = new ParliamentaryTableResponse( - 5L, - new ParliamentaryTableInfoResponse("비토 테이블 1", TableType.PARLIAMENTARY, "토론 주제", true, true), - List.of( - new ParliamentaryTimeBoxResponse(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxResponse(Stance.CONS, ParliamentaryBoxType.OPENING, 3, 1) - ) - ); - doReturn(response).when(parliamentaryService).findTable(eq(tableId), any()); - - var document = document("parliamentary/get", 200) - .request(requestDocument) - .response(responseDocument) - .build(); - - given(document) - .contentType(ContentType.JSON) - .headers(EXIST_MEMBER_HEADER) - .pathParam("tableId", tableId) - .when().get("/api/table/parliamentary/{tableId}") - .then().statusCode(200); - } - - @ParameterizedTest - @EnumSource(value = ClientErrorCode.class, names = {"TABLE_NOT_FOUND", "NOT_TABLE_OWNER"}) - void 의회식_테이블_조회_실패(ClientErrorCode errorCode) { - long tableId = 5L; - doThrow(new DTClientErrorException(errorCode)).when(parliamentaryService).findTable(eq(tableId), any()); - - var document = document("parliamentary/get", errorCode) - .request(requestDocument) - .response(ERROR_RESPONSE) - .build(); - - given(document) - .contentType(ContentType.JSON) - .headers(EXIST_MEMBER_HEADER) - .pathParam("tableId", tableId) - .when().get("/api/table/parliamentary/{tableId}") - .then().statusCode(errorCode.getStatus().value()); - } - } - - @Nested - class UpdateTable { - - private final RestDocumentationRequest requestDocument = request() - .tag(Tag.PARLIAMENTARY_API) - .summary("의회식 토론 시간표 수정") - .requestHeader( - headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") - ) - .pathParameter( - parameterWithName("tableId").description("테이블 ID") - ) - .requestBodyField( - fieldWithPath("info").type(OBJECT).description("토론 테이블 정보"), - fieldWithPath("info.name").type(STRING).description("테이블 이름"), - fieldWithPath("info.agenda").type(STRING).description("토론 주제"), - fieldWithPath("info.warningBell").type(BOOLEAN).description("30초 종소리 유무"), - fieldWithPath("info.finishBell").type(BOOLEAN).description("발언 종료 종소리 유무"), - fieldWithPath("table").type(ARRAY).description("토론 테이블 구성"), - fieldWithPath("table[].stance").type(STRING).description("입장"), - fieldWithPath("table[].type").type(STRING).description("발언 유형"), - fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)"), - fieldWithPath("table[].speakerNumber").type(NUMBER).description("발언자 번호").optional() - ); - - private final RestDocumentationResponse responseDocument = response() - .responseBodyField( - fieldWithPath("id").type(NUMBER).description("테이블 ID"), - fieldWithPath("info").type(OBJECT).description("토론 테이블 정보"), - fieldWithPath("info.name").type(STRING).description("테이블 이름"), - fieldWithPath("info.type").type(STRING).description("토론 형식"), - fieldWithPath("info.agenda").type(STRING).description("토론 주제"), - fieldWithPath("info.warningBell").type(BOOLEAN).description("30초 종소리 유무"), - fieldWithPath("info.finishBell").type(BOOLEAN).description("발언 종료 종소리 유무"), - fieldWithPath("table").type(ARRAY).description("토론 테이블 구성"), - fieldWithPath("table[].stance").type(STRING).description("입장"), - fieldWithPath("table[].type").type(STRING).description("발언 유형"), - fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)"), - fieldWithPath("table[].speakerNumber").type(NUMBER).description("발언자 번호").optional() - ); - - @Test - void 의회식_토론_테이블_수정() { - long tableId = 5L; - ParliamentaryTableCreateRequest request = new ParliamentaryTableCreateRequest( - new ParliamentaryTableInfoCreateRequest("비토 테이블 2", "토론 주제 2", true, true), - List.of( - new ParliamentaryTimeBoxCreateRequest(Stance.PROS, ParliamentaryBoxType.OPENING, 300, 1), - new ParliamentaryTimeBoxCreateRequest(Stance.CONS, ParliamentaryBoxType.OPENING, 300, 1) - ) - ); - ParliamentaryTableResponse response = new ParliamentaryTableResponse( - 5L, - new ParliamentaryTableInfoResponse("비토 테이블 2", TableType.PARLIAMENTARY, "토론 주제 2", true, true), - List.of( - new ParliamentaryTimeBoxResponse(Stance.PROS, ParliamentaryBoxType.OPENING, 300, 1), - new ParliamentaryTimeBoxResponse(Stance.CONS, ParliamentaryBoxType.OPENING, 300, 1) - ) - ); - doReturn(response).when(parliamentaryService).updateTable(eq(request), eq(tableId), any()); - - var document = document("parliamentary/put", 200) - .request(requestDocument) - .response(responseDocument) - .build(); - - given(document) - .contentType(ContentType.JSON) - .headers(EXIST_MEMBER_HEADER) - .pathParam("tableId", tableId) - .body(request) - .when().put("/api/table/parliamentary/{tableId}") - .then().statusCode(200); - } - - @EnumSource( - value = ClientErrorCode.class, - names = { - "INVALID_TABLE_NAME_LENGTH", - "INVALID_TABLE_NAME_FORM", - "INVALID_TABLE_TIME", - "INVALID_TIME_BOX_SEQUENCE", - "INVALID_TIME_BOX_SPEAKER", - "INVALID_TIME_BOX_TIME", - "INVALID_TIME_BOX_STANCE", - "INVALID_TIME_BOX_FORMAT" - } - ) - @ParameterizedTest - void 의회식_테이블_수정_실패(ClientErrorCode errorCode) { - long tableId = 5L; - ParliamentaryTableCreateRequest request = new ParliamentaryTableCreateRequest( - new ParliamentaryTableInfoCreateRequest("비토 테이블 2", "토론 주제 2", true, true), - List.of( - new ParliamentaryTimeBoxCreateRequest(Stance.PROS, ParliamentaryBoxType.OPENING, 300, 1), - new ParliamentaryTimeBoxCreateRequest(Stance.CONS, ParliamentaryBoxType.OPENING, 300, 1) - ) - ); - doThrow(new DTClientErrorException(errorCode)).when(parliamentaryService) - .updateTable(eq(request), eq(tableId), any()); - - var document = document("parliamentary/put", errorCode) - .request(requestDocument) - .response(ERROR_RESPONSE) - .build(); - - given(document) - .contentType(ContentType.JSON) - .headers(EXIST_MEMBER_HEADER) - .pathParam("tableId", tableId) - .body(request) - .when().put("/api/table/parliamentary/{tableId}") - .then().statusCode(errorCode.getStatus().value()); - } - } - - @Nested - class Debate { - - private final RestDocumentationRequest requestDocument = request() - .summary("의회식 토론 진행") - .tag(Tag.PARLIAMENTARY_API) - .requestHeader( - headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") - ) - .pathParameter( - parameterWithName("tableId").description("테이블 ID") - ); - - private final RestDocumentationResponse responseDocument = response() - .responseBodyField( - fieldWithPath("id").type(NUMBER).description("테이블 ID"), - fieldWithPath("info").type(OBJECT).description("토론 테이블 정보"), - fieldWithPath("info.name").type(STRING).description("테이블 이름"), - fieldWithPath("info.type").type(STRING).description("토론 형식"), - fieldWithPath("info.agenda").type(STRING).description("토론 주제"), - fieldWithPath("info.warningBell").type(BOOLEAN).description("30초 종소리 유무"), - fieldWithPath("info.finishBell").type(BOOLEAN).description("발언 종료 종소리 유무"), - fieldWithPath("table").type(ARRAY).description("토론 테이블 구성"), - fieldWithPath("table[].stance").type(STRING).description("입장"), - fieldWithPath("table[].type").type(STRING).description("발언 유형"), - fieldWithPath("table[].time").type(NUMBER).description("발언 시간(초)"), - fieldWithPath("table[].speakerNumber").type(NUMBER).description("발언자 번호").optional() - ); - - @Test - void 의회식_토론_진행_성공() { - long tableId = 5L; - ParliamentaryTableResponse response = new ParliamentaryTableResponse( - 5L, - new ParliamentaryTableInfoResponse("비토 테이블 1", TableType.PARLIAMENTARY, "토론 주제", true, true), - List.of( - new ParliamentaryTimeBoxResponse(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxResponse(Stance.CONS, ParliamentaryBoxType.OPENING, 3, 1) - ) - ); - doReturn(response).when(parliamentaryService).updateUsedAt(eq(tableId), any()); - - var document = document("parliamentary/patch_debate", 200) - .request(requestDocument) - .response(responseDocument) - .build(); - - given(document) - .contentType(ContentType.JSON) - .headers(EXIST_MEMBER_HEADER) - .pathParam("tableId", tableId) - .when().patch("/api/table/parliamentary/{tableId}/debate") - .then().statusCode(200); - } - - @ParameterizedTest - @EnumSource(value = ClientErrorCode.class, names = {"TABLE_NOT_FOUND", "NOT_TABLE_OWNER"}) - void 의회식_토론_진행_실패(ClientErrorCode errorCode) { - long tableId = 5L; - doThrow(new DTClientErrorException(errorCode)).when(parliamentaryService).updateUsedAt(eq(tableId), any()); - - var document = document("parliamentary/get", errorCode) - .request(requestDocument) - .response(ERROR_RESPONSE) - .build(); - - given(document) - .contentType(ContentType.JSON) - .headers(EXIST_MEMBER_HEADER) - .pathParam("tableId", tableId) - .when().patch("/api/table/parliamentary/{tableId}/debate") - .then().statusCode(errorCode.getStatus().value()); - } - } - - @Nested - class DeleteTable { - - private final RestDocumentationRequest requestDocument = request() - .tag(Tag.PARLIAMENTARY_API) - .summary("의회식 토론 시간표 삭제") - .requestHeader( - headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") - ) - .pathParameter( - parameterWithName("tableId").description("테이블 ID") - ); - - @Test - void 의회식_테이블_삭제_성공() { - long tableId = 5L; - doNothing().when(parliamentaryService).deleteTable(eq(tableId), any()); - - var document = document("parliamentary/delete", 204) - .request(requestDocument) - .build(); - - given(document) - .headers(EXIST_MEMBER_HEADER) - .pathParam("tableId", tableId) - .when().delete("/api/table/parliamentary/{tableId}") - .then().statusCode(204); - } - - @EnumSource(value = ClientErrorCode.class, names = {"TABLE_NOT_FOUND", "NOT_TABLE_OWNER"}) - @ParameterizedTest - void 의회식_테이블_삭제_실패(ClientErrorCode errorCode) { - long tableId = 5L; - doThrow(new DTClientErrorException(errorCode)).when(parliamentaryService).deleteTable(eq(tableId), any()); - - var document = document("parliamentary/delete", errorCode) - .request(requestDocument) - .response(ERROR_RESPONSE) - .build(); - - given(document) - .headers(EXIST_MEMBER_HEADER) - .pathParam("tableId", tableId) - .when().delete("/api/table/parliamentary/{tableId}") - .then().statusCode(errorCode.getStatus().value()); - } - } -} diff --git a/src/test/java/com/debatetimer/domain/TimeBoxesTest.java b/src/test/java/com/debatetimer/domain/TimeBoxesTest.java index c3a80805..47602c63 100644 --- a/src/test/java/com/debatetimer/domain/TimeBoxesTest.java +++ b/src/test/java/com/debatetimer/domain/TimeBoxesTest.java @@ -2,10 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; +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.domain.parliamentary.ParliamentaryBoxType; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.domain.parliamentary.ParliamentaryTimeBox; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -20,14 +20,15 @@ class SortedBySequence { @Test void 타임박스의_순서에_따라_정렬된다() { Member member = new Member("default@gmail.com"); - ParliamentaryTable testTable = new ParliamentaryTable(member, "토론 테이블", "주제", true, true); - ParliamentaryTimeBox firstBox = new ParliamentaryTimeBox(testTable, 1, Stance.PROS, - ParliamentaryBoxType.OPENING, 300, 1); - ParliamentaryTimeBox secondBox = new ParliamentaryTimeBox(testTable, 2, Stance.PROS, - ParliamentaryBoxType.OPENING, 300, 1); - List timeBoxes = new ArrayList<>(Arrays.asList(secondBox, firstBox)); + CustomizeTable testTable = new CustomizeTable(member, "토론 테이블", "주제", + true, true, "찬성", "반대"); + CustomizeTimeBox firstBox = new CustomizeTimeBox(testTable, 1, Stance.PROS, "입론", + CustomizeBoxType.NORMAL, 300, "콜리"); + CustomizeTimeBox secondBox = new CustomizeTimeBox(testTable, 2, Stance.PROS, "입론", + CustomizeBoxType.NORMAL, 300, "콜리2"); + List timeBoxes = new ArrayList<>(Arrays.asList(secondBox, firstBox)); - TimeBoxes actual = new TimeBoxes<>(timeBoxes); + TimeBoxes actual = new TimeBoxes<>(timeBoxes); assertThat(actual.getTimeBoxes()).containsExactly(firstBox, secondBox); } diff --git a/src/test/java/com/debatetimer/domain/parliamentary/ParliamentaryTableTest.java b/src/test/java/com/debatetimer/domain/parliamentary/ParliamentaryTableTest.java deleted file mode 100644 index 1a16fc1d..00000000 --- a/src/test/java/com/debatetimer/domain/parliamentary/ParliamentaryTableTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.debatetimer.domain.parliamentary; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.debatetimer.dto.member.TableType; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -class ParliamentaryTableTest { - - @Nested - class GetType { - - @Test - void 의회식_테이블_타입을_반환한다() { - ParliamentaryTable parliamentaryTable = new ParliamentaryTable(); - - assertThat(parliamentaryTable.getType()).isEqualTo(TableType.PARLIAMENTARY); - } - } -} diff --git a/src/test/java/com/debatetimer/domain/parliamentary/ParliamentaryTimeBoxTest.java b/src/test/java/com/debatetimer/domain/parliamentary/ParliamentaryTimeBoxTest.java deleted file mode 100644 index dcd6cbb8..00000000 --- a/src/test/java/com/debatetimer/domain/parliamentary/ParliamentaryTimeBoxTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.debatetimer.domain.parliamentary; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.debatetimer.domain.Stance; -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; - -class ParliamentaryTimeBoxTest { - - @Nested - class ValidateStance { - - @Test - void 박스타입에_가능한_입장을_검증한다() { - ParliamentaryTable table = new ParliamentaryTable(); - assertThatCode(() -> new ParliamentaryTimeBox(table, 1, Stance.CONS, ParliamentaryBoxType.OPENING, 10, 1)) - .doesNotThrowAnyException(); - } - - @Test - void 박스타입에_불가한_입장으로_생성을_시도하면_예외를_발생시킨다() { - ParliamentaryTable table = new ParliamentaryTable(); - assertThatThrownBy( - () -> new ParliamentaryTimeBox(table, 1, Stance.NEUTRAL, ParliamentaryBoxType.OPENING, 10, 1)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_STANCE.getMessage()); - } - } - - @Nested - class ValidateSpeakerNumber { - - @ValueSource(ints = {0, -1}) - @ParameterizedTest - void 의회식_타임박스의_발표자_번호_음수는_불가능하다(int speakerNumber) { - ParliamentaryTable table = new ParliamentaryTable(); - - assertThatThrownBy( - () -> new ParliamentaryTimeBox(table, 1, Stance.PROS, ParliamentaryBoxType.OPENING, 10, speakerNumber)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEAKER.getMessage()); - } - } - - @Nested - class getSpeakerNumber { - - @ValueSource(ints = {1, 5}) - @ParameterizedTest - void 의회식_타임박스의_발표자_번호는_양수만_가능하다(int speakerNumber) { - ParliamentaryTable table = new ParliamentaryTable(); - ParliamentaryTimeBox timeBox = new ParliamentaryTimeBox(table, 1, Stance.PROS, ParliamentaryBoxType.OPENING, 10, speakerNumber); - - Integer actual = timeBox.getSpeakerNumber(); - - assertThat(actual).isEqualTo(speakerNumber); - } - - @Test - void 의회식_타임박스의_발표자는_비어있을_수_있다() { - ParliamentaryTable table = new ParliamentaryTable(); - Integer speakerNumber = null; - ParliamentaryTimeBox timeBox = new ParliamentaryTimeBox(table, 1, Stance.PROS, ParliamentaryBoxType.OPENING, 10, speakerNumber); - - Integer actual = timeBox.getSpeakerNumber(); - - assertThat(actual).isEqualTo(speakerNumber); - } - } -} diff --git a/src/test/java/com/debatetimer/fixture/ParliamentaryTableGenerator.java b/src/test/java/com/debatetimer/fixture/ParliamentaryTableGenerator.java deleted file mode 100644 index 698b370b..00000000 --- a/src/test/java/com/debatetimer/fixture/ParliamentaryTableGenerator.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.debatetimer.fixture; - -import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.repository.parliamentary.ParliamentaryTableRepository; -import org.springframework.stereotype.Component; - -@Component -public class ParliamentaryTableGenerator { - - private final ParliamentaryTableRepository parliamentaryTableRepository; - - public ParliamentaryTableGenerator(ParliamentaryTableRepository parliamentaryTableRepository) { - this.parliamentaryTableRepository = parliamentaryTableRepository; - } - - public ParliamentaryTable generate(Member member) { - ParliamentaryTable table = new ParliamentaryTable( - member, - "토론 테이블", - "주제", - false, - false - ); - return parliamentaryTableRepository.save(table); - } -} diff --git a/src/test/java/com/debatetimer/fixture/ParliamentaryTimeBoxGenerator.java b/src/test/java/com/debatetimer/fixture/ParliamentaryTimeBoxGenerator.java deleted file mode 100644 index 05107cd3..00000000 --- a/src/test/java/com/debatetimer/fixture/ParliamentaryTimeBoxGenerator.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.debatetimer.fixture; - -import com.debatetimer.domain.Stance; -import com.debatetimer.domain.parliamentary.ParliamentaryBoxType; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.domain.parliamentary.ParliamentaryTimeBox; -import com.debatetimer.repository.parliamentary.ParliamentaryTimeBoxRepository; -import org.springframework.stereotype.Component; - -@Component -public class ParliamentaryTimeBoxGenerator { - - private final ParliamentaryTimeBoxRepository parliamentaryTimeBoxRepository; - - public ParliamentaryTimeBoxGenerator(ParliamentaryTimeBoxRepository parliamentaryTimeBoxRepository) { - this.parliamentaryTimeBoxRepository = parliamentaryTimeBoxRepository; - } - - public ParliamentaryTimeBox generate(ParliamentaryTable testTable, int sequence) { - ParliamentaryTimeBox timeBox = new ParliamentaryTimeBox(testTable, sequence, Stance.PROS, - ParliamentaryBoxType.OPENING, 180, 1); - return parliamentaryTimeBoxRepository.save(timeBox); - } - - public ParliamentaryTimeBox generateNotExistSpeaker(ParliamentaryTable testTable, int sequence) { - ParliamentaryTimeBox timeBox = new ParliamentaryTimeBox(testTable, sequence, Stance.PROS, - ParliamentaryBoxType.OPENING, 180, null); - return parliamentaryTimeBoxRepository.save(timeBox); - } -} diff --git a/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java b/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java index 445e5384..338f7430 100644 --- a/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java @@ -4,8 +4,6 @@ import com.debatetimer.fixture.CustomizeTableGenerator; import com.debatetimer.fixture.CustomizeTimeBoxGenerator; import com.debatetimer.fixture.MemberGenerator; -import com.debatetimer.fixture.ParliamentaryTableGenerator; -import com.debatetimer.fixture.ParliamentaryTimeBoxGenerator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; @@ -13,8 +11,6 @@ @Import({ JpaAuditingConfig.class, MemberGenerator.class, - ParliamentaryTableGenerator.class, - ParliamentaryTimeBoxGenerator.class, CustomizeTableGenerator.class, CustomizeTimeBoxGenerator.class }) @@ -24,12 +20,6 @@ public abstract class BaseRepositoryTest { @Autowired protected MemberGenerator memberGenerator; - @Autowired - protected ParliamentaryTableGenerator parliamentaryTableGenerator; - - @Autowired - protected ParliamentaryTimeBoxGenerator parliamentaryTimeBoxGenerator; - @Autowired protected CustomizeTableGenerator customizeTableGenerator; diff --git a/src/test/java/com/debatetimer/repository/parliamentary/ParliamentaryTableRepositoryTest.java b/src/test/java/com/debatetimer/repository/parliamentary/ParliamentaryTableRepositoryTest.java deleted file mode 100644 index efce7327..00000000 --- a/src/test/java/com/debatetimer/repository/parliamentary/ParliamentaryTableRepositoryTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.debatetimer.repository.parliamentary; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import com.debatetimer.repository.BaseRepositoryTest; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -class ParliamentaryTableRepositoryTest extends BaseRepositoryTest { - - @Autowired - private ParliamentaryTableRepository tableRepository; - - @Nested - class FindAllByMember { - - @Test - void 특정_회원의_테이블만_조회한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - Member bito = memberGenerator.generate("default2@gmail.com"); - ParliamentaryTable chanTable1 = parliamentaryTableGenerator.generate(chan); - ParliamentaryTable chanTable2 = parliamentaryTableGenerator.generate(chan); - ParliamentaryTable bitoTable = parliamentaryTableGenerator.generate(bito); - - List foundKeoChanTables = tableRepository.findAllByMember(chan); - - assertThat(foundKeoChanTables).containsExactly(chanTable1, chanTable2); - } - } - - @Nested - class GetById { - - @Test - void 특정_아이디의_테이블을_조회한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable chanTable = parliamentaryTableGenerator.generate(chan); - - ParliamentaryTable foundChanTable = tableRepository.getById(chanTable.getId()); - - assertThat(foundChanTable).usingRecursiveComparison().isEqualTo(chanTable); - } - - @Test - void 특정_아이디의_테이블이_없으면_에러를_발생시킨다() { - assertThatThrownBy(() -> tableRepository.getById(1L)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); - } - } -} diff --git a/src/test/java/com/debatetimer/repository/parliamentary/ParliamentaryTimeBoxRepositoryTest.java b/src/test/java/com/debatetimer/repository/parliamentary/ParliamentaryTimeBoxRepositoryTest.java deleted file mode 100644 index c412db5c..00000000 --- a/src/test/java/com/debatetimer/repository/parliamentary/ParliamentaryTimeBoxRepositoryTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.debatetimer.repository.parliamentary; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.domain.parliamentary.ParliamentaryTimeBox; -import com.debatetimer.repository.BaseRepositoryTest; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -class ParliamentaryTimeBoxRepositoryTest extends BaseRepositoryTest { - - @Autowired - private ParliamentaryTimeBoxRepository parliamentaryTimeBoxRepository; - - @Nested - class FindAllByParliamentaryTable { - - @Test - void 특정_테이블의_타임박스를_모두_조회한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - Member bito = memberGenerator.generate("default2@gmail.com"); - ParliamentaryTable chanTable = parliamentaryTableGenerator.generate(chan); - ParliamentaryTable bitoTable = parliamentaryTableGenerator.generate(bito); - ParliamentaryTimeBox chanBox1 = parliamentaryTimeBoxGenerator.generate(chanTable, 1); - ParliamentaryTimeBox chanBox2 = parliamentaryTimeBoxGenerator.generate(chanTable, 2); - ParliamentaryTimeBox bitoBox1 = parliamentaryTimeBoxGenerator.generate(bitoTable, 2); - ParliamentaryTimeBox bitoBox2 = parliamentaryTimeBoxGenerator.generate(bitoTable, 2); - - List foundBoxes = parliamentaryTimeBoxRepository.findAllByParliamentaryTable( - chanTable); - - assertThat(foundBoxes).containsExactly(chanBox1, chanBox2); - } - } -} diff --git a/src/test/java/com/debatetimer/service/BaseServiceTest.java b/src/test/java/com/debatetimer/service/BaseServiceTest.java index 65d88130..f512da4e 100644 --- a/src/test/java/com/debatetimer/service/BaseServiceTest.java +++ b/src/test/java/com/debatetimer/service/BaseServiceTest.java @@ -4,13 +4,9 @@ import com.debatetimer.fixture.CustomizeTableGenerator; import com.debatetimer.fixture.CustomizeTimeBoxGenerator; import com.debatetimer.fixture.MemberGenerator; -import com.debatetimer.fixture.ParliamentaryTableGenerator; -import com.debatetimer.fixture.ParliamentaryTimeBoxGenerator; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import com.debatetimer.repository.member.MemberRepository; -import com.debatetimer.repository.parliamentary.ParliamentaryTableRepository; -import com.debatetimer.repository.parliamentary.ParliamentaryTimeBoxRepository; import java.util.List; import java.util.stream.IntStream; import org.junit.jupiter.api.extension.ExtendWith; @@ -24,12 +20,6 @@ public abstract class BaseServiceTest { @Autowired protected MemberRepository memberRepository; - @Autowired - protected ParliamentaryTableRepository parliamentaryTableRepository; - - @Autowired - protected ParliamentaryTimeBoxRepository parliamentaryTimeBoxRepository; - @Autowired protected CustomizeTableRepository customizeTableRepository; @@ -39,12 +29,6 @@ public abstract class BaseServiceTest { @Autowired protected MemberGenerator memberGenerator; - @Autowired - protected ParliamentaryTableGenerator parliamentaryTableGenerator; - - @Autowired - protected ParliamentaryTimeBoxGenerator parliamentaryTimeBoxGenerator; - @Autowired protected CustomizeTableGenerator customizeTableGenerator; diff --git a/src/test/java/com/debatetimer/service/member/MemberServiceTest.java b/src/test/java/com/debatetimer/service/member/MemberServiceTest.java index 17c6000c..1777798b 100644 --- a/src/test/java/com/debatetimer/service/member/MemberServiceTest.java +++ b/src/test/java/com/debatetimer/service/member/MemberServiceTest.java @@ -5,7 +5,6 @@ import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; import com.debatetimer.dto.member.MemberCreateResponse; import com.debatetimer.dto.member.MemberInfo; import com.debatetimer.dto.member.TableResponses; @@ -53,7 +52,7 @@ class GetTables { @Test void 회원의_전체_토론_시간표를_조회한다() { Member member = memberGenerator.generate("default@gmail.com"); - parliamentaryTableGenerator.generate(member); + customizeTableGenerator.generate(member); customizeTableGenerator.generate(member); TableResponses response = memberService.getTables(member.getId()); @@ -64,16 +63,13 @@ class GetTables { @Test void 회원의_전체_토론_시간표는_정해진_순서대로_반환한다() throws InterruptedException { Member member = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable table1 = parliamentaryTableGenerator.generate(member); - CustomizeTable table2 = customizeTableGenerator.generate(member); + CustomizeTable table = customizeTableGenerator.generate(member); Thread.sleep(1); - table1.updateUsedAt(); TableResponses response = memberService.getTables(member.getId()); assertAll( - () -> assertThat(response.tables().get(0).id()).isEqualTo(table1.getId()), - () -> assertThat(response.tables().get(1).id()).isEqualTo(table2.getId()) + () -> assertThat(response.tables().get(0).id()).isEqualTo(table.getId()) ); } } diff --git a/src/test/java/com/debatetimer/service/parliamentary/ParliamentaryServiceTest.java b/src/test/java/com/debatetimer/service/parliamentary/ParliamentaryServiceTest.java deleted file mode 100644 index 750b1b90..00000000 --- a/src/test/java/com/debatetimer/service/parliamentary/ParliamentaryServiceTest.java +++ /dev/null @@ -1,207 +0,0 @@ -package com.debatetimer.service.parliamentary; - -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.Stance; -import com.debatetimer.domain.member.Member; -import com.debatetimer.domain.parliamentary.ParliamentaryBoxType; -import com.debatetimer.domain.parliamentary.ParliamentaryTable; -import com.debatetimer.domain.parliamentary.ParliamentaryTimeBox; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTableCreateRequest; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTableInfoCreateRequest; -import com.debatetimer.dto.parliamentary.request.ParliamentaryTimeBoxCreateRequest; -import com.debatetimer.dto.parliamentary.response.ParliamentaryTableResponse; -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 ParliamentaryServiceTest extends BaseServiceTest { - - @Autowired - private ParliamentaryService parliamentaryService; - - @Nested - class Save { - - @Test - void 의회식_토론_테이블을_생성한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - ParliamentaryTableCreateRequest chanTableRequest = new ParliamentaryTableCreateRequest( - new ParliamentaryTableInfoCreateRequest("커찬의 테이블", "주제", true, true), - List.of(new ParliamentaryTimeBoxCreateRequest(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxCreateRequest(Stance.CONS, ParliamentaryBoxType.OPENING, 3, 1))); - - ParliamentaryTableResponse savedTableResponse = parliamentaryService.save(chanTableRequest, chan); - Optional foundTable = parliamentaryTableRepository.findById(savedTableResponse.id()); - List foundTimeBoxes = parliamentaryTimeBoxRepository.findAllByParliamentaryTable( - foundTable.get()); - - assertAll( - () -> assertThat(foundTable.get().getName()).isEqualTo(chanTableRequest.info().name()), - () -> assertThat(foundTimeBoxes).hasSize(chanTableRequest.table().size()) - ); - } - } - - @Nested - class FindTable { - - @Test - void 의회식_토론_테이블을_조회한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable chanTable = parliamentaryTableGenerator.generate(chan); - parliamentaryTimeBoxGenerator.generate(chanTable, 1); - parliamentaryTimeBoxGenerator.generate(chanTable, 2); - - ParliamentaryTableResponse foundResponse = parliamentaryService.findTable(chanTable.getId(), chan); - - assertAll( - () -> assertThat(foundResponse.id()).isEqualTo(chanTable.getId()), - () -> assertThat(foundResponse.table()).hasSize(2) - ); - } - - @Test - void 회원_소유가_아닌_테이블_조회_시_예외를_발생시킨다() { - Member chan = memberGenerator.generate("default@gmail.com"); - Member coli = memberGenerator.generate("default2@gmail.com"); - ParliamentaryTable chanTable = parliamentaryTableGenerator.generate(chan); - long chanTableId = chanTable.getId(); - - assertThatThrownBy(() -> parliamentaryService.findTable(chanTableId, coli)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.NOT_TABLE_OWNER.getMessage()); - } - } - - @Nested - class UpdateTable { - - @Test - void 의회식_토론_테이블을_수정한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable chanTable = parliamentaryTableGenerator.generate(chan); - ParliamentaryTableCreateRequest renewTableRequest = new ParliamentaryTableCreateRequest( - new ParliamentaryTableInfoCreateRequest("커찬의 테이블", "주제", true, true), - List.of(new ParliamentaryTimeBoxCreateRequest(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxCreateRequest(Stance.CONS, ParliamentaryBoxType.OPENING, 3, 1))); - - parliamentaryService.updateTable(renewTableRequest, chanTable.getId(), chan); - - Optional updatedTable = parliamentaryTableRepository.findById(chanTable.getId()); - List updatedTimeBoxes = parliamentaryTimeBoxRepository.findAllByParliamentaryTable( - updatedTable.get()); - - assertAll( - () -> assertThat(updatedTable.get().getId()).isEqualTo(chanTable.getId()), - () -> assertThat(updatedTable.get().getName()).isEqualTo(renewTableRequest.info().name()), - () -> assertThat(updatedTimeBoxes).hasSize(renewTableRequest.table().size()) - ); - } - - @Test - void 회원_소유가_아닌_테이블_수정_시_예외를_발생시킨다() { - Member chan = memberGenerator.generate("default@gmail.com"); - Member coli = memberGenerator.generate("default2@gmail.com"); - ParliamentaryTable chanTable = parliamentaryTableGenerator.generate(chan); - long chanTableId = chanTable.getId(); - ParliamentaryTableCreateRequest renewTableRequest = new ParliamentaryTableCreateRequest( - new ParliamentaryTableInfoCreateRequest("새로운 테이블", "주제", true, true), - List.of(new ParliamentaryTimeBoxCreateRequest(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxCreateRequest(Stance.CONS, ParliamentaryBoxType.OPENING, 3, 1))); - - assertThatThrownBy(() -> parliamentaryService.updateTable(renewTableRequest, chanTableId, coli)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.NOT_TABLE_OWNER.getMessage()); - } - - @Test - void 테이블_정보_수정을_동시에_요청할_때_동시에_처리하지_않는다() throws InterruptedException { - Member member = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable table = parliamentaryTableGenerator.generate(member); - ParliamentaryTableCreateRequest request = new ParliamentaryTableCreateRequest( - new ParliamentaryTableInfoCreateRequest("커찬의 테이블", "주제", true, true), - List.of(new ParliamentaryTimeBoxCreateRequest(Stance.PROS, ParliamentaryBoxType.OPENING, 3, 1), - new ParliamentaryTimeBoxCreateRequest(Stance.CONS, ParliamentaryBoxType.OPENING, 3, 1))); - - runAtSameTime(2, () -> parliamentaryService.updateTable(request, member.getId(), member)); - - assertThat(parliamentaryTimeBoxRepository.findAllByParliamentaryTable(table)).hasSize(2); - } - } - - @Nested - class UpdateUsedAt { - - @Test - void 의회식_토론_테이블의_사용_시각을_최신화한다() { - Member member = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable table = parliamentaryTableGenerator.generate(member); - LocalDateTime beforeUsedAt = table.getUsedAt(); - - parliamentaryService.updateUsedAt(table.getId(), member); - - Optional updatedTable = parliamentaryTableRepository.findById(table.getId()); - - assertAll( - () -> assertThat(updatedTable.get().getId()).isEqualTo(table.getId()), - () -> assertThat(updatedTable.get().getUsedAt()).isAfter(beforeUsedAt) - ); - } - - @Test - void 회원_소유가_아닌_테이블_수정_시_예외를_발생시킨다() { - Member chan = memberGenerator.generate("default@gmail.com"); - Member coli = memberGenerator.generate("default2@gmail.com"); - ParliamentaryTable chanTable = parliamentaryTableGenerator.generate(chan); - long chanTableId = chanTable.getId(); - - assertThatThrownBy(() -> parliamentaryService.updateUsedAt(chanTableId, coli)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.NOT_TABLE_OWNER.getMessage()); - } - } - - @Nested - class DeleteTable { - - @Test - void 의회식_토론_테이블을_삭제한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - ParliamentaryTable chanTable = parliamentaryTableGenerator.generate(chan); - parliamentaryTimeBoxGenerator.generate(chanTable, 1); - parliamentaryTimeBoxGenerator.generate(chanTable, 2); - - parliamentaryService.deleteTable(chanTable.getId(), chan); - - Optional foundTable = parliamentaryTableRepository.findById(chanTable.getId()); - List timeBoxes = parliamentaryTimeBoxRepository.findAllByParliamentaryTable( - chanTable); - - assertAll( - () -> assertThat(foundTable).isEmpty(), - () -> assertThat(timeBoxes).isEmpty() - ); - } - - @Test - void 회원_소유가_아닌_테이블_삭제_시_예외를_발생시킨다() { - Member chan = memberGenerator.generate("default@gmail.com"); - Member coli = memberGenerator.generate("default2@gmail.com"); - ParliamentaryTable chanTable = parliamentaryTableGenerator.generate(chan); - long chanTableId = chanTable.getId(); - - assertThatThrownBy(() -> parliamentaryService.deleteTable(chanTableId, coli)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.NOT_TABLE_OWNER.getMessage()); - } - } -} diff --git a/src/test/java/com/debatetimer/view/exporter/ParliamentaryBoxTypeViewTest.java b/src/test/java/com/debatetimer/view/exporter/ParliamentaryBoxTypeViewTest.java deleted file mode 100644 index b8035a86..00000000 --- a/src/test/java/com/debatetimer/view/exporter/ParliamentaryBoxTypeViewTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.debatetimer.view.exporter; - -import static org.assertj.core.api.Assertions.assertThatCode; - -import com.debatetimer.domain.parliamentary.ParliamentaryBoxType; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; - -class ParliamentaryBoxTypeViewTest { - - @Nested - class MapView { - - @ParameterizedTest - @EnumSource(value = ParliamentaryBoxType.class) - void 타임박스_타입과_일치하는_메시지를_반환한다(ParliamentaryBoxType boxType) { - assertThatCode(() -> ParliamentaryBoxTypeView.mapView(boxType)) - .doesNotThrowAnyException(); - } - } -} From 09d22f6b77cc0bb3170e0228b4a4fe0d57736cf5 Mon Sep 17 00:00:00 2001 From: SANGHUN OH <121424793+unifolio0@users.noreply.github.com> Date: Sun, 18 May 2025 10:51:34 +0900 Subject: [PATCH 03/27] [TEST] DTO Null, Blank E2E Test (#174) --- build.gradle | 1 + .../aop/logging/ClientLoggingAspect.java | 1 - .../aop/logging/LoggingClient.java | 1 + .../client/notifier/DiscordNotifier.java | 3 +- .../debatetimer/config/NotifierConfig.java | 2 + .../controller/auth/AuthMember.java | 1 + .../controller/member/MemberController.java | 3 +- .../controller/BaseControllerTest.java | 39 +++ .../customize/CustomizeControllerTest.java | 296 +++++++++++++++--- .../member/MemberControllerTest.java | 40 ++- .../debatetimer/domain/DebateTableTest.java | 4 +- .../fixture/BlankArgumentsProvider.java | 14 + .../fixture/NullAndEmptyAndBlankSource.java | 16 + .../CustomizeTableRepositoryTest.java | 2 +- .../CustomizeTimeBoxRepositoryTest.java | 4 +- 15 files changed, 378 insertions(+), 49 deletions(-) create mode 100644 src/test/java/com/debatetimer/fixture/BlankArgumentsProvider.java create mode 100644 src/test/java/com/debatetimer/fixture/NullAndEmptyAndBlankSource.java diff --git a/build.gradle b/build.gradle index c35390a3..a24fcd40 100644 --- a/build.gradle +++ b/build.gradle @@ -68,6 +68,7 @@ dependencies { // Test testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter:1.1.11") // Rest Docs & Swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' diff --git a/src/main/java/com/debatetimer/aop/logging/ClientLoggingAspect.java b/src/main/java/com/debatetimer/aop/logging/ClientLoggingAspect.java index 5bf587c8..224e18db 100644 --- a/src/main/java/com/debatetimer/aop/logging/ClientLoggingAspect.java +++ b/src/main/java/com/debatetimer/aop/logging/ClientLoggingAspect.java @@ -14,7 +14,6 @@ @Component public class ClientLoggingAspect extends LoggingAspect { - @Pointcut("@within(com.debatetimer.aop.logging.LoggingClient)") public void loggingClients() { } diff --git a/src/main/java/com/debatetimer/aop/logging/LoggingClient.java b/src/main/java/com/debatetimer/aop/logging/LoggingClient.java index 0fc01034..93fdbfae 100644 --- a/src/main/java/com/debatetimer/aop/logging/LoggingClient.java +++ b/src/main/java/com/debatetimer/aop/logging/LoggingClient.java @@ -8,4 +8,5 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface LoggingClient { + } diff --git a/src/main/java/com/debatetimer/client/notifier/DiscordNotifier.java b/src/main/java/com/debatetimer/client/notifier/DiscordNotifier.java index c4bb1529..5f5c952f 100644 --- a/src/main/java/com/debatetimer/client/notifier/DiscordNotifier.java +++ b/src/main/java/com/debatetimer/client/notifier/DiscordNotifier.java @@ -13,7 +13,7 @@ public class DiscordNotifier implements ErrorNotifier { private static final String NOTIFICATION_PREFIX = ":rotating_light: [**Error 발생!**]\n"; - private static final String STACK_TRACE_AFFIX = "\n```\n"; + private static final String STACK_TRACE_AFFIX = "\n```\n"; private static final String DISCORD_LINE_SEPARATOR = "\n"; private static final int STACK_TRACE_LENGTH = 10; @@ -29,6 +29,7 @@ private JDA initializeJda(String token) { try { return JDABuilder.createDefault(token).build().awaitReady(); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); throw new DTInitializationException(InitializationErrorCode.JDA_INITIALIZATION_FAIL); } } diff --git a/src/main/java/com/debatetimer/config/NotifierConfig.java b/src/main/java/com/debatetimer/config/NotifierConfig.java index 0235a042..04511cf7 100644 --- a/src/main/java/com/debatetimer/config/NotifierConfig.java +++ b/src/main/java/com/debatetimer/config/NotifierConfig.java @@ -5,12 +5,14 @@ import com.debatetimer.client.notifier.DiscordNotifier; import com.debatetimer.client.notifier.DiscordProperties; import com.debatetimer.client.notifier.ErrorNotifier; +import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import org.springframework.boot.context.properties.EnableConfigurationProperties; 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 NotifierConfig { @Profile({"dev", "prod"}) diff --git a/src/main/java/com/debatetimer/controller/auth/AuthMember.java b/src/main/java/com/debatetimer/controller/auth/AuthMember.java index b8a31964..3d34af2c 100644 --- a/src/main/java/com/debatetimer/controller/auth/AuthMember.java +++ b/src/main/java/com/debatetimer/controller/auth/AuthMember.java @@ -8,4 +8,5 @@ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface AuthMember { + } diff --git a/src/main/java/com/debatetimer/controller/member/MemberController.java b/src/main/java/com/debatetimer/controller/member/MemberController.java index a63be8ab..4020369d 100644 --- a/src/main/java/com/debatetimer/controller/member/MemberController.java +++ b/src/main/java/com/debatetimer/controller/member/MemberController.java @@ -16,6 +16,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -39,7 +40,7 @@ public TableResponses getTables(@AuthMember Member member) { } @PostMapping("/api/member") - public ResponseEntity createMember(@RequestBody MemberCreateRequest request) { + public ResponseEntity createMember(@Validated @RequestBody MemberCreateRequest request) { MemberInfo memberInfo = authService.getMemberInfo(request); MemberCreateResponse memberCreateResponse = memberService.createMember(memberInfo); JwtTokenResponse jwtToken = authManager.issueToken(memberInfo); diff --git a/src/test/java/com/debatetimer/controller/BaseControllerTest.java b/src/test/java/com/debatetimer/controller/BaseControllerTest.java index 7ff30539..0ccdab51 100644 --- a/src/test/java/com/debatetimer/controller/BaseControllerTest.java +++ b/src/test/java/com/debatetimer/controller/BaseControllerTest.java @@ -2,12 +2,20 @@ import com.debatetimer.DataBaseCleaner; import com.debatetimer.client.oauth.OAuthClient; +import com.debatetimer.domain.Stance; +import com.debatetimer.domain.customize.CustomizeBoxType; +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.fixture.HeaderGenerator; import com.debatetimer.fixture.MemberGenerator; import com.debatetimer.fixture.TokenGenerator; import com.debatetimer.repository.customize.CustomizeTableRepository; +import com.navercorp.fixturemonkey.ArbitraryBuilder; +import com.navercorp.fixturemonkey.FixtureMonkey; +import com.navercorp.fixturemonkey.api.introspector.ConstructorPropertiesArbitraryIntrospector; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.filter.log.RequestLoggingFilter; @@ -50,6 +58,10 @@ public abstract class BaseControllerTest { private RequestSpecification spec; + private final FixtureMonkey fixtureMonkey = FixtureMonkey.builder() + .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) + .build(); + @BeforeEach void setEnvironment() { RestAssured.port = port; @@ -62,4 +74,31 @@ void setEnvironment() { protected RequestSpecification given() { return RestAssured.given(spec); } + + protected ArbitraryBuilder getCustomizeTableCreateRequestBuilder() { + return fixtureMonkey.giveMeBuilder(CustomizeTableCreateRequest.class) + .set("info", getCustomizeTableInfoCreateRequestBuilder().sample()) + .set("table", getCustomizeTimeBoxCreateRequestBuilder().sampleList(2)); + } + + private ArbitraryBuilder getCustomizeTableInfoCreateRequestBuilder() { + return fixtureMonkey.giveMeBuilder(CustomizeTableInfoCreateRequest.class) + .set("name", "자유 테이블") + .set("agenda", "주제") + .set("prosTeamName", "찬성") + .set("consTeamName", "반대") + .set("warningBell", true) + .set("finishBell", true); + } + + private ArbitraryBuilder getCustomizeTimeBoxCreateRequestBuilder() { + return fixtureMonkey.giveMeBuilder(CustomizeTimeBoxCreateRequest.class) + .set("stance", Stance.PROS) + .set("speechType", "입론1") + .set("boxType", CustomizeBoxType.NORMAL) + .set("time", 120) + .set("timePerTeam", 60) + .set("timePerSpeaking", null) + .set("speaker", "발언자"); + } } diff --git a/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java b/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java index bbaf87b7..06493950 100644 --- a/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java +++ b/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java @@ -9,14 +9,18 @@ import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; 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.exception.ErrorResponse; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.fixture.NullAndEmptyAndBlankSource; import io.restassured.http.ContentType; import io.restassured.http.Headers; -import java.util.List; +import io.restassured.response.ValidatableResponse; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.springframework.http.HttpStatus; class CustomizeControllerTest extends BaseControllerTest { @@ -25,25 +29,11 @@ class Save { @Test void 사용자_지정_테이블을_생성한다() { - Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTableCreateRequest request = new CustomizeTableCreateRequest( - new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", - "반대", true, true), - List.of( - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자1"), - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, 60, null, null) - ) - ); - Headers headers = headerGenerator.generateAccessTokenHeader(bito); + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("table[1].speaker", null) + .sample(); - CustomizeTableResponse response = given() - .contentType(ContentType.JSON) - .headers(headers) - .body(request) - .when().post("/api/table/customize") - .then().statusCode(201) + CustomizeTableResponse response = sendCustomizeTableSaveRequest(request, HttpStatus.CREATED) .extract().as(CustomizeTableResponse.class); assertAll( @@ -51,6 +41,111 @@ class Save { () -> assertThat(response.table()).hasSize(request.table().size()) ); } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 사용자_지정_테이블을_생성할때_테이블_이름은_개행문자_외_다른_글자가_포함되야한다(String tableName) { + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("info.name", tableName) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableSaveRequest(request, HttpStatus.BAD_REQUEST) + .extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @NullSource + @ParameterizedTest + void 사용자_지정_테이블을_생성할때_테이블_주제는_null이_올_수_없다(String agenda) { + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("info.agenda", agenda) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableSaveRequest(request, HttpStatus.BAD_REQUEST) + .extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 사용자_지정_테이블을_생성할때_찬성팀_이름은_개행문자_외_다른_글자가_포함되야한다(String prosTeamName) { + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("info.prosTeamName", prosTeamName) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableSaveRequest(request, HttpStatus.BAD_REQUEST) + .extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 사용자_지정_테이블을_생성할때_반대팀_이름은_개행문자_외_다른_글자가_포함되야한다(String consTeamName) { + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("info.consTeamName", consTeamName) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableSaveRequest(request, HttpStatus.BAD_REQUEST) + .extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @NullSource + @ParameterizedTest + void 사용자_지정_테이블을_생성할때_타임박스_입장은_null이_올_수_없다(Stance stance) { + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("table[0].stance", stance) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableSaveRequest(request, HttpStatus.BAD_REQUEST) + .extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 사용자_지정_테이블을_생성할때_타임박스_발언_유형은_개행문자_외_다른_글자가_포함되야한다(String speechType) { + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("table[0].speechType", speechType) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableSaveRequest(request, HttpStatus.BAD_REQUEST) + .extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @NullSource + @ParameterizedTest + void 사용자_지정_테이블을_생성할때_타임박스_타입은_null이_올_수_없다(CustomizeBoxType boxType) { + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("table[0].boxType", boxType) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableSaveRequest(request, HttpStatus.BAD_REQUEST) + .extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + private ValidatableResponse sendCustomizeTableSaveRequest( + CustomizeTableCreateRequest request, + HttpStatus statusCode + ) { + Member bito = memberGenerator.generate("default@gmail.com"); + Headers headers = headerGenerator.generateAccessTokenHeader(bito); + return given() + .contentType(ContentType.JSON) + .headers(headers) + .body(request) + .when().post("/api/table/customize") + .then().statusCode(statusCode.value()); + } } @Nested @@ -86,26 +181,14 @@ class UpdateTable { void 사용자_지정_토론_테이블을_업데이트한다() { Member bito = memberGenerator.generate("default@gmail.com"); CustomizeTable bitoTable = customizeTableGenerator.generate(bito); - CustomizeTableCreateRequest renewTableRequest = new CustomizeTableCreateRequest( - new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", - "반대", true, true), - List.of( - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자1"), - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, 60, null, null) - ) - ); Headers headers = headerGenerator.generateAccessTokenHeader(bito); + CustomizeTableCreateRequest renewTableRequest = getCustomizeTableCreateRequestBuilder() + .set("table[1].speaker", null) + .sample(); - CustomizeTableResponse response = given() - .contentType(ContentType.JSON) - .pathParam("tableId", bitoTable.getId()) - .headers(headers) - .body(renewTableRequest) - .when().put("/api/table/customize/{tableId}") - .then().statusCode(200) - .extract().as(CustomizeTableResponse.class); + CustomizeTableResponse response = sendCustomizeTableUpdateRequest( + renewTableRequest, HttpStatus.OK, bitoTable, headers + ).extract().as(CustomizeTableResponse.class); assertAll( () -> assertThat(response.id()).isEqualTo(bitoTable.getId()), @@ -113,6 +196,141 @@ class UpdateTable { () -> assertThat(response.table()).hasSize(renewTableRequest.table().size()) ); } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 사용자_지정_테이블을_업데이트할때_테이블_이름은_개행문자_외_다른_글자가_포함되야한다(String tableName) { + Member bito = memberGenerator.generate("default@gmail.com"); + CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + Headers headers = headerGenerator.generateAccessTokenHeader(bito); + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("info.name", tableName) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableUpdateRequest( + request, HttpStatus.BAD_REQUEST, bitoTable, headers + ).extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @NullSource + @ParameterizedTest + void 사용자_지정_테이블을_업데이트할때_테이블_주제는_null이_올_수_없다(String agenda) { + Member bito = memberGenerator.generate("default@gmail.com"); + CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + Headers headers = headerGenerator.generateAccessTokenHeader(bito); + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("info.agenda", agenda) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableUpdateRequest( + request, HttpStatus.BAD_REQUEST, bitoTable, headers + ).extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 사용자_지정_테이블을_업데이트할때_찬성팀_이름은_개행문자_외_다른_글자가_포함되야한다(String prosTeamName) { + Member bito = memberGenerator.generate("default@gmail.com"); + CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + Headers headers = headerGenerator.generateAccessTokenHeader(bito); + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("info.prosTeamName", prosTeamName) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableUpdateRequest( + request, HttpStatus.BAD_REQUEST, bitoTable, headers + ).extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 사용자_지정_테이블을_업데이트할때_반대팀_이름은_개행문자_외_다른_글자가_포함되야한다(String consTeamName) { + Member bito = memberGenerator.generate("default@gmail.com"); + CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + Headers headers = headerGenerator.generateAccessTokenHeader(bito); + + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("info.consTeamName", consTeamName) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableUpdateRequest( + request, HttpStatus.BAD_REQUEST, bitoTable, headers + ).extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @NullSource + @ParameterizedTest + void 사용자_지정_테이블을_업데이트할때_타임박스_입장은_null이_올_수_없다(Stance stance) { + Member bito = memberGenerator.generate("default@gmail.com"); + CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + Headers headers = headerGenerator.generateAccessTokenHeader(bito); + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("table[0].stance", stance) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableUpdateRequest( + request, HttpStatus.BAD_REQUEST, bitoTable, headers + ).extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 사용자_지정_테이블을_업데이트할때_타임박스_발언_유형은_개행문자_외_다른_글자가_포함되야한다(String speechType) { + Member bito = memberGenerator.generate("default@gmail.com"); + CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + Headers headers = headerGenerator.generateAccessTokenHeader(bito); + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("table[0].speechType", speechType) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableUpdateRequest( + request, HttpStatus.BAD_REQUEST, bitoTable, headers + ).extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @NullSource + @ParameterizedTest + void 사용자_지정_테이블을_업데이트할때_타임박스_타입은_null이_올_수_없다(CustomizeBoxType boxType) { + Member bito = memberGenerator.generate("default@gmail.com"); + CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + Headers headers = headerGenerator.generateAccessTokenHeader(bito); + CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() + .set("table[0].boxType", boxType) + .sample(); + + ErrorResponse errorResponse = sendCustomizeTableUpdateRequest( + request, HttpStatus.BAD_REQUEST, bitoTable, headers + ).extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + private ValidatableResponse sendCustomizeTableUpdateRequest( + CustomizeTableCreateRequest request, + HttpStatus statusCode, + CustomizeTable table, + Headers headers + ) { + return given() + .contentType(ContentType.JSON) + .pathParam("tableId", table.getId()) + .headers(headers) + .body(request) + .when().put("/api/table/customize/{tableId}") + .then().statusCode(statusCode.value()); + } } @Nested diff --git a/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java b/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java index 45604e67..b0a59bca 100644 --- a/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java +++ b/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java @@ -10,10 +10,16 @@ import com.debatetimer.dto.member.MemberInfo; import com.debatetimer.dto.member.OAuthToken; import com.debatetimer.dto.member.TableResponses; +import com.debatetimer.exception.ErrorResponse; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.fixture.NullAndEmptyAndBlankSource; import io.restassured.http.ContentType; import io.restassured.http.Headers; +import io.restassured.response.ValidatableResponse; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.springframework.http.HttpStatus; class MemberControllerTest extends BaseControllerTest { @@ -45,16 +51,46 @@ class CreateMember { @Test void 회원을_생성한다() { MemberCreateRequest request = new MemberCreateRequest("gnkldsnglnksl", "http://redirectUrl"); + + sendMemberCreateRequest(request, HttpStatus.CREATED); + } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 회원을_생성할_때_code에는_개행문자_외_다른_글자가_포함되야한다(String code) { + MemberCreateRequest request = new MemberCreateRequest(code, "http://redirectUrl"); + + ErrorResponse errorResponse = sendMemberCreateRequest(request, HttpStatus.BAD_REQUEST) + .extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 회원을_생성할_때_redirect주소에는_개행문자_외_다른_글자가_포함되야한다(String redirectUrl) { + MemberCreateRequest request = new MemberCreateRequest("gnkldsnglnksl", redirectUrl); + + ErrorResponse errorResponse = sendMemberCreateRequest(request, HttpStatus.BAD_REQUEST) + .extract().as(ErrorResponse.class); + + assertThat(errorResponse.message()).isEqualTo(ClientErrorCode.FIELD_ERROR.getMessage()); + } + + private ValidatableResponse sendMemberCreateRequest( + MemberCreateRequest request, + HttpStatus statusCode + ) { OAuthToken oAuthToken = new OAuthToken("accessToken"); MemberInfo memberInfo = new MemberInfo("default@gmail.com"); doReturn(oAuthToken).when(oAuthClient).requestToken(request); doReturn(memberInfo).when(oAuthClient).requestMemberInfo(oAuthToken); - given() + return given() .contentType(ContentType.JSON) .body(request) .when().post("/api/member") - .then().statusCode(201); + .then().statusCode(statusCode.value()); } } diff --git a/src/test/java/com/debatetimer/domain/DebateTableTest.java b/src/test/java/com/debatetimer/domain/DebateTableTest.java index 3035f0c8..eb46dd7c 100644 --- a/src/test/java/com/debatetimer/domain/DebateTableTest.java +++ b/src/test/java/com/debatetimer/domain/DebateTableTest.java @@ -79,8 +79,8 @@ class Update { assertAll( () -> assertThat(table.getName()).isEqualTo("newName"), () -> assertThat(table.getAgenda()).isEqualTo("newAgenda"), - () -> assertThat(table.isWarningBell()).isEqualTo(false), - () -> assertThat(table.isFinishBell()).isEqualTo(false) + () -> assertThat(table.isWarningBell()).isFalse(), + () -> assertThat(table.isFinishBell()).isFalse() ); } diff --git a/src/test/java/com/debatetimer/fixture/BlankArgumentsProvider.java b/src/test/java/com/debatetimer/fixture/BlankArgumentsProvider.java new file mode 100644 index 00000000..326414bc --- /dev/null +++ b/src/test/java/com/debatetimer/fixture/BlankArgumentsProvider.java @@ -0,0 +1,14 @@ +package com.debatetimer.fixture; + +import java.util.stream.Stream; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; + +public class BlankArgumentsProvider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of(Arguments.arguments(" ", "\t", "\n", "\r\n", "\r")); + } +} diff --git a/src/test/java/com/debatetimer/fixture/NullAndEmptyAndBlankSource.java b/src/test/java/com/debatetimer/fixture/NullAndEmptyAndBlankSource.java new file mode 100644 index 00000000..67681d12 --- /dev/null +++ b/src/test/java/com/debatetimer/fixture/NullAndEmptyAndBlankSource.java @@ -0,0 +1,16 @@ +package com.debatetimer.fixture; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@ArgumentsSource(BlankArgumentsProvider.class) +@NullAndEmptySource +public @interface NullAndEmptyAndBlankSource { + +} diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java index 049387ff..3e2552c2 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java @@ -27,7 +27,7 @@ class FindAllByMember { Member bito = memberGenerator.generate("default2@gmail.com"); CustomizeTable chanTable1 = customizeTableGenerator.generate(chan); CustomizeTable chanTable2 = customizeTableGenerator.generate(chan); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + customizeTableGenerator.generate(bito); List foundKeoChanTables = tableRepository.findAllByMember(chan); diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java index 7afe406d..151b62ee 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java @@ -28,8 +28,8 @@ class FindAllByCustomizeTable { CustomizeTable bitoTable = customizeTableGenerator.generate(bito); CustomizeTimeBox chanBox1 = customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); CustomizeTimeBox chanBox2 = customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); - CustomizeTimeBox bitoBox1 = customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 2); - CustomizeTimeBox bitoBox2 = customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 2); + customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 2); + customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 2); List foundBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(chanTable); From 35e66ece66792b4ec189b93c36e0d91944fc42a1 Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Mon, 19 May 2025 14:20:44 +0900 Subject: [PATCH 04/27] =?UTF-8?q?[FIX]=20'=ED=86=A0=EB=A1=A0=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EB=B3=80=EA=B2=BD=20API'=20=EB=8F=99?= =?UTF-8?q?=EC=8B=9C=EC=84=B1=20=EB=AC=B8=EC=A0=9C=20(#173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customize/CustomizeTableRepository.java | 16 ++++++----- .../service/customize/CustomizeService.java | 27 +++++++------------ .../customize/CustomizeDocumentTest.java | 7 ++--- .../CustomizeTableRepositoryTest.java | 24 ----------------- .../customize/CustomizeServiceTest.java | 8 +++--- 5 files changed, 26 insertions(+), 56 deletions(-) diff --git a/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java b/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java index e461e58c..d92536a0 100644 --- a/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java @@ -2,10 +2,11 @@ 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 jakarta.persistence.LockModeType; import java.util.List; import java.util.Optional; +import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; public interface CustomizeTableRepository extends Repository { @@ -14,12 +15,13 @@ public interface CustomizeTableRepository extends Repository findById(long id); - default CustomizeTable getById(long tableId) { - return findById(tableId) - .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); - } - List findAllByMember(Member member); + Optional findByIdAndMember(long tableId, Member member); + + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("SELECT c FROM CustomizeTable c WHERE c.id = :id AND c.member = :member") + Optional findByIdAndMemberWithLock(long id, Member member); + void delete(CustomizeTable table); } diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeService.java b/src/main/java/com/debatetimer/service/customize/CustomizeService.java index fc6afe60..f17a2d6b 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeService.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeService.java @@ -13,7 +13,6 @@ import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; @Service @@ -34,18 +33,20 @@ public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateReques @Transactional(readOnly = true) public CustomizeTableResponse findTable(long tableId, Member member) { - CustomizeTable table = getOwnerTable(tableId, member.getId()); + CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); return new CustomizeTableResponse(table, timeBoxes); } - @Transactional(isolation = Isolation.SERIALIZABLE) + @Transactional public CustomizeTableResponse updateTable( CustomizeTableCreateRequest tableCreateRequest, long tableId, Member member ) { - CustomizeTable existingTable = getOwnerTable(tableId, member.getId()); + CustomizeTable existingTable = tableRepository.findByIdAndMemberWithLock(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); CustomizeTable renewedTable = tableCreateRequest.toTable(member); existingTable.update(renewedTable); @@ -57,7 +58,8 @@ public CustomizeTableResponse updateTable( @Transactional public CustomizeTableResponse updateUsedAt(long tableId, Member member) { - CustomizeTable table = getOwnerTable(tableId, member.getId()); + CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); table.updateUsedAt(); @@ -66,7 +68,8 @@ public CustomizeTableResponse updateUsedAt(long tableId, Member member) { @Transactional public void deleteTable(long tableId, Member member) { - CustomizeTable table = getOwnerTable(tableId, member.getId()); + CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); timeBoxRepository.deleteAll(timeBoxes.getTimeBoxes()); tableRepository.delete(table); @@ -80,16 +83,4 @@ private TimeBoxes saveTimeBoxes( List savedTimeBoxes = timeBoxRepository.saveAll(timeBoxes.getTimeBoxes()); return new TimeBoxes<>(savedTimeBoxes); } - - private CustomizeTable getOwnerTable(long tableId, long memberId) { - CustomizeTable foundTable = tableRepository.getById(tableId); - validateOwn(foundTable, memberId); - return foundTable; - } - - private void validateOwn(CustomizeTable table, long memberId) { - if (!table.isOwner(memberId)) { - throw new DTClientErrorException(ClientErrorCode.NOT_TABLE_OWNER); - } - } } diff --git a/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java b/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java index 629239c8..180afad7 100644 --- a/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java +++ b/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java @@ -259,7 +259,7 @@ class GetTable { } @ParameterizedTest - @EnumSource(value = ClientErrorCode.class, names = {"TABLE_NOT_FOUND", "NOT_TABLE_OWNER"}) + @EnumSource(value = ClientErrorCode.class, names = {"TABLE_NOT_FOUND"}) void 사용자_지정_테이블_조회_실패(ClientErrorCode errorCode) { long tableId = 5L; doThrow(new DTClientErrorException(errorCode)).when(customizeService).findTable(eq(tableId), any()); @@ -387,6 +387,7 @@ class UpdateTable { @EnumSource( value = ClientErrorCode.class, names = { + "TABLE_NOT_FOUND", "INVALID_TABLE_NAME_LENGTH", "INVALID_TABLE_NAME_FORM", "INVALID_TABLE_TIME", @@ -507,7 +508,7 @@ class Debate { } @ParameterizedTest - @EnumSource(value = ClientErrorCode.class, names = {"TABLE_NOT_FOUND", "NOT_TABLE_OWNER"}) + @EnumSource(value = ClientErrorCode.class, names = {"TABLE_NOT_FOUND"}) void 사용자_지정_토론_진행_실패(ClientErrorCode errorCode) { long tableId = 5L; doThrow(new DTClientErrorException(errorCode)).when(customizeService).updateUsedAt(eq(tableId), any()); @@ -555,7 +556,7 @@ class DeleteTable { .then().statusCode(204); } - @EnumSource(value = ClientErrorCode.class, names = {"TABLE_NOT_FOUND", "NOT_TABLE_OWNER"}) + @EnumSource(value = ClientErrorCode.class, names = {"TABLE_NOT_FOUND"}) @ParameterizedTest void 사용자_지정_테이블_삭제_실패(ClientErrorCode errorCode) { long tableId = 5L; diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java index 3e2552c2..69482003 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java @@ -1,12 +1,9 @@ package com.debatetimer.repository.customize; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; 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.BaseRepositoryTest; import java.util.List; import org.junit.jupiter.api.Nested; @@ -34,25 +31,4 @@ class FindAllByMember { assertThat(foundKeoChanTables).containsExactly(chanTable1, chanTable2); } } - - @Nested - class GetById { - - @Test - void 특정_아이디의_테이블을_조회한다() { - Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTable chanTable = customizeTableGenerator.generate(chan); - - CustomizeTable foundChanTable = tableRepository.getById(chanTable.getId()); - - assertThat(foundChanTable).usingRecursiveComparison().isEqualTo(chanTable); - } - - @Test - void 특정_아이디의_테이블이_없으면_에러를_발생시킨다() { - assertThatThrownBy(() -> tableRepository.getById(1L)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); - } - } } diff --git a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java index 33cad149..71a10611 100644 --- a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java +++ b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java @@ -84,7 +84,7 @@ class FindTable { assertThatThrownBy(() -> customizeService.findTable(chanTableId, coli)) .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.NOT_TABLE_OWNER.getMessage()); + .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); } } @@ -138,7 +138,7 @@ class UpdateTable { assertThatThrownBy(() -> customizeService.updateTable(renewTableRequest, chanTableId, coli)) .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.NOT_TABLE_OWNER.getMessage()); + .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); } @Test @@ -199,7 +199,7 @@ class UpdateUsedAt { assertThatThrownBy(() -> customizeService.updateTable(renewTableRequest, chanTableId, coli)) .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.NOT_TABLE_OWNER.getMessage()); + .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); } } @@ -234,7 +234,7 @@ class DeleteTable { assertThatThrownBy(() -> customizeService.deleteTable(chanTableId, coli)) .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.NOT_TABLE_OWNER.getMessage()); + .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); } } } From 4fae84f71b37d200d12f9ffe3347227320780725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B1=B4=EC=9A=B0?= Date: Mon, 19 May 2025 14:34:02 +0900 Subject: [PATCH 05/27] =?UTF-8?q?[REFACTOR]=20MappedSuperClass=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20(#177)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/debatetimer/domain/DebateTable.java | 82 ------------ .../com/debatetimer/domain/DebateTimeBox.java | 64 --------- .../com/debatetimer/domain/TimeBoxes.java | 20 --- .../domain/customize/CustomizeTable.java | 61 +++++++-- .../domain/customize/CustomizeTimeBox.java | 57 +++++++- .../domain/customize/CustomizeTimeBoxes.java | 20 +++ .../request/CustomizeTableCreateRequest.java | 7 +- .../response/CustomizeTableResponse.java | 8 +- .../debatetimer/dto/member/TableResponse.java | 4 +- .../dto/member/TableResponses.java | 5 +- .../com/debatetimer/dto/member/TableType.java | 2 - .../exception/errorcode/ClientErrorCode.java | 6 +- .../customize/CustomizeTimeBoxRepository.java | 6 +- .../service/customize/CustomizeService.java | 30 ++--- .../controller/member/MemberDocumentTest.java | 4 +- .../debatetimer/domain/DebateTableTest.java | 126 ------------------ .../debatetimer/domain/DebateTimeBoxTest.java | 78 ----------- .../domain/customize/CustomizeTableTest.java | 86 ++++++++++++ .../customize/CustomizeTimeBoxTest.java | 75 +++++++++++ .../CustomizeTimeBoxesTest.java} | 10 +- 20 files changed, 320 insertions(+), 431 deletions(-) delete mode 100644 src/main/java/com/debatetimer/domain/DebateTable.java delete mode 100644 src/main/java/com/debatetimer/domain/DebateTimeBox.java delete mode 100644 src/main/java/com/debatetimer/domain/TimeBoxes.java create mode 100644 src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java delete mode 100644 src/test/java/com/debatetimer/domain/DebateTableTest.java delete mode 100644 src/test/java/com/debatetimer/domain/DebateTimeBoxTest.java rename src/test/java/com/debatetimer/domain/{TimeBoxesTest.java => customize/CustomizeTimeBoxesTest.java} (78%) diff --git a/src/main/java/com/debatetimer/domain/DebateTable.java b/src/main/java/com/debatetimer/domain/DebateTable.java deleted file mode 100644 index 41161031..00000000 --- a/src/main/java/com/debatetimer/domain/DebateTable.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.debatetimer.domain; - -import com.debatetimer.domain.member.Member; -import com.debatetimer.dto.member.TableType; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.MappedSuperclass; -import jakarta.validation.constraints.NotNull; -import java.time.LocalDateTime; -import java.util.Objects; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@MappedSuperclass -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public abstract class DebateTable extends BaseTimeEntity { - - private static final String NAME_REGEX = "^[\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\s]+$"; - public static final int NAME_MAX_LENGTH = 20; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; - - @NotNull - private String name; - - private String agenda; - private boolean warningBell; - private boolean finishBell; - - @NotNull - private LocalDateTime usedAt; - - protected DebateTable(Member member, String name, String agenda, boolean warningBell, boolean finishBell) { - validate(name); - - this.member = member; - this.name = name; - this.agenda = agenda; - this.warningBell = warningBell; - this.finishBell = finishBell; - this.usedAt = LocalDateTime.now(); - } - - public final boolean isOwner(long memberId) { - return Objects.equals(this.member.getId(), memberId); - } - - public final void updateUsedAt() { - this.usedAt = LocalDateTime.now(); - } - - protected final void updateTable(DebateTable renewTable) { - validate(renewTable.getName()); - - this.name = renewTable.getName(); - this.agenda = renewTable.getAgenda(); - this.warningBell = renewTable.isWarningBell(); - this.finishBell = renewTable.isFinishBell(); - updateUsedAt(); - } - - private void validate(String name) { - if (name.length() > NAME_MAX_LENGTH) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TABLE_NAME_LENGTH); - } - if (!name.matches(NAME_REGEX)) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TABLE_NAME_FORM); - } - } - - public abstract long getId(); - - public abstract TableType getType(); -} diff --git a/src/main/java/com/debatetimer/domain/DebateTimeBox.java b/src/main/java/com/debatetimer/domain/DebateTimeBox.java deleted file mode 100644 index c0409032..00000000 --- a/src/main/java/com/debatetimer/domain/DebateTimeBox.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.debatetimer.domain; - -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.MappedSuperclass; -import jakarta.validation.constraints.NotNull; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@MappedSuperclass -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public abstract class DebateTimeBox { - - public static final int SPEAKER_MAX_LENGTH = 5; - - private int sequence; - - @NotNull - @Enumerated(EnumType.STRING) - private Stance stance; - - private int time; - private String speaker; - - protected DebateTimeBox(int sequence, Stance stance, int time, String speaker) { - validateSpeaker(speaker); - validateSequence(sequence); - validateTime(time); - - this.sequence = sequence; - this.stance = stance; - this.time = time; - this.speaker = initializeSpeaker(speaker); - } - - private String initializeSpeaker(String speaker) { - if (speaker == null || speaker.isBlank()) { - return null; - } - return speaker; - } - - private void validateSpeaker(String speaker) { - if (speaker != null && speaker.length() > SPEAKER_MAX_LENGTH) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH); - } - } - - private void validateSequence(int sequence) { - if (sequence <= 0) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SEQUENCE); - } - } - - private void validateTime(int time) { - if (time <= 0) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_TIME); - } - } -} diff --git a/src/main/java/com/debatetimer/domain/TimeBoxes.java b/src/main/java/com/debatetimer/domain/TimeBoxes.java deleted file mode 100644 index 62159ec0..00000000 --- a/src/main/java/com/debatetimer/domain/TimeBoxes.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.debatetimer.domain; - -import java.util.Comparator; -import java.util.List; -import lombok.Getter; - -@Getter -public class TimeBoxes { - - private static final Comparator TIME_BOX_COMPARATOR = Comparator - .comparing(DebateTimeBox::getSequence); - - private final List timeBoxes; - - public TimeBoxes(List timeBoxes) { - this.timeBoxes = timeBoxes.stream() - .sorted(TIME_BOX_COMPARATOR) - .toList(); - } -} diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java index 0bc38a83..ff487570 100644 --- a/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java +++ b/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java @@ -1,15 +1,19 @@ package com.debatetimer.domain.customize; -import com.debatetimer.domain.DebateTable; import com.debatetimer.domain.member.Member; import com.debatetimer.dto.member.TableType; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -17,21 +21,39 @@ @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class CustomizeTable extends DebateTable { +public class CustomizeTable { + private static final String TABLE_NAME_REGEX = "^[\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\s]+$"; private static final String TEAM_NAME_REGEX = "^[\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\s]+$"; + public static final int TABLE_NAME_MAX_LENGTH = 20; public static final int TEAM_NAME_MAX_LENGTH = 8; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @NotNull + private String name; + + private String agenda; + @NotBlank private String prosTeamName; @NotBlank private String consTeamName; + private boolean warningBell; + private boolean finishBell; + + @NotNull + private LocalDateTime usedAt; + public CustomizeTable( Member member, String name, @@ -41,17 +63,40 @@ public CustomizeTable( String prosTeamName, String consTeamName ) { - super(member, name, agenda, warningBell, finishBell); + validateTableName(name); validateTeamName(prosTeamName); validateTeamName(consTeamName); + this.member = member; + this.name = name; + this.agenda = agenda; this.prosTeamName = prosTeamName; this.consTeamName = consTeamName; + this.warningBell = warningBell; + this.finishBell = finishBell; + this.usedAt = LocalDateTime.now(); } - public void update(CustomizeTable renewTable) { + public void updateTable(CustomizeTable renewTable) { + this.name = renewTable.getName(); + this.agenda = renewTable.getAgenda(); this.prosTeamName = renewTable.getProsTeamName(); this.consTeamName = renewTable.getConsTeamName(); - updateTable(renewTable); + this.warningBell = renewTable.isWarningBell(); + this.finishBell = renewTable.isFinishBell(); + this.usedAt = LocalDateTime.now(); + } + + public void updateUsedAt() { + this.usedAt = LocalDateTime.now(); + } + + private void validateTableName(String name) { + if (name.length() > TABLE_NAME_MAX_LENGTH) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TABLE_NAME_LENGTH); + } + if (!name.matches(TABLE_NAME_REGEX)) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TABLE_NAME_FORM); + } } private void validateTeamName(String teamName) { @@ -63,12 +108,6 @@ private void validateTeamName(String teamName) { } } - @Override - public long getId() { - return id; - } - - @Override public TableType getType() { return TableType.CUSTOMIZE; } diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBox.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBox.java index cc98e171..c580713d 100644 --- a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBox.java +++ b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBox.java @@ -1,6 +1,5 @@ package com.debatetimer.domain.customize; -import com.debatetimer.domain.DebateTimeBox; import com.debatetimer.domain.Stance; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; @@ -22,10 +21,11 @@ @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class CustomizeTimeBox extends DebateTimeBox { +public class CustomizeTimeBox { public static final int SPEECH_TYPE_MAX_LENGTH = 10; public static final int TIME_MULTIPLIER = 2; + public static final int SPEAKER_MAX_LENGTH = 5; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -36,6 +36,15 @@ public class CustomizeTimeBox extends DebateTimeBox { @JoinColumn(name = "table_id") private CustomizeTable customizeTable; + private int sequence; + + @NotNull + @Enumerated(EnumType.STRING) + private Stance stance; + + private int time; + private String speaker; + @NotBlank private String speechType; @@ -55,11 +64,17 @@ public CustomizeTimeBox( Integer time, String speaker ) { - super(sequence, stance, time, speaker); validateNotTimeBasedType(boxType); validateSpeechType(speechType); + validateSpeaker(speaker); + validateSequence(sequence); + validateTime(time); this.customizeTable = customizeTable; + this.sequence = sequence; + this.stance = stance; + this.time = time; + this.speaker = initializeSpeaker(speaker); this.speechType = speechType; this.boxType = boxType; } @@ -74,11 +89,17 @@ public CustomizeTimeBox( Integer timePerSpeaking, String speaker ) { - super(sequence, stance, convertToTime(timePerTeam), speaker); validateTimeBasedTimes(timePerTeam, timePerSpeaking); validateTimeBasedType(boxType); validateSpeechType(speechType); - + validateSpeaker(speaker); + validateSequence(sequence); + validateTime(convertToTime(timePerTeam)); + + this.sequence = sequence; + this.stance = stance; + this.time = convertToTime(timePerTeam); + this.speaker = initializeSpeaker(speaker); this.customizeTable = customizeTable; this.speechType = speechType; this.boxType = boxType; @@ -93,6 +114,32 @@ private static int convertToTime(Integer timePerTeam) { return timePerTeam * TIME_MULTIPLIER; } + + private String initializeSpeaker(String speaker) { + if (speaker == null || speaker.isBlank()) { + return null; + } + return speaker; + } + + private void validateSpeaker(String speaker) { + if (speaker != null && speaker.length() > SPEAKER_MAX_LENGTH) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH); + } + } + + private void validateSequence(int sequence) { + if (sequence <= 0) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SEQUENCE); + } + } + + private void validateTime(int time) { + if (time <= 0) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_TIME); + } + } + private void validateTime(Integer time) { if (time == null || time <= 0) { throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_TIME); diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java new file mode 100644 index 00000000..7be35232 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java @@ -0,0 +1,20 @@ +package com.debatetimer.domain.customize; + +import java.util.Comparator; +import java.util.List; +import lombok.Getter; + +@Getter +public class CustomizeTimeBoxes { + + private static final Comparator TIME_BOX_COMPARATOR = Comparator + .comparing(CustomizeTimeBox::getSequence); + + private final List timeBoxes; + + public CustomizeTimeBoxes(List timeBoxes) { + this.timeBoxes = timeBoxes.stream() + .sorted(TIME_BOX_COMPARATOR) + .toList(); + } +} 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 da62fb26..e0eb509e 100644 --- a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableCreateRequest.java +++ b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableCreateRequest.java @@ -1,8 +1,7 @@ package com.debatetimer.dto.customize.request; -import com.debatetimer.domain.TimeBoxes; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.CustomizeTimeBoxes; import com.debatetimer.domain.member.Member; import jakarta.validation.Valid; import java.util.List; @@ -18,9 +17,9 @@ public CustomizeTable toTable(Member member) { return info.toTable(member); } - public TimeBoxes toTimeBoxes(CustomizeTable customizeTable) { + public CustomizeTimeBoxes toTimeBoxes(CustomizeTable customizeTable) { return IntStream.range(0, table.size()) .mapToObj(i -> table.get(i).toTimeBox(customizeTable, i + 1)) - .collect(Collectors.collectingAndThen(Collectors.toList(), TimeBoxes::new)); + .collect(Collectors.collectingAndThen(Collectors.toList(), CustomizeTimeBoxes::new)); } } diff --git a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java index bbd1bf95..d074287d 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java @@ -1,24 +1,24 @@ package com.debatetimer.dto.customize.response; -import com.debatetimer.domain.TimeBoxes; import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.CustomizeTimeBoxes; import java.util.List; public record CustomizeTableResponse(long id, CustomizeTableInfoResponse info, List table) { public CustomizeTableResponse( CustomizeTable customizeTable, - TimeBoxes timeBasedTimeBoxes + CustomizeTimeBoxes customizeTimeBoxes ) { this( customizeTable.getId(), new CustomizeTableInfoResponse(customizeTable), - toTimeBoxResponses(timeBasedTimeBoxes) + toTimeBoxResponses(customizeTimeBoxes) ); } - private static List toTimeBoxResponses(TimeBoxes timeBoxes) { + private static List toTimeBoxResponses(CustomizeTimeBoxes timeBoxes) { List customizeTimeBoxes = timeBoxes.getTimeBoxes(); return customizeTimeBoxes .stream() diff --git a/src/main/java/com/debatetimer/dto/member/TableResponse.java b/src/main/java/com/debatetimer/dto/member/TableResponse.java index 7c6baa62..fe066734 100644 --- a/src/main/java/com/debatetimer/dto/member/TableResponse.java +++ b/src/main/java/com/debatetimer/dto/member/TableResponse.java @@ -1,10 +1,10 @@ package com.debatetimer.dto.member; -import com.debatetimer.domain.DebateTable; +import com.debatetimer.domain.customize.CustomizeTable; public record TableResponse(long id, String name, TableType type, String agenda) { - public TableResponse(DebateTable debateTable) { + public TableResponse(CustomizeTable debateTable) { this( debateTable.getId(), debateTable.getName(), diff --git a/src/main/java/com/debatetimer/dto/member/TableResponses.java b/src/main/java/com/debatetimer/dto/member/TableResponses.java index 9ee7968a..4abe1d47 100644 --- a/src/main/java/com/debatetimer/dto/member/TableResponses.java +++ b/src/main/java/com/debatetimer/dto/member/TableResponses.java @@ -1,6 +1,5 @@ package com.debatetimer.dto.member; -import com.debatetimer.domain.DebateTable; import com.debatetimer.domain.customize.CustomizeTable; import java.util.Comparator; import java.util.List; @@ -8,8 +7,8 @@ public record TableResponses(List tables) { - private static final Comparator DEBATE_TABLE_COMPARATOR = Comparator - .comparing(DebateTable::getUsedAt) + private static final Comparator DEBATE_TABLE_COMPARATOR = Comparator + .comparing(CustomizeTable::getUsedAt) .reversed(); public static TableResponses from(List customizeTables) { diff --git a/src/main/java/com/debatetimer/dto/member/TableType.java b/src/main/java/com/debatetimer/dto/member/TableType.java index 8b7efa1d..7f5872b0 100644 --- a/src/main/java/com/debatetimer/dto/member/TableType.java +++ b/src/main/java/com/debatetimer/dto/member/TableType.java @@ -2,7 +2,5 @@ public enum TableType { - PARLIAMENTARY, - TIME_BASED, CUSTOMIZE; } diff --git a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java index 39cbdceb..346ebbb0 100644 --- a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java +++ b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java @@ -1,7 +1,5 @@ package com.debatetimer.exception.errorcode; -import com.debatetimer.domain.DebateTable; -import com.debatetimer.domain.DebateTimeBox; import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.customize.CustomizeTimeBox; import lombok.Getter; @@ -12,7 +10,7 @@ public enum ClientErrorCode implements ResponseErrorCode { INVALID_TABLE_NAME_LENGTH( HttpStatus.BAD_REQUEST, - "테이블 이름은 1자 이상 %d자 이하여야 합니다".formatted(DebateTable.NAME_MAX_LENGTH) + "테이블 이름은 1자 이상 %d자 이하여야 합니다".formatted(CustomizeTable.TABLE_NAME_MAX_LENGTH) ), INVALID_TABLE_NAME_FORM( HttpStatus.BAD_REQUEST, @@ -32,7 +30,7 @@ public enum ClientErrorCode implements ResponseErrorCode { ), INVALID_TIME_BOX_SPEAKER_LENGTH( HttpStatus.BAD_REQUEST, - "발언자 이름은 1자 이상 %d자 이하여야 합니다.".formatted(DebateTimeBox.SPEAKER_MAX_LENGTH) + "발언자 이름은 1자 이상 %d자 이하여야 합니다.".formatted(CustomizeTimeBox.SPEAKER_MAX_LENGTH) ), INVALID_TEAM_NAME_LENGTH( HttpStatus.BAD_REQUEST, diff --git a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java index 85657e38..8f20829f 100644 --- a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java @@ -1,8 +1,8 @@ package com.debatetimer.repository.customize; -import com.debatetimer.domain.TimeBoxes; import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.CustomizeTimeBoxes; import java.util.List; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -22,9 +22,9 @@ default List saveAll(List timeBoxes) { List findAllByCustomizeTable(CustomizeTable table); - default TimeBoxes findTableTimeBoxes(CustomizeTable table) { + default CustomizeTimeBoxes findTableTimeBoxes(CustomizeTable table) { List timeBoxes = findAllByCustomizeTable(table); - return new TimeBoxes<>(timeBoxes); + return new CustomizeTimeBoxes(timeBoxes); } @Query("DELETE FROM CustomizeTimeBox ctb WHERE ctb IN :timeBoxes") diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeService.java b/src/main/java/com/debatetimer/service/customize/CustomizeService.java index f17a2d6b..bc568eeb 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeService.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeService.java @@ -1,8 +1,8 @@ package com.debatetimer.service.customize; -import com.debatetimer.domain.TimeBoxes; import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.CustomizeTimeBoxes; import com.debatetimer.domain.member.Member; import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; import com.debatetimer.dto.customize.response.CustomizeTableResponse; @@ -27,15 +27,15 @@ public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateReques CustomizeTable table = tableCreateRequest.toTable(member); CustomizeTable savedTable = tableRepository.save(table); - TimeBoxes savedTimeBoxes = saveTimeBoxes(tableCreateRequest, savedTable); - return new CustomizeTableResponse(savedTable, savedTimeBoxes); + CustomizeTimeBoxes savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, savedTable); + return new CustomizeTableResponse(savedTable, savedCustomizeTimeBoxes); } @Transactional(readOnly = true) public CustomizeTableResponse findTable(long tableId, Member member) { CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); + CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); return new CustomizeTableResponse(table, timeBoxes); } @@ -48,19 +48,19 @@ public CustomizeTableResponse updateTable( CustomizeTable existingTable = tableRepository.findByIdAndMemberWithLock(tableId, member) .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); CustomizeTable renewedTable = tableCreateRequest.toTable(member); - existingTable.update(renewedTable); + existingTable.updateTable(renewedTable); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(existingTable); - timeBoxRepository.deleteAll(timeBoxes.getTimeBoxes()); - TimeBoxes savedTimeBoxes = saveTimeBoxes(tableCreateRequest, existingTable); - return new CustomizeTableResponse(existingTable, savedTimeBoxes); + CustomizeTimeBoxes customizeTimeBoxes = timeBoxRepository.findTableTimeBoxes(existingTable); + timeBoxRepository.deleteAll(customizeTimeBoxes.getTimeBoxes()); + CustomizeTimeBoxes savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, existingTable); + return new CustomizeTableResponse(existingTable, savedCustomizeTimeBoxes); } @Transactional public CustomizeTableResponse updateUsedAt(long tableId, Member member) { CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); + CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); table.updateUsedAt(); return new CustomizeTableResponse(table, timeBoxes); @@ -70,17 +70,17 @@ public CustomizeTableResponse updateUsedAt(long tableId, Member member) { public void deleteTable(long tableId, Member member) { CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); + CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); timeBoxRepository.deleteAll(timeBoxes.getTimeBoxes()); tableRepository.delete(table); } - private TimeBoxes saveTimeBoxes( + private CustomizeTimeBoxes saveTimeBoxes( CustomizeTableCreateRequest tableCreateRequest, CustomizeTable table ) { - TimeBoxes timeBoxes = tableCreateRequest.toTimeBoxes(table); - List savedTimeBoxes = timeBoxRepository.saveAll(timeBoxes.getTimeBoxes()); - return new TimeBoxes<>(savedTimeBoxes); + CustomizeTimeBoxes customizeTimeBoxes = tableCreateRequest.toTimeBoxes(table); + List savedTimeBoxes = timeBoxRepository.saveAll(customizeTimeBoxes.getTimeBoxes()); + return new CustomizeTimeBoxes(savedTimeBoxes); } } diff --git a/src/test/java/com/debatetimer/controller/member/MemberDocumentTest.java b/src/test/java/com/debatetimer/controller/member/MemberDocumentTest.java index 19716b59..149c7de3 100644 --- a/src/test/java/com/debatetimer/controller/member/MemberDocumentTest.java +++ b/src/test/java/com/debatetimer/controller/member/MemberDocumentTest.java @@ -108,8 +108,8 @@ class GetTables { @Test void 테이블_조회_성공() { TableResponses response = new TableResponses( - List.of(new TableResponse(1L, "토론 테이블 1", TableType.PARLIAMENTARY, "주제1"), - new TableResponse(2L, "토론 테이블 2", TableType.PARLIAMENTARY, "주제2")) + List.of(new TableResponse(1L, "토론 테이블 1", TableType.CUSTOMIZE, "주제1"), + new TableResponse(2L, "토론 테이블 2", TableType.CUSTOMIZE, "주제2")) ); doReturn(response).when(memberService).getTables(EXIST_MEMBER_ID); diff --git a/src/test/java/com/debatetimer/domain/DebateTableTest.java b/src/test/java/com/debatetimer/domain/DebateTableTest.java deleted file mode 100644 index eb46dd7c..00000000 --- a/src/test/java/com/debatetimer/domain/DebateTableTest.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.debatetimer.domain; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -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.dto.member.TableType; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import java.time.LocalDateTime; -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; - -class DebateTableTest { - - @Nested - class Validate { - - @ValueSource(strings = {"a bc가다9", "가0나 다ab", "ㄱㄷㅇㄹ", "漢字", "にほんご", "vielfältig"}) - @ParameterizedTest - void 테이블_이름은_이모지를_제외한_글자만_가능하다(String name) { - Member member = new Member("default@gmail.com"); - assertThatCode(() -> new DebateTableTestObject(member, name, "agenda", true, true)) - .doesNotThrowAnyException(); - } - - @ValueSource(strings = {"a😀bc가다9", "🐥", "🥦"}) - @ParameterizedTest - void 테이블_이름에_이모지를_넣을_수_없다(String name) { - Member member = new Member("default@gmail.com"); - assertThatThrownBy(() -> new DebateTableTestObject(member, name, "agenda", true, true)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TABLE_NAME_FORM.getMessage()); - } - - @Test - void 테이블_이름은_정해진_길이_이내여야_한다() { - Member member = new Member("default@gmail.com"); - String longTableName = "f".repeat(DebateTable.NAME_MAX_LENGTH + 1); - - assertThatThrownBy(() -> new DebateTableTestObject(member, longTableName, "agenda", true, true)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TABLE_NAME_LENGTH.getMessage()); - } - } - - @Nested - class UpdateUsedAt { - - @Test - void 테이블의_사용_시각을_업데이트한다() throws InterruptedException { - Member member = new Member("default@gmail.com"); - DebateTableTestObject table = new DebateTableTestObject(member, "tableName", "agenda", true, true); - LocalDateTime beforeUsedAt = table.getUsedAt(); - Thread.sleep(1); - - table.updateUsedAt(); - - assertThat(table.getUsedAt()).isAfter(beforeUsedAt); - } - } - - @Nested - class Update { - - @Test - void 테이블_정보를_업데이트_할_수_있다() { - Member member = new Member("default@gmail.com"); - DebateTableTestObject table = new DebateTableTestObject(member, "tableName", "agenda", true, true); - DebateTableTestObject renewTable = new DebateTableTestObject(member, "newName", "newAgenda", false, - false); - - table.updateTable(renewTable); - - assertAll( - () -> assertThat(table.getName()).isEqualTo("newName"), - () -> assertThat(table.getAgenda()).isEqualTo("newAgenda"), - () -> assertThat(table.isWarningBell()).isFalse(), - () -> assertThat(table.isFinishBell()).isFalse() - ); - } - - @Test - void 테이블_업데이트_할_때_사용_시간을_변경한다() throws InterruptedException { - Member member = new Member("default@gmail.com"); - DebateTableTestObject table = new DebateTableTestObject(member, "tableName", "agenda", true, true); - DebateTableTestObject renewTable = new DebateTableTestObject(member, "newName", "newAgenda", false, - false); - LocalDateTime beforeUsedAt = table.getUsedAt(); - Thread.sleep(1); - - table.updateTable(renewTable); - - assertThat(table.getUsedAt()).isAfter(beforeUsedAt); - } - } - - private static class DebateTableTestObject extends DebateTable { - - public DebateTableTestObject(Member member, - String name, - String agenda, - boolean warningBell, - boolean finishBell) { - super(member, name, agenda, warningBell, finishBell); - } - - @Override - public long getId() { - return 0; - } - - @Override - public TableType getType() { - return null; - } - - public void updateTable(DebateTableTestObject renewTable) { - super.updateTable(renewTable); - } - } -} diff --git a/src/test/java/com/debatetimer/domain/DebateTimeBoxTest.java b/src/test/java/com/debatetimer/domain/DebateTimeBoxTest.java deleted file mode 100644 index d6ea6af1..00000000 --- a/src/test/java/com/debatetimer/domain/DebateTimeBoxTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.debatetimer.domain; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -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; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullSource; -import org.junit.jupiter.params.provider.ValueSource; - -class DebateTimeBoxTest { - - @Nested - class ValidateSequence { - - @ValueSource(ints = {0, -1}) - @ParameterizedTest - void 순서는_양수만_가능하다(int sequence) { - assertThatThrownBy(() -> new DebateTimeBoxTestObject(sequence, Stance.CONS, 60, "발언자")) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SEQUENCE.getMessage()); - } - - - } - - @Nested - class ValidateTime { - - @ValueSource(ints = {0, -1}) - @ParameterizedTest - void 시간은_양수만_가능하다(int time) { - assertThatThrownBy( - () -> new DebateTimeBoxTestObject(1, Stance.CONS, time, "발언자")) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); - } - } - - @Nested - class ValidateSpeaker { - - @Test - void 발언자_이름은_일정길이_이내로_허용된다() { - String speaker = "k".repeat(DebateTimeBox.SPEAKER_MAX_LENGTH + 1); - - assertThatThrownBy(() -> new DebateTimeBoxTestObject(1, Stance.CONS, 60, speaker)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH.getMessage()); - } - - @NullSource - @ParameterizedTest - void 발언자는_빈_값이_허용된다(String speaker) { - assertThatCode(() -> new DebateTimeBoxTestObject(1, Stance.CONS, 60, speaker)) - .doesNotThrowAnyException(); - } - - @ValueSource(strings = {" ", " "}) - @ParameterizedTest - void 발언자는_공백이_입력되면_null로_저장된다(String speaker) { - DebateTimeBoxTestObject timeBox = new DebateTimeBoxTestObject(1, Stance.CONS, 60, speaker); - - assertThat(timeBox.getSpeaker()).isNull(); - } - } - - private static class DebateTimeBoxTestObject extends DebateTimeBox { - - public DebateTimeBoxTestObject(int sequence, Stance stance, int time, String speaker) { - super(sequence, stance, time, speaker); - } - } -} diff --git a/src/test/java/com/debatetimer/domain/customize/CustomizeTableTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTableTest.java index e2f5d389..c95d5dda 100644 --- a/src/test/java/com/debatetimer/domain/customize/CustomizeTableTest.java +++ b/src/test/java/com/debatetimer/domain/customize/CustomizeTableTest.java @@ -3,11 +3,13 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; 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.dto.member.TableType; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; +import java.time.LocalDateTime; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -26,6 +28,37 @@ class GetType { } } + @Nested + class ValidateTableName { + + @ValueSource(strings = {"a bc가다9", "가0나 다ab", "ㄱㄷㅇㄹ", "漢字", "にほんご", "vielfältig"}) + @ParameterizedTest + void 테이블_이름은_이모지를_제외한_글자만_가능하다(String name) { + Member member = new Member("default@gmail.com"); + assertThatCode(() -> new CustomizeTable(member, name, "agenda", true, true, "pros", "cons")) + .doesNotThrowAnyException(); + } + + @ValueSource(strings = {"a😀bc가다9", "🐥", "🥦"}) + @ParameterizedTest + void 테이블_이름에_이모지를_넣을_수_없다(String name) { + Member member = new Member("default@gmail.com"); + assertThatThrownBy(() -> new CustomizeTable(member, name, "agenda", true, true, "pros", "cons")) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TABLE_NAME_FORM.getMessage()); + } + + @Test + void 테이블_이름은_정해진_길이_이내여야_한다() { + Member member = new Member("default@gmail.com"); + String longTableName = "f".repeat(CustomizeTable.TABLE_NAME_MAX_LENGTH + 1); + + assertThatThrownBy(() -> new CustomizeTable(member, longTableName, "agenda", true, true, "pros", "cons")) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TABLE_NAME_LENGTH.getMessage()); + } + } + @Nested class ValidateTeamName { @@ -85,4 +118,57 @@ class ValidateTeamName { .hasMessage(ClientErrorCode.INVALID_TEAM_NAME_FORM.getMessage()); } } + + @Nested + class UpdateUsedAt { + + @Test + void 테이블의_사용_시각을_업데이트한다() throws InterruptedException { + Member member = new Member("default@gmail.com"); + CustomizeTable table = new CustomizeTable(member, "tableName", "agenda", true, true, "찬성", "반대"); + LocalDateTime beforeUsedAt = table.getUsedAt(); + Thread.sleep(1); + + table.updateUsedAt(); + + assertThat(table.getUsedAt()).isAfter(beforeUsedAt); + } + } + + @Nested + class Update { + + @Test + void 테이블_정보를_업데이트_할_수_있다() { + Member member = new Member("default@gmail.com"); + CustomizeTable table = new CustomizeTable(member, "tableName", "agenda", true, true, "pros", "cons"); + CustomizeTable renewTable = new CustomizeTable(member, "newName", "newAgenda", false, false, "newPros", + "newCons"); + + table.updateTable(renewTable); + + assertAll( + () -> assertThat(table.getName()).isEqualTo("newName"), + () -> assertThat(table.getAgenda()).isEqualTo("newAgenda"), + () -> assertThat(table.getProsTeamName()).isEqualTo("newPros"), + () -> assertThat(table.getConsTeamName()).isEqualTo("newCons"), + () -> assertThat(table.isWarningBell()).isFalse(), + () -> assertThat(table.isFinishBell()).isFalse() + ); + } + + @Test + void 테이블_업데이트_할_때_사용_시간을_변경한다() throws InterruptedException { + Member member = new Member("default@gmail.com"); + CustomizeTable table = new CustomizeTable(member, "tableName", "agenda", true, true, "pros", "cons"); + CustomizeTable renewTable = new CustomizeTable(member, "newName", "newAgenda", false, false, "newPros", + "newCons"); + LocalDateTime beforeUsedAt = table.getUsedAt(); + Thread.sleep(1); + + table.updateTable(renewTable); + + assertThat(table.getUsedAt()).isAfter(beforeUsedAt); + } + } } diff --git a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxTest.java index 4d482d1d..50fb562b 100644 --- a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxTest.java +++ b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxTest.java @@ -9,9 +9,84 @@ 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.NullSource; +import org.junit.jupiter.params.provider.ValueSource; class CustomizeTimeBoxTest { + @Nested + class ValidateSequence { + + @ValueSource(ints = {0, -1}) + @ParameterizedTest + void 순서는_양수만_가능하다(int sequence) { + CustomizeTable table = new CustomizeTable(); + CustomizeBoxType customizeBoxType = CustomizeBoxType.TIME_BASED; + + assertThatThrownBy(() -> new CustomizeTimeBox(table, sequence, Stance.NEUTRAL, "자유토론", + customizeBoxType, 120, 60, "발언자")) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SEQUENCE.getMessage()); + } + } + + @Nested + class ValidateTime { + + @ValueSource(ints = {0, -1}) + @ParameterizedTest + void 시간은_양수만_가능하다(int time) { + CustomizeTable table = new CustomizeTable(); + CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; + + assertThatThrownBy( + () -> new CustomizeTimeBox(table, 1, Stance.CONS, "자유토론", + customizeBoxType, time, 60, "발언자")) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + } + + @Nested + class ValidateSpeaker { + + @Test + void 발언자_이름은_일정길이_이내로_허용된다() { + CustomizeTable table = new CustomizeTable(); + CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; + String speaker = "k".repeat(CustomizeTimeBox.SPEAKER_MAX_LENGTH + 1); + + assertThatThrownBy(() -> new CustomizeTimeBox(table, 1, Stance.CONS, "입론", + customizeBoxType, 120, speaker)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH.getMessage()); + } + + @NullSource + @ParameterizedTest + void 발언자는_빈_값이_허용된다(String speaker) { + CustomizeTable table = new CustomizeTable(); + CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; + + assertThatCode(() -> new CustomizeTimeBox(table, 1, Stance.CONS, "입론", + customizeBoxType, 120, speaker)) + .doesNotThrowAnyException(); + } + + @ValueSource(strings = {" ", " "}) + @ParameterizedTest + void 발언자는_공백이_입력되면_null로_저장된다(String speaker) { + CustomizeTable table = new CustomizeTable(); + CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; + + CustomizeTimeBox timeBox = new CustomizeTimeBox(table, 1, Stance.CONS, "입론", + customizeBoxType, 120, speaker); + + assertThat(timeBox.getSpeaker()).isNull(); + } + } + @Nested class ValidateCustomizeTime { diff --git a/src/test/java/com/debatetimer/domain/TimeBoxesTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java similarity index 78% rename from src/test/java/com/debatetimer/domain/TimeBoxesTest.java rename to src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java index 47602c63..f37f835f 100644 --- a/src/test/java/com/debatetimer/domain/TimeBoxesTest.java +++ b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java @@ -1,10 +1,8 @@ -package com.debatetimer.domain; +package com.debatetimer.domain.customize; import static org.assertj.core.api.Assertions.assertThat; -import com.debatetimer.domain.customize.CustomizeBoxType; -import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.Stance; import com.debatetimer.domain.member.Member; import java.util.ArrayList; import java.util.Arrays; @@ -12,7 +10,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -class TimeBoxesTest { +class CustomizeTimeBoxesTest { @Nested class SortedBySequence { @@ -28,7 +26,7 @@ class SortedBySequence { CustomizeBoxType.NORMAL, 300, "콜리2"); List timeBoxes = new ArrayList<>(Arrays.asList(secondBox, firstBox)); - TimeBoxes actual = new TimeBoxes<>(timeBoxes); + CustomizeTimeBoxes actual = new CustomizeTimeBoxes(timeBoxes); assertThat(actual.getTimeBoxes()).containsExactly(firstBox, secondBox); } From ce816d970d1fcb506ac51740ccc8f1c01e22a032 Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Tue, 20 May 2025 14:32:24 +0900 Subject: [PATCH 06/27] =?UTF-8?q?[CHORE]=20DataDog=EC=9D=84=20=ED=86=B5?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EA=B7=B8=20=EC=88=98=EC=A7=91=20(#178)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logging/log4j2-dev.yml | 6 +++--- src/main/resources/logging/log4j2-prod.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/resources/logging/log4j2-dev.yml b/src/main/resources/logging/log4j2-dev.yml index 5225f07e..608c5f3f 100644 --- a/src/main/resources/logging/log4j2-dev.yml +++ b/src/main/resources/logging/log4j2-dev.yml @@ -5,13 +5,13 @@ Configuration: Properties: Property: name: log-dir - value: "logs" + value: "/home/ubuntu/logs" Appenders: RollingFile: name: RollingFile_Appender fileName: ${log-dir}/logfile.log - filePattern: "${log-dir}logfile-%d{yyyy-MM-dd}.%i.txt" + filePattern: "${log-dir}/logfile-%d{yyyy-MM-dd}.%i.log.gz" PatternLayout: pattern: "%d{yyyy-MM-dd HH:mm:ss} [%X{requestId}] [%thread] [%-5level] %logger{35} - %msg%n" immediateFlush: false #false로 설정되어야 Async로 buffer에 저장됨 @@ -29,7 +29,7 @@ Configuration: basePath: "${log-dir}" maxDepth: "1" IfLastModified: - age: "P7D" + age: "P7D" #7일간 데이터는 저장됨 Loggers: Root: diff --git a/src/main/resources/logging/log4j2-prod.yml b/src/main/resources/logging/log4j2-prod.yml index dee42005..222520d3 100644 --- a/src/main/resources/logging/log4j2-prod.yml +++ b/src/main/resources/logging/log4j2-prod.yml @@ -5,13 +5,13 @@ Configuration: Properties: Property: name: log-dir - value: "logs" + value: "/home/ubuntu/logs" Appenders: RollingFile: name: RollingFile_Appender fileName: ${log-dir}/logfile.log - filePattern: "${log-dir}logfile-%d{yyyy-MM-dd}.%i.txt" + filePattern: "${log-dir}/logfile-%d{yyyy-MM-dd}.%i.log.gz" PatternLayout: pattern: "%d{yyyy-MM-dd HH:mm:ss} [%X{requestId}] [%thread] [%-5level] %logger{35} - %msg%n" immediateFlush: false #false로 설정되어야 Async로 buffer에 저장됨 From 2c25f128d3831951f517a1feae7256c5a45486e3 Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Wed, 28 May 2025 09:53:14 +0900 Subject: [PATCH 07/27] =?UTF-8?q?[CHORE]=20DataDog=EC=9D=84=20=ED=86=B5?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EA=B7=B8=20=EC=88=98=EC=A7=91=20(#179)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logging/log4j2-dev.yml | 9 +++++++-- src/main/resources/logging/log4j2-prod.yml | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/resources/logging/log4j2-dev.yml b/src/main/resources/logging/log4j2-dev.yml index 608c5f3f..71ab44f3 100644 --- a/src/main/resources/logging/log4j2-dev.yml +++ b/src/main/resources/logging/log4j2-dev.yml @@ -12,8 +12,13 @@ Configuration: name: RollingFile_Appender fileName: ${log-dir}/logfile.log filePattern: "${log-dir}/logfile-%d{yyyy-MM-dd}.%i.log.gz" - PatternLayout: - pattern: "%d{yyyy-MM-dd HH:mm:ss} [%X{requestId}] [%thread] [%-5level] %logger{35} - %msg%n" + JsonLayout: + complete: false + compact: true + eventEol: true + properties: true + stacktraceAsString: true + objectMessageAsJsonObject: true immediateFlush: false #false로 설정되어야 Async로 buffer에 저장됨 Policies: diff --git a/src/main/resources/logging/log4j2-prod.yml b/src/main/resources/logging/log4j2-prod.yml index 222520d3..a2e5f0f6 100644 --- a/src/main/resources/logging/log4j2-prod.yml +++ b/src/main/resources/logging/log4j2-prod.yml @@ -12,8 +12,13 @@ Configuration: name: RollingFile_Appender fileName: ${log-dir}/logfile.log filePattern: "${log-dir}/logfile-%d{yyyy-MM-dd}.%i.log.gz" - PatternLayout: - pattern: "%d{yyyy-MM-dd HH:mm:ss} [%X{requestId}] [%thread] [%-5level] %logger{35} - %msg%n" + JsonLayout: + complete: false + compact: true + eventEol: true + properties: true + stacktraceAsString: true + objectMessageAsJsonObject: true immediateFlush: false #false로 설정되어야 Async로 buffer에 저장됨 Policies: From ec33f589aaef8218d804d5eccacc3c6af7e24c6f Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Wed, 28 May 2025 10:01:04 +0900 Subject: [PATCH 08/27] =?UTF-8?q?[FEAT]=20MultipartException=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20(#180)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../debatetimer/exception/errorcode/ClientErrorCode.java | 4 +++- .../exception/handler/GlobalExceptionHandler.java | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java index 346ebbb0..cd341416 100644 --- a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java +++ b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java @@ -57,7 +57,9 @@ public enum ClientErrorCode implements ResponseErrorCode { METHOD_NOT_SUPPORTED(HttpStatus.METHOD_NOT_ALLOWED, "허용되지 않은 메서드입니다."), MEDIA_TYPE_NOT_SUPPORTED(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "허용되지 않은 미디어 타입입니다."), ALREADY_DISCONNECTED(HttpStatus.BAD_REQUEST, "이미 클라이언트에서 요청이 종료되었습니다."), - NO_COOKIE_FOUND(HttpStatus.BAD_REQUEST, "필수 쿠키 값이 존재하지 않습니다."); + NO_COOKIE_FOUND(HttpStatus.BAD_REQUEST, "필수 쿠키 값이 존재하지 않습니다."), + FILE_UPLOAD_ERROR(HttpStatus.BAD_REQUEST, "파일 업로드에 실패했습니다."), + ; private final HttpStatus status; private final String message; diff --git a/src/main/java/com/debatetimer/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/debatetimer/exception/handler/GlobalExceptionHandler.java index e5d107e4..295c6ef0 100644 --- a/src/main/java/com/debatetimer/exception/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/debatetimer/exception/handler/GlobalExceptionHandler.java @@ -23,6 +23,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.context.request.async.AsyncRequestNotUsableException; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.multipart.MultipartException; import org.springframework.web.servlet.resource.NoResourceFoundException; @Slf4j @@ -101,6 +102,12 @@ public ResponseEntity handleMissingRequestCookieException(Missing return toResponse(ClientErrorCode.NO_COOKIE_FOUND); } + @ExceptionHandler(MultipartException.class) + public ResponseEntity handleMultipartException(MultipartException exception) { + logClientError(exception); + return toResponse(ClientErrorCode.FILE_UPLOAD_ERROR); + } + @ExceptionHandler(DTOAuthClientException.class) public ResponseEntity handleOAuthClientException(DTOAuthClientException exception) { logClientError(exception); From 1217bc7c78fa856595a1439c1b0e493745dcb618 Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Sat, 31 May 2025 15:55:41 +0900 Subject: [PATCH 09/27] =?UTF-8?q?[REFACTOR]=20=ED=83=80=EC=9E=84=20?= =?UTF-8?q?=EB=B0=95=EC=8A=A4=20=EC=82=AD=EC=A0=9C&=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=8F=99=EC=8B=9C=EC=84=B1=20=EC=9D=B4=EC=8A=88=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=20(#184)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customize/CustomizeTableRepository.java | 12 +++--- .../customize/CustomizeTimeBoxRepository.java | 5 +-- .../service/customize/CustomizeService.java | 20 +++------ .../CustomizeTableRepositoryTest.java | 28 +++++++++++++ .../CustomizeTimeBoxRepositoryTest.java | 42 +++++++++++++++++++ 5 files changed, 84 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java b/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java index d92536a0..5653a9b1 100644 --- a/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java @@ -2,11 +2,10 @@ import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; -import jakarta.persistence.LockModeType; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; import java.util.List; import java.util.Optional; -import org.springframework.data.jpa.repository.Lock; -import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; public interface CustomizeTableRepository extends Repository { @@ -19,9 +18,10 @@ public interface CustomizeTableRepository extends Repository findByIdAndMember(long tableId, Member member); - @Lock(LockModeType.PESSIMISTIC_WRITE) - @Query("SELECT c FROM CustomizeTable c WHERE c.id = :id AND c.member = :member") - Optional findByIdAndMemberWithLock(long id, Member member); + default CustomizeTable getByIdAndMember(long tableId, Member member) { + return findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + } void delete(CustomizeTable table); } diff --git a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java index 8f20829f..67e947a9 100644 --- a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java @@ -27,8 +27,7 @@ default CustomizeTimeBoxes findTableTimeBoxes(CustomizeTable table) { return new CustomizeTimeBoxes(timeBoxes); } - @Query("DELETE FROM CustomizeTimeBox ctb WHERE ctb IN :timeBoxes") + @Query("DELETE FROM CustomizeTimeBox ctb WHERE ctb.customizeTable = :table") @Modifying(clearAutomatically = true, flushAutomatically = true) - @Transactional - void deleteAll(List timeBoxes); + void deleteAllByTable(CustomizeTable table); } diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeService.java b/src/main/java/com/debatetimer/service/customize/CustomizeService.java index bc568eeb..8f839c65 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeService.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeService.java @@ -6,8 +6,6 @@ import com.debatetimer.domain.member.Member; import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; import com.debatetimer.dto.customize.response.CustomizeTableResponse; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import java.util.List; @@ -33,8 +31,7 @@ public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateReques @Transactional(readOnly = true) public CustomizeTableResponse findTable(long tableId, Member member) { - CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) - .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + CustomizeTable table = tableRepository.getByIdAndMember(tableId, member); CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); return new CustomizeTableResponse(table, timeBoxes); } @@ -45,21 +42,18 @@ public CustomizeTableResponse updateTable( long tableId, Member member ) { - CustomizeTable existingTable = tableRepository.findByIdAndMemberWithLock(tableId, member) - .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + CustomizeTable existingTable = tableRepository.getByIdAndMember(tableId, member); CustomizeTable renewedTable = tableCreateRequest.toTable(member); existingTable.updateTable(renewedTable); - CustomizeTimeBoxes customizeTimeBoxes = timeBoxRepository.findTableTimeBoxes(existingTable); - timeBoxRepository.deleteAll(customizeTimeBoxes.getTimeBoxes()); + timeBoxRepository.deleteAllByTable(existingTable); CustomizeTimeBoxes savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, existingTable); return new CustomizeTableResponse(existingTable, savedCustomizeTimeBoxes); } @Transactional public CustomizeTableResponse updateUsedAt(long tableId, Member member) { - CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) - .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + CustomizeTable table = tableRepository.getByIdAndMember(tableId, member); CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); table.updateUsedAt(); @@ -68,10 +62,8 @@ public CustomizeTableResponse updateUsedAt(long tableId, Member member) { @Transactional public void deleteTable(long tableId, Member member) { - CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) - .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); - CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); - timeBoxRepository.deleteAll(timeBoxes.getTimeBoxes()); + CustomizeTable table = tableRepository.getByIdAndMember(tableId, member); + timeBoxRepository.deleteAllByTable(table); tableRepository.delete(table); } diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java index 69482003..07b70037 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java @@ -1,9 +1,12 @@ package com.debatetimer.repository.customize; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; 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.BaseRepositoryTest; import java.util.List; import org.junit.jupiter.api.Nested; @@ -31,4 +34,29 @@ class FindAllByMember { assertThat(foundKeoChanTables).containsExactly(chanTable1, chanTable2); } } + + @Nested + class GetByIdAndMember { + + @Test + void 특정_회원의_테이블을_조회한다() { + Member chan = memberGenerator.generate("default@gmail.com"); + CustomizeTable table = customizeTableGenerator.generate(chan); + + CustomizeTable foundTable = tableRepository.getByIdAndMember(table.getId(), chan); + + assertThat(foundTable).isEqualTo(table); + } + + @Test + void 존재하지_않는_테이블을_조회하면_예외를_던진다() { + Member chan = memberGenerator.generate("default@gmail.com"); + customizeTableGenerator.generate(chan); + long nonExistTableId = 99999999L; + + assertThatThrownBy(() -> tableRepository.getByIdAndMember(nonExistTableId, chan)) + .isInstanceOf(DTClientErrorException.class) + .hasMessageContaining(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); + } + } } diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java index 151b62ee..8cf1aa1b 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java @@ -1,6 +1,7 @@ package com.debatetimer.repository.customize; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.CustomizeTable; @@ -36,4 +37,45 @@ class FindAllByCustomizeTable { assertThat(foundBoxes).containsExactly(chanBox1, chanBox2); } } + + @Nested + class DeleteAllByTable { + + @Test + void 특정_테이블의_타임박스를_모두_삭제한다() { + Member chan = memberGenerator.generate("default@gmail.com"); + CustomizeTable chanTable = customizeTableGenerator.generate(chan); + customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); + customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); + + customizeTimeBoxRepository.deleteAllByTable(chanTable); + + List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(chanTable); + assertThat(timeBoxes).isEmpty(); + } + + @Test + void 특정_테이블의_타임_박스를_삭제해도_다른_테이블의_타임_박스는_삭제되지_않는다() { + Member chan = memberGenerator.generate("default@gmail.com"); + CustomizeTable filledTable = customizeTableGenerator.generate(chan); + customizeTimeBoxGenerator.generate(filledTable, CustomizeBoxType.NORMAL, 1); + customizeTimeBoxGenerator.generate(filledTable, CustomizeBoxType.NORMAL, 2); + CustomizeTable deletedTable = customizeTableGenerator.generate(chan); + customizeTimeBoxGenerator.generate(deletedTable, CustomizeBoxType.NORMAL, 1); + + customizeTimeBoxRepository.deleteAllByTable(deletedTable); + + List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(filledTable); + assertThat(timeBoxes).hasSize(2); + } + + @Test + void 테이블의_타임_박스가_없을_경우_타임_박스_삭제_시_예외가_발생하지_않는다() { + Member chan = memberGenerator.generate("default@gmail.com"); + CustomizeTable emptyTable = customizeTableGenerator.generate(chan); + + assertThatCode(() -> customizeTimeBoxRepository.deleteAllByTable(emptyTable)) + .doesNotThrowAnyException(); + } + } } From c9778c37241eb1c5efcd5ab8bb48b2c7d03af0b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B1=B4=EC=9A=B0?= Date: Thu, 12 Jun 2025 17:13:14 +0900 Subject: [PATCH 10/27] =?UTF-8?q?[FIX]=20modified=5Fat=20=EB=AF=B8?= =?UTF-8?q?=EA=B0=B1=EC=8B=A0=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0=20?= =?UTF-8?q?(#187)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/debatetimer/domain/customize/CustomizeTable.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java index ff487570..a35cf613 100644 --- a/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java +++ b/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java @@ -1,5 +1,6 @@ package com.debatetimer.domain.customize; +import com.debatetimer.domain.BaseTimeEntity; import com.debatetimer.domain.member.Member; import com.debatetimer.dto.member.TableType; import com.debatetimer.exception.custom.DTClientErrorException; @@ -21,7 +22,7 @@ @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class CustomizeTable { +public class CustomizeTable extends BaseTimeEntity { private static final String TABLE_NAME_REGEX = "^[\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\s]+$"; private static final String TEAM_NAME_REGEX = "^[\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\s]+$"; From 8002da94f3660a179303f8277403f8ed380dae4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B1=B4=EC=9A=B0?= Date: Fri, 27 Jun 2025 16:03:57 +0900 Subject: [PATCH 11/27] =?UTF-8?q?[REFACTOR]=20=ED=85=8C=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=20=EB=8F=84=EB=A9=94=EC=9D=B8-=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: SANGHUN OH <121424793+unifolio0@users.noreply.github.com> Co-authored-by: unifolio0 --- .../debatetimer/domain/customize/Agenda.java | 28 +++ .../domain/customize/CustomizeTable.java | 128 +++++-------- .../domain/customize/CustomizeTimeBoxes.java | 1 + .../domain/{ => customize}/Stance.java | 2 +- .../domain/customize/TableName.java | 28 +++ .../domain/customize/TeamName.java | 28 +++ .../com/debatetimer/domain/member/Member.java | 2 +- .../CustomizeTableInfoCreateRequest.java | 4 +- .../CustomizeTimeBoxCreateRequest.java | 12 +- .../response/CustomizeTableResponse.java | 9 +- .../response/CustomizeTimeBoxResponse.java | 4 +- .../debatetimer/dto/member/TableResponse.java | 10 +- .../customize}/BaseTimeEntity.java | 2 +- .../customize/CustomizeTableEntity.java | 96 ++++++++++ .../customize/CustomizeTimeBox.java | 15 +- .../exception/errorcode/ClientErrorCode.java | 14 +- .../customize/CustomizeTableRepository.java | 16 +- .../customize/CustomizeTimeBoxRepository.java | 10 +- .../service/customize/CustomizeService.java | 38 ++-- .../service/member/MemberService.java | 10 +- .../controller/BaseControllerTest.java | 2 +- .../customize/CustomizeControllerTest.java | 28 +-- .../customize/CustomizeDocumentTest.java | 4 +- .../member/MemberControllerTest.java | 8 +- .../domain/customize/AgendaTest.java | 23 +++ .../domain/customize/CustomizeTableTest.java | 174 ------------------ .../customize/CustomizeTimeBoxesTest.java | 9 +- .../domain/customize/TableNameTest.java | 42 +++++ .../domain/customize/TeamNameTest.java | 53 ++++++ .../customize/CustomizeTableEntityTest.java | 82 +++++++++ .../customize/CustomizeTimeBoxTest.java | 31 ++-- .../fixture/CustomizeTableGenerator.java | 12 +- .../fixture/CustomizeTimeBoxGenerator.java | 11 +- ...> CustomizeTableEntityRepositoryTest.java} | 12 +- .../CustomizeTimeBoxRepositoryTest.java | 18 +- .../customize/CustomizeServiceTest.java | 32 ++-- .../service/member/MemberServiceTest.java | 4 +- 37 files changed, 599 insertions(+), 403 deletions(-) create mode 100644 src/main/java/com/debatetimer/domain/customize/Agenda.java rename src/main/java/com/debatetimer/domain/{ => customize}/Stance.java (58%) create mode 100644 src/main/java/com/debatetimer/domain/customize/TableName.java create mode 100644 src/main/java/com/debatetimer/domain/customize/TeamName.java rename src/main/java/com/debatetimer/{domain => entity/customize}/BaseTimeEntity.java (93%) create mode 100644 src/main/java/com/debatetimer/entity/customize/CustomizeTableEntity.java rename src/main/java/com/debatetimer/{domain => entity}/customize/CustomizeTimeBox.java (92%) create mode 100644 src/test/java/com/debatetimer/domain/customize/AgendaTest.java delete mode 100644 src/test/java/com/debatetimer/domain/customize/CustomizeTableTest.java create mode 100644 src/test/java/com/debatetimer/domain/customize/TableNameTest.java create mode 100644 src/test/java/com/debatetimer/domain/customize/TeamNameTest.java create mode 100644 src/test/java/com/debatetimer/entity/customize/CustomizeTableEntityTest.java rename src/test/java/com/debatetimer/{domain => entity}/customize/CustomizeTimeBoxTest.java (87%) rename src/test/java/com/debatetimer/repository/customize/{CustomizeTableRepositoryTest.java => CustomizeTableEntityRepositoryTest.java} (78%) diff --git a/src/main/java/com/debatetimer/domain/customize/Agenda.java b/src/main/java/com/debatetimer/domain/customize/Agenda.java new file mode 100644 index 00000000..6e0b3571 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/Agenda.java @@ -0,0 +1,28 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import lombok.Getter; + +@Getter +public class Agenda { + + public static final int AGENDA_MAX_LENGTH = 255; + + private final String value; + + public Agenda(String value) { + validate(value); + this.value = value; + } + + private void validate(String value) { + if (value == null) { + return; + } + + if (value.length() > AGENDA_MAX_LENGTH) { + throw new DTClientErrorException(ClientErrorCode.INVALID_AGENDA_LENGTH); + } + } +} diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java index a35cf613..dfb7fe31 100644 --- a/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java +++ b/src/main/java/com/debatetimer/domain/customize/CustomizeTable.java @@ -1,115 +1,75 @@ package com.debatetimer.domain.customize; -import com.debatetimer.domain.BaseTimeEntity; import com.debatetimer.domain.member.Member; import com.debatetimer.dto.member.TableType; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import java.time.LocalDateTime; -import lombok.AccessLevel; import lombok.Getter; -import lombok.NoArgsConstructor; -@Entity @Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class CustomizeTable extends BaseTimeEntity { - - private static final String TABLE_NAME_REGEX = "^[\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\s]+$"; - private static final String TEAM_NAME_REGEX = "^[\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\s]+$"; - public static final int TABLE_NAME_MAX_LENGTH = 20; - public static final int TEAM_NAME_MAX_LENGTH = 8; - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; - - @NotNull - private String name; - - private String agenda; - - @NotBlank - private String prosTeamName; - - @NotBlank - private String consTeamName; - - private boolean warningBell; - private boolean finishBell; - - @NotNull - private LocalDateTime usedAt; +public class CustomizeTable { + + private final Long id; + private final Member member; + private final TableName name; + private final Agenda agenda; + private final TeamName prosTeamName; + private final TeamName consTeamName; + private final boolean warningBell; + private final boolean finishBell; + private final LocalDateTime usedAt; public CustomizeTable( + Long id, Member member, String name, String agenda, + String prosTeamName, + String consTeamName, boolean warningBell, boolean finishBell, - String prosTeamName, - String consTeamName + LocalDateTime usedAt ) { - validateTableName(name); - validateTeamName(prosTeamName); - validateTeamName(consTeamName); + this.id = id; this.member = member; - this.name = name; - this.agenda = agenda; - this.prosTeamName = prosTeamName; - this.consTeamName = consTeamName; + this.name = new TableName(name); + this.agenda = new Agenda(agenda); + this.prosTeamName = new TeamName(prosTeamName); + this.consTeamName = new TeamName(consTeamName); this.warningBell = warningBell; this.finishBell = finishBell; - this.usedAt = LocalDateTime.now(); + this.usedAt = usedAt; } - public void updateTable(CustomizeTable renewTable) { - this.name = renewTable.getName(); - this.agenda = renewTable.getAgenda(); - this.prosTeamName = renewTable.getProsTeamName(); - this.consTeamName = renewTable.getConsTeamName(); - this.warningBell = renewTable.isWarningBell(); - this.finishBell = renewTable.isFinishBell(); - this.usedAt = LocalDateTime.now(); + public CustomizeTable( + Member member, + String name, + String agenda, + String prosTeamName, + String consTeamName, + boolean warningBell, + boolean finishBell, + LocalDateTime usedAt + ) { + this(null, member, name, agenda, prosTeamName, consTeamName, warningBell, finishBell, usedAt); } - public void updateUsedAt() { - this.usedAt = LocalDateTime.now(); + public TableType getType() { + return TableType.CUSTOMIZE; } - private void validateTableName(String name) { - if (name.length() > TABLE_NAME_MAX_LENGTH) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TABLE_NAME_LENGTH); - } - if (!name.matches(TABLE_NAME_REGEX)) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TABLE_NAME_FORM); - } + public String getProsTeamName() { + return prosTeamName.getValue(); } - private void validateTeamName(String teamName) { - if (teamName.length() > TEAM_NAME_MAX_LENGTH) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TEAM_NAME_LENGTH); - } - if (!teamName.matches(TEAM_NAME_REGEX)) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TEAM_NAME_FORM); - } + public String getConsTeamName() { + return consTeamName.getValue(); } - public TableType getType() { - return TableType.CUSTOMIZE; + public String getName() { + return name.getValue(); + } + + public String getAgenda() { + return agenda.getValue(); } } diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java index 7be35232..585c8bd0 100644 --- a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java +++ b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java @@ -1,5 +1,6 @@ package com.debatetimer.domain.customize; +import com.debatetimer.entity.customize.CustomizeTimeBox; import java.util.Comparator; import java.util.List; import lombok.Getter; diff --git a/src/main/java/com/debatetimer/domain/Stance.java b/src/main/java/com/debatetimer/domain/customize/Stance.java similarity index 58% rename from src/main/java/com/debatetimer/domain/Stance.java rename to src/main/java/com/debatetimer/domain/customize/Stance.java index bc9dc559..fd9b0975 100644 --- a/src/main/java/com/debatetimer/domain/Stance.java +++ b/src/main/java/com/debatetimer/domain/customize/Stance.java @@ -1,4 +1,4 @@ -package com.debatetimer.domain; +package com.debatetimer.domain.customize; public enum Stance { diff --git a/src/main/java/com/debatetimer/domain/customize/TableName.java b/src/main/java/com/debatetimer/domain/customize/TableName.java new file mode 100644 index 00000000..4cbcfe33 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/TableName.java @@ -0,0 +1,28 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import lombok.Getter; + +@Getter +public class TableName { + + private static final String NAME_REGEX = "^[\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\s]+$"; + public static final int NAME_MAX_LENGTH = 20; + + private final String value; + + public TableName(String value) { + validate(value); + this.value = value; + } + + private void validate(String name) { + if (name.length() > NAME_MAX_LENGTH) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TABLE_NAME_LENGTH); + } + if (!name.matches(NAME_REGEX)) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TABLE_NAME_FORM); + } + } +} diff --git a/src/main/java/com/debatetimer/domain/customize/TeamName.java b/src/main/java/com/debatetimer/domain/customize/TeamName.java new file mode 100644 index 00000000..352eca26 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/TeamName.java @@ -0,0 +1,28 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import lombok.Getter; + +@Getter +public class TeamName { + + private static final String NAME_REGEX = "^[\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\s]+$"; + public static final int NAME_MAX_LENGTH = 8; + + private final String value; + + public TeamName(String value) { + validate(value); + this.value = value; + } + + private void validate(String teamName) { + if (teamName.isBlank() || teamName.length() > NAME_MAX_LENGTH) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TEAM_NAME_LENGTH); + } + if (!teamName.matches(NAME_REGEX)) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TEAM_NAME_FORM); + } + } +} diff --git a/src/main/java/com/debatetimer/domain/member/Member.java b/src/main/java/com/debatetimer/domain/member/Member.java index 2299c385..9dec2246 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.domain.BaseTimeEntity; +import com.debatetimer.entity.customize.BaseTimeEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; diff --git a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableInfoCreateRequest.java b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableInfoCreateRequest.java index c83a7d5d..d6e6ecd1 100644 --- a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableInfoCreateRequest.java +++ b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableInfoCreateRequest.java @@ -4,6 +4,7 @@ import com.debatetimer.domain.member.Member; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; public record CustomizeTableInfoCreateRequest( @NotBlank @@ -23,6 +24,7 @@ public record CustomizeTableInfoCreateRequest( ) { public CustomizeTable toTable(Member member) { - return new CustomizeTable(member, name, agenda, warningBell, finishBell, prosTeamName, consTeamName); + return new CustomizeTable(member, name, agenda, prosTeamName, consTeamName, warningBell, finishBell, + LocalDateTime.now()); } } diff --git a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java index d5c6d320..b3c125a1 100644 --- a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java +++ b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java @@ -1,9 +1,10 @@ package com.debatetimer.dto.customize.request; -import com.debatetimer.domain.Stance; import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.Stance; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBox; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import org.springframework.lang.Nullable; @@ -33,9 +34,10 @@ public record CustomizeTimeBoxCreateRequest( public CustomizeTimeBox toTimeBox(CustomizeTable customizeTable, int sequence) { if (boxType.isTimeBased()) { - return new CustomizeTimeBox(customizeTable, sequence, stance, speechType, boxType, timePerTeam, - timePerSpeaking, speaker); + return new CustomizeTimeBox(new CustomizeTableEntity(customizeTable), sequence, stance, speechType, + boxType, timePerTeam, timePerSpeaking, speaker); } - return new CustomizeTimeBox(customizeTable, sequence, stance, speechType, boxType, time, speaker); + return new CustomizeTimeBox(new CustomizeTableEntity(customizeTable), sequence, stance, speechType, boxType, + time, speaker); } } diff --git a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java index d074287d..a890db68 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java @@ -1,8 +1,8 @@ package com.debatetimer.dto.customize.response; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBox; import com.debatetimer.domain.customize.CustomizeTimeBoxes; +import com.debatetimer.entity.customize.CustomizeTimeBox; import java.util.List; public record CustomizeTableResponse(long id, CustomizeTableInfoResponse info, List table) { @@ -11,11 +11,8 @@ public CustomizeTableResponse( CustomizeTable customizeTable, CustomizeTimeBoxes customizeTimeBoxes ) { - this( - customizeTable.getId(), - new CustomizeTableInfoResponse(customizeTable), - toTimeBoxResponses(customizeTimeBoxes) - ); + this(customizeTable.getId(), new CustomizeTableInfoResponse(customizeTable), + toTimeBoxResponses(customizeTimeBoxes)); } private static List toTimeBoxResponses(CustomizeTimeBoxes timeBoxes) { diff --git a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java index 113bd23e..cca2eff9 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java @@ -1,8 +1,8 @@ package com.debatetimer.dto.customize.response; -import com.debatetimer.domain.Stance; import com.debatetimer.domain.customize.CustomizeBoxType; -import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.Stance; +import com.debatetimer.entity.customize.CustomizeTimeBox; public record CustomizeTimeBoxResponse( Stance stance, diff --git a/src/main/java/com/debatetimer/dto/member/TableResponse.java b/src/main/java/com/debatetimer/dto/member/TableResponse.java index fe066734..e4c31f7a 100644 --- a/src/main/java/com/debatetimer/dto/member/TableResponse.java +++ b/src/main/java/com/debatetimer/dto/member/TableResponse.java @@ -4,12 +4,12 @@ public record TableResponse(long id, String name, TableType type, String agenda) { - public TableResponse(CustomizeTable debateTable) { + public TableResponse(CustomizeTable customizeTable) { this( - debateTable.getId(), - debateTable.getName(), - debateTable.getType(), - debateTable.getAgenda() + customizeTable.getId(), + customizeTable.getName(), + customizeTable.getType(), + customizeTable.getAgenda() ); } } diff --git a/src/main/java/com/debatetimer/domain/BaseTimeEntity.java b/src/main/java/com/debatetimer/entity/customize/BaseTimeEntity.java similarity index 93% rename from src/main/java/com/debatetimer/domain/BaseTimeEntity.java rename to src/main/java/com/debatetimer/entity/customize/BaseTimeEntity.java index ea8677cc..1bca8c75 100644 --- a/src/main/java/com/debatetimer/domain/BaseTimeEntity.java +++ b/src/main/java/com/debatetimer/entity/customize/BaseTimeEntity.java @@ -1,4 +1,4 @@ -package com.debatetimer.domain; +package com.debatetimer.entity.customize; import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; diff --git a/src/main/java/com/debatetimer/entity/customize/CustomizeTableEntity.java b/src/main/java/com/debatetimer/entity/customize/CustomizeTableEntity.java new file mode 100644 index 00000000..88cfd3bb --- /dev/null +++ b/src/main/java/com/debatetimer/entity/customize/CustomizeTableEntity.java @@ -0,0 +1,96 @@ +package com.debatetimer.entity.customize; + +import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.member.Member; +import com.debatetimer.dto.member.TableType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Table(name = "customize_table") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class CustomizeTableEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @NotBlank + private String name; + + private String agenda; + + @NotBlank + private String prosTeamName; + + @NotBlank + private String consTeamName; + + private boolean warningBell; + private boolean finishBell; + + @NotNull + private LocalDateTime usedAt; + + public CustomizeTableEntity(CustomizeTable customizeTable) { + this.id = customizeTable.getId(); + this.member = customizeTable.getMember(); + this.name = customizeTable.getName(); + this.agenda = customizeTable.getAgenda(); + this.prosTeamName = customizeTable.getProsTeamName(); + this.consTeamName = customizeTable.getConsTeamName(); + this.warningBell = customizeTable.isWarningBell(); + this.finishBell = customizeTable.isFinishBell(); + this.usedAt = LocalDateTime.now(); + } + + public CustomizeTable toDomain() { + return new CustomizeTable( + id, + member, + name, + agenda, + prosTeamName, + consTeamName, + warningBell, + finishBell, + usedAt + ); + } + + public void updateTable(CustomizeTable renewTable) { + this.name = renewTable.getName(); + this.agenda = renewTable.getAgenda(); + this.prosTeamName = renewTable.getProsTeamName(); + this.consTeamName = renewTable.getConsTeamName(); + this.warningBell = renewTable.isWarningBell(); + this.finishBell = renewTable.isFinishBell(); + this.usedAt = LocalDateTime.now(); + } + + public void updateUsedAt() { + this.usedAt = LocalDateTime.now(); + } + + public TableType getType() { + return TableType.CUSTOMIZE; + } +} diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBox.java b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBox.java similarity index 92% rename from src/main/java/com/debatetimer/domain/customize/CustomizeTimeBox.java rename to src/main/java/com/debatetimer/entity/customize/CustomizeTimeBox.java index c580713d..64e85be6 100644 --- a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBox.java +++ b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBox.java @@ -1,6 +1,7 @@ -package com.debatetimer.domain.customize; +package com.debatetimer.entity.customize; -import com.debatetimer.domain.Stance; +import com.debatetimer.domain.customize.CustomizeBoxType; +import com.debatetimer.domain.customize.Stance; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import jakarta.persistence.Entity; @@ -34,7 +35,7 @@ public class CustomizeTimeBox { @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "table_id") - private CustomizeTable customizeTable; + private CustomizeTableEntity customizeTable; private int sequence; @@ -56,7 +57,7 @@ public class CustomizeTimeBox { private Integer timePerSpeaking; public CustomizeTimeBox( - CustomizeTable customizeTable, + CustomizeTableEntity customizeTableEntity, int sequence, Stance stance, String speechType, @@ -70,7 +71,7 @@ public CustomizeTimeBox( validateSequence(sequence); validateTime(time); - this.customizeTable = customizeTable; + this.customizeTable = customizeTableEntity; this.sequence = sequence; this.stance = stance; this.time = time; @@ -80,7 +81,7 @@ public CustomizeTimeBox( } public CustomizeTimeBox( - CustomizeTable customizeTable, + CustomizeTableEntity customizeTableEntity, int sequence, Stance stance, String speechType, @@ -100,7 +101,7 @@ public CustomizeTimeBox( this.stance = stance; this.time = convertToTime(timePerTeam); this.speaker = initializeSpeaker(speaker); - this.customizeTable = customizeTable; + this.customizeTable = customizeTableEntity; this.speechType = speechType; this.boxType = boxType; this.timePerTeam = timePerTeam; diff --git a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java index cd341416..35fee335 100644 --- a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java +++ b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java @@ -1,7 +1,9 @@ package com.debatetimer.exception.errorcode; -import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.Agenda; +import com.debatetimer.domain.customize.TableName; +import com.debatetimer.domain.customize.TeamName; +import com.debatetimer.entity.customize.CustomizeTimeBox; import lombok.Getter; import org.springframework.http.HttpStatus; @@ -10,7 +12,7 @@ public enum ClientErrorCode implements ResponseErrorCode { INVALID_TABLE_NAME_LENGTH( HttpStatus.BAD_REQUEST, - "테이블 이름은 1자 이상 %d자 이하여야 합니다".formatted(CustomizeTable.TABLE_NAME_MAX_LENGTH) + "테이블 이름은 1자 이상 %d자 이하여야 합니다".formatted(TableName.NAME_MAX_LENGTH) ), INVALID_TABLE_NAME_FORM( HttpStatus.BAD_REQUEST, @@ -34,12 +36,16 @@ public enum ClientErrorCode implements ResponseErrorCode { ), INVALID_TEAM_NAME_LENGTH( HttpStatus.BAD_REQUEST, - "팀 이름은 1자 이상 %d자 이하여야 합니다.".formatted(CustomizeTable.TEAM_NAME_MAX_LENGTH) + "팀 이름은 1자 이상 %d자 이하여야 합니다.".formatted(TeamName.NAME_MAX_LENGTH) ), INVALID_TEAM_NAME_FORM( HttpStatus.BAD_REQUEST, "팀 이름에 이모지를 넣을 수 없습니다" ), + INVALID_AGENDA_LENGTH( + HttpStatus.BAD_REQUEST, + "토론 주제는 1자 이상 %d자 이하여야 합니다.".formatted(Agenda.AGENDA_MAX_LENGTH) + ), TABLE_NOT_FOUND(HttpStatus.NOT_FOUND, "토론 테이블을 찾을 수 없습니다."), NOT_TABLE_OWNER(HttpStatus.UNAUTHORIZED, "테이블을 소유한 회원이 아닙니다."), diff --git a/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java b/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java index 5653a9b1..bf00c3c8 100644 --- a/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/CustomizeTableRepository.java @@ -1,27 +1,27 @@ package com.debatetimer.repository.customize; -import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; +import com.debatetimer.entity.customize.CustomizeTableEntity; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import java.util.List; import java.util.Optional; import org.springframework.data.repository.Repository; -public interface CustomizeTableRepository extends Repository { +public interface CustomizeTableRepository extends Repository { - CustomizeTable save(CustomizeTable customizeTable); + CustomizeTableEntity save(CustomizeTableEntity customizeTableEntity); - Optional findById(long id); + Optional findById(long id); - List findAllByMember(Member member); + List findAllByMember(Member member); - Optional findByIdAndMember(long tableId, Member member); + Optional findByIdAndMember(long tableId, Member member); - default CustomizeTable getByIdAndMember(long tableId, Member member) { + default CustomizeTableEntity getByIdAndMember(long tableId, Member member) { return findByIdAndMember(tableId, member) .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); } - void delete(CustomizeTable table); + void delete(CustomizeTableEntity table); } diff --git a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java index 67e947a9..aeae7418 100644 --- a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java @@ -1,8 +1,8 @@ package com.debatetimer.repository.customize; -import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBox; import com.debatetimer.domain.customize.CustomizeTimeBoxes; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBox; import java.util.List; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -20,14 +20,14 @@ default List saveAll(List timeBoxes) { .toList(); } - List findAllByCustomizeTable(CustomizeTable table); + List findAllByCustomizeTable(CustomizeTableEntity table); - default CustomizeTimeBoxes findTableTimeBoxes(CustomizeTable table) { + default CustomizeTimeBoxes findTableTimeBoxes(CustomizeTableEntity table) { List timeBoxes = findAllByCustomizeTable(table); return new CustomizeTimeBoxes(timeBoxes); } @Query("DELETE FROM CustomizeTimeBox ctb WHERE ctb.customizeTable = :table") @Modifying(clearAutomatically = true, flushAutomatically = true) - void deleteAllByTable(CustomizeTable table); + void deleteAllByTable(CustomizeTableEntity table); } diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeService.java b/src/main/java/com/debatetimer/service/customize/CustomizeService.java index 8f839c65..840a60b0 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeService.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeService.java @@ -1,11 +1,13 @@ package com.debatetimer.service.customize; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBox; import com.debatetimer.domain.customize.CustomizeTimeBoxes; 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.CustomizeTableEntity; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import java.util.List; @@ -23,17 +25,18 @@ public class CustomizeService { @Transactional public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateRequest, Member member) { CustomizeTable table = tableCreateRequest.toTable(member); - CustomizeTable savedTable = tableRepository.save(table); + CustomizeTableEntity savedTable = tableRepository.save(new CustomizeTableEntity(table)); - CustomizeTimeBoxes savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, savedTable); - return new CustomizeTableResponse(savedTable, savedCustomizeTimeBoxes); + CustomizeTimeBoxes savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, savedTable.toDomain()); + return new CustomizeTableResponse(savedTable.toDomain(), savedCustomizeTimeBoxes); } @Transactional(readOnly = true) public CustomizeTableResponse findTable(long tableId, Member member) { - CustomizeTable table = tableRepository.getByIdAndMember(tableId, member); - CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); - return new CustomizeTableResponse(table, timeBoxes); + CustomizeTableEntity tableEntity = tableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); + return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxes); } @Transactional @@ -42,27 +45,29 @@ public CustomizeTableResponse updateTable( long tableId, Member member ) { - CustomizeTable existingTable = tableRepository.getByIdAndMember(tableId, member); + CustomizeTableEntity existingTable = tableRepository.getByIdAndMember(tableId, member); CustomizeTable renewedTable = tableCreateRequest.toTable(member); existingTable.updateTable(renewedTable); timeBoxRepository.deleteAllByTable(existingTable); - CustomizeTimeBoxes savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, existingTable); - return new CustomizeTableResponse(existingTable, savedCustomizeTimeBoxes); + CustomizeTimeBoxes savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, existingTable.toDomain()); + return new CustomizeTableResponse(existingTable.toDomain(), savedCustomizeTimeBoxes); } @Transactional public CustomizeTableResponse updateUsedAt(long tableId, Member member) { - CustomizeTable table = tableRepository.getByIdAndMember(tableId, member); - CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); - table.updateUsedAt(); + CustomizeTableEntity tableEntity = tableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); + tableEntity.updateUsedAt(); - return new CustomizeTableResponse(table, timeBoxes); + return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxes); } @Transactional public void deleteTable(long tableId, Member member) { - CustomizeTable table = tableRepository.getByIdAndMember(tableId, member); + CustomizeTableEntity table = tableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); timeBoxRepository.deleteAllByTable(table); tableRepository.delete(table); } @@ -72,7 +77,8 @@ private CustomizeTimeBoxes saveTimeBoxes( CustomizeTable table ) { CustomizeTimeBoxes customizeTimeBoxes = tableCreateRequest.toTimeBoxes(table); - List savedTimeBoxes = timeBoxRepository.saveAll(customizeTimeBoxes.getTimeBoxes()); + List savedTimeBoxes = timeBoxRepository.saveAll( + customizeTimeBoxes.getTimeBoxes()); return new CustomizeTimeBoxes(savedTimeBoxes); } } diff --git a/src/main/java/com/debatetimer/service/member/MemberService.java b/src/main/java/com/debatetimer/service/member/MemberService.java index f5634e8b..88aba887 100644 --- a/src/main/java/com/debatetimer/service/member/MemberService.java +++ b/src/main/java/com/debatetimer/service/member/MemberService.java @@ -1,13 +1,13 @@ package com.debatetimer.service.member; -import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; import com.debatetimer.dto.member.MemberCreateResponse; import com.debatetimer.dto.member.MemberInfo; import com.debatetimer.dto.member.TableResponses; +import com.debatetimer.entity.customize.CustomizeTableEntity; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.member.MemberRepository; -import java.util.List; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,8 +22,10 @@ public class MemberService { @Transactional(readOnly = true) public TableResponses getTables(long memberId) { Member member = memberRepository.getById(memberId); - List memberTables = customizeTableRepository.findAllByMember(member); - return TableResponses.from(memberTables); + return customizeTableRepository.findAllByMember(member) + .stream() + .map(CustomizeTableEntity::toDomain) + .collect(Collectors.collectingAndThen(Collectors.toList(), TableResponses::from)); } @Transactional diff --git a/src/test/java/com/debatetimer/controller/BaseControllerTest.java b/src/test/java/com/debatetimer/controller/BaseControllerTest.java index 0ccdab51..78976392 100644 --- a/src/test/java/com/debatetimer/controller/BaseControllerTest.java +++ b/src/test/java/com/debatetimer/controller/BaseControllerTest.java @@ -2,8 +2,8 @@ import com.debatetimer.DataBaseCleaner; import com.debatetimer.client.oauth.OAuthClient; -import com.debatetimer.domain.Stance; import com.debatetimer.domain.customize.CustomizeBoxType; +import com.debatetimer.domain.customize.Stance; import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; import com.debatetimer.dto.customize.request.CustomizeTableInfoCreateRequest; import com.debatetimer.dto.customize.request.CustomizeTimeBoxCreateRequest; diff --git a/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java b/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java index 06493950..e82c3c5e 100644 --- a/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java +++ b/src/test/java/com/debatetimer/controller/customize/CustomizeControllerTest.java @@ -4,12 +4,12 @@ import static org.junit.jupiter.api.Assertions.assertAll; import com.debatetimer.controller.BaseControllerTest; -import com.debatetimer.domain.Stance; import com.debatetimer.domain.customize.CustomizeBoxType; -import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.customize.Stance; 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.CustomizeTableEntity; import com.debatetimer.exception.ErrorResponse; import com.debatetimer.exception.errorcode.ClientErrorCode; import com.debatetimer.fixture.NullAndEmptyAndBlankSource; @@ -154,7 +154,7 @@ class GetTable { @Test void 사용자_지정_테이블을_조회한다() { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 1); customizeTimeBoxGenerator.generateNotExistSpeaker(bitoTable, CustomizeBoxType.NORMAL, 2); Headers headers = headerGenerator.generateAccessTokenHeader(bito); @@ -180,7 +180,7 @@ class UpdateTable { @Test void 사용자_지정_토론_테이블을_업데이트한다() { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.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"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.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"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.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"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.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"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.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"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.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"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.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"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); Headers headers = headerGenerator.generateAccessTokenHeader(bito); CustomizeTableCreateRequest request = getCustomizeTableCreateRequestBuilder() .set("table[0].boxType", boxType) @@ -320,7 +320,7 @@ class UpdateTable { private ValidatableResponse sendCustomizeTableUpdateRequest( CustomizeTableCreateRequest request, HttpStatus statusCode, - CustomizeTable table, + CustomizeTableEntity table, Headers headers ) { return given() @@ -339,7 +339,7 @@ class Debate { @Test void 사용자_지정_토론을_시작한다() { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 1); customizeTimeBoxGenerator.generateNotExistSpeaker(bitoTable, CustomizeBoxType.NORMAL, 2); Headers headers = headerGenerator.generateAccessTokenHeader(bito); @@ -366,7 +366,7 @@ class DeleteTable { @Test void 사용자_지정_토론_테이블을_삭제한다() { Member bito = memberGenerator.generate("default@gmail.com"); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 1); customizeTimeBoxGenerator.generateNotExistSpeaker(bitoTable, CustomizeBoxType.NORMAL, 2); Headers headers = headerGenerator.generateAccessTokenHeader(bito); diff --git a/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java b/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java index 180afad7..c14b37fd 100644 --- a/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java +++ b/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java @@ -18,8 +18,8 @@ import com.debatetimer.controller.RestDocumentationRequest; import com.debatetimer.controller.RestDocumentationResponse; import com.debatetimer.controller.Tag; -import com.debatetimer.domain.Stance; import com.debatetimer.domain.customize.CustomizeBoxType; +import com.debatetimer.domain.customize.Stance; import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; import com.debatetimer.dto.customize.request.CustomizeTableInfoCreateRequest; import com.debatetimer.dto.customize.request.CustomizeTimeBoxCreateRequest; @@ -146,6 +146,7 @@ class Save { "INVALID_TABLE_NAME_LENGTH", "INVALID_TABLE_NAME_FORM", "INVALID_TABLE_TIME", + "INVALID_AGENDA_LENGTH", "INVALID_TIME_BOX_SEQUENCE", "INVALID_TIME_BOX_TIME", "INVALID_TIME_BOX_STANCE", @@ -391,6 +392,7 @@ class UpdateTable { "INVALID_TABLE_NAME_LENGTH", "INVALID_TABLE_NAME_FORM", "INVALID_TABLE_TIME", + "INVALID_AGENDA_LENGTH", "INVALID_TIME_BOX_SEQUENCE", "INVALID_TIME_BOX_TIME", "INVALID_TIME_BOX_STANCE", diff --git a/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java b/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java index b0a59bca..01a972e9 100644 --- a/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java +++ b/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java @@ -10,12 +10,14 @@ import com.debatetimer.dto.member.MemberInfo; import com.debatetimer.dto.member.OAuthToken; import com.debatetimer.dto.member.TableResponses; +import com.debatetimer.entity.customize.CustomizeTableEntity; import com.debatetimer.exception.ErrorResponse; import com.debatetimer.exception.errorcode.ClientErrorCode; import com.debatetimer.fixture.NullAndEmptyAndBlankSource; import io.restassured.http.ContentType; import io.restassured.http.Headers; import io.restassured.response.ValidatableResponse; +import java.time.LocalDateTime; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -29,9 +31,9 @@ class GetTables { @Test void 회원의_전체_토론_시간표를_조회한다() { Member member = memberGenerator.generate("default@gmail.com"); - customizeTableRepository.save(new CustomizeTable(member, "커스텀 테이블", "주제", false, false, - "찬성", "반대")); - + CustomizeTable table = new CustomizeTable(member, "커스텀 테이블", "주제", "찬성", "반대", false, false, + LocalDateTime.now()); + customizeTableRepository.save(new CustomizeTableEntity(table)); Headers headers = headerGenerator.generateAccessTokenHeader(member); TableResponses response = given() diff --git a/src/test/java/com/debatetimer/domain/customize/AgendaTest.java b/src/test/java/com/debatetimer/domain/customize/AgendaTest.java new file mode 100644 index 00000000..37a2050b --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/AgendaTest.java @@ -0,0 +1,23 @@ +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 AgendaTest { + + @Nested + class Validate { + @Test + void 테이블_이름은_정해진_길이_이내여야_한다() { + String longAgenda = "f".repeat(Agenda.AGENDA_MAX_LENGTH + 1); + + assertThatThrownBy(() -> new Agenda(longAgenda)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_AGENDA_LENGTH.getMessage()); + } + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/CustomizeTableTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTableTest.java deleted file mode 100644 index c95d5dda..00000000 --- a/src/test/java/com/debatetimer/domain/customize/CustomizeTableTest.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.debatetimer.domain.customize; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -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.dto.member.TableType; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import java.time.LocalDateTime; -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; - -class CustomizeTableTest { - - @Nested - class GetType { - - @Test - void 사용자_지정_테이블_타입을_반환한다() { - CustomizeTable customizeTable = new CustomizeTable(); - - assertThat(customizeTable.getType()).isEqualTo(TableType.CUSTOMIZE); - } - } - - @Nested - class ValidateTableName { - - @ValueSource(strings = {"a bc가다9", "가0나 다ab", "ㄱㄷㅇㄹ", "漢字", "にほんご", "vielfältig"}) - @ParameterizedTest - void 테이블_이름은_이모지를_제외한_글자만_가능하다(String name) { - Member member = new Member("default@gmail.com"); - assertThatCode(() -> new CustomizeTable(member, name, "agenda", true, true, "pros", "cons")) - .doesNotThrowAnyException(); - } - - @ValueSource(strings = {"a😀bc가다9", "🐥", "🥦"}) - @ParameterizedTest - void 테이블_이름에_이모지를_넣을_수_없다(String name) { - Member member = new Member("default@gmail.com"); - assertThatThrownBy(() -> new CustomizeTable(member, name, "agenda", true, true, "pros", "cons")) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TABLE_NAME_FORM.getMessage()); - } - - @Test - void 테이블_이름은_정해진_길이_이내여야_한다() { - Member member = new Member("default@gmail.com"); - String longTableName = "f".repeat(CustomizeTable.TABLE_NAME_MAX_LENGTH + 1); - - assertThatThrownBy(() -> new CustomizeTable(member, longTableName, "agenda", true, true, "pros", "cons")) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TABLE_NAME_LENGTH.getMessage()); - } - } - - @Nested - class ValidateTeamName { - - @Test - void 찬성_팀_이름은_정해진_길이_이내여야_한다() { - Member member = new Member("default@gmail.com"); - String longProsTeamName = "f".repeat(CustomizeTable.TEAM_NAME_MAX_LENGTH + 1); - - assertThatThrownBy( - () -> new CustomizeTable(member, "name", "agenda", true, true, longProsTeamName, "cons")) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TEAM_NAME_LENGTH.getMessage()); - } - - @Test - void 반대_팀_이름은_정해진_길이_이내여야_한다() { - Member member = new Member("default@gmail.com"); - String longConsTeamName = "f".repeat(CustomizeTable.TEAM_NAME_MAX_LENGTH + 1); - - assertThatThrownBy( - () -> new CustomizeTable(member, "name", "agenda", true, true, "pros", longConsTeamName)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TEAM_NAME_LENGTH.getMessage()); - } - - @ValueSource(strings = {"a bc가다9", "가0나 다ab", "ㄱㄷㅇㄹ", "漢字", "にほんご", "vielfäl"}) - @ParameterizedTest - void 찬성_팀_이름은_이모지를_제외한_글자만_가능하다(String prosName) { - Member member = new Member("default@gmail.com"); - assertThatCode(() -> new CustomizeTable(member, "name", "agenda", true, true, prosName, "cons")) - .doesNotThrowAnyException(); - } - - @ValueSource(strings = {"a😀가다9", "🐥", "🥦"}) - @ParameterizedTest - void 찬성_팀_이름에_이모지를_넣을_수_없다(String prosName) { - Member member = new Member("default@gmail.com"); - assertThatThrownBy(() -> new CustomizeTable(member, "name", "agenda", true, true, prosName, "cons")) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TEAM_NAME_FORM.getMessage()); - } - - @ValueSource(strings = {"a bc가다9", "가0나 다ab", "ㄱㄷㅇㄹ", "漢字", "にほんご", "vielfäl"}) - @ParameterizedTest - void 반대_팀_이름은_이모지를_제외한_글자만_가능하다(String consName) { - Member member = new Member("default@gmail.com"); - assertThatCode(() -> new CustomizeTable(member, "name", "agenda", true, true, "pros", consName)) - .doesNotThrowAnyException(); - } - - @ValueSource(strings = {"a😀가다9", "🐥", "🥦"}) - @ParameterizedTest - void 반대_팀_이름에_이모지를_넣을_수_없다(String consName) { - Member member = new Member("default@gmail.com"); - assertThatThrownBy(() -> new CustomizeTable(member, "name", "agenda", true, true, "pros", consName)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TEAM_NAME_FORM.getMessage()); - } - } - - @Nested - class UpdateUsedAt { - - @Test - void 테이블의_사용_시각을_업데이트한다() throws InterruptedException { - Member member = new Member("default@gmail.com"); - CustomizeTable table = new CustomizeTable(member, "tableName", "agenda", true, true, "찬성", "반대"); - LocalDateTime beforeUsedAt = table.getUsedAt(); - Thread.sleep(1); - - table.updateUsedAt(); - - assertThat(table.getUsedAt()).isAfter(beforeUsedAt); - } - } - - @Nested - class Update { - - @Test - void 테이블_정보를_업데이트_할_수_있다() { - Member member = new Member("default@gmail.com"); - CustomizeTable table = new CustomizeTable(member, "tableName", "agenda", true, true, "pros", "cons"); - CustomizeTable renewTable = new CustomizeTable(member, "newName", "newAgenda", false, false, "newPros", - "newCons"); - - table.updateTable(renewTable); - - assertAll( - () -> assertThat(table.getName()).isEqualTo("newName"), - () -> assertThat(table.getAgenda()).isEqualTo("newAgenda"), - () -> assertThat(table.getProsTeamName()).isEqualTo("newPros"), - () -> assertThat(table.getConsTeamName()).isEqualTo("newCons"), - () -> assertThat(table.isWarningBell()).isFalse(), - () -> assertThat(table.isFinishBell()).isFalse() - ); - } - - @Test - void 테이블_업데이트_할_때_사용_시간을_변경한다() throws InterruptedException { - Member member = new Member("default@gmail.com"); - CustomizeTable table = new CustomizeTable(member, "tableName", "agenda", true, true, "pros", "cons"); - CustomizeTable renewTable = new CustomizeTable(member, "newName", "newAgenda", false, false, "newPros", - "newCons"); - LocalDateTime beforeUsedAt = table.getUsedAt(); - Thread.sleep(1); - - table.updateTable(renewTable); - - assertThat(table.getUsedAt()).isAfter(beforeUsedAt); - } - } -} diff --git a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java index f37f835f..57c3122a 100644 --- a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java +++ b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java @@ -2,8 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; -import com.debatetimer.domain.Stance; import com.debatetimer.domain.member.Member; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBox; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -18,8 +20,9 @@ class SortedBySequence { @Test void 타임박스의_순서에_따라_정렬된다() { Member member = new Member("default@gmail.com"); - CustomizeTable testTable = new CustomizeTable(member, "토론 테이블", "주제", - true, true, "찬성", "반대"); + CustomizeTable table = new CustomizeTable(member, "토론 테이블", "주제", "찬성", + "반대", true, true, LocalDateTime.now()); + CustomizeTableEntity testTable = new CustomizeTableEntity(table); CustomizeTimeBox firstBox = new CustomizeTimeBox(testTable, 1, Stance.PROS, "입론", CustomizeBoxType.NORMAL, 300, "콜리"); CustomizeTimeBox secondBox = new CustomizeTimeBox(testTable, 2, Stance.PROS, "입론", diff --git a/src/test/java/com/debatetimer/domain/customize/TableNameTest.java b/src/test/java/com/debatetimer/domain/customize/TableNameTest.java new file mode 100644 index 00000000..1d160e0c --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/TableNameTest.java @@ -0,0 +1,42 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThatCode; +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; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class TableNameTest { + + @Nested + class Validate { + + @ValueSource(strings = {"a bc가다9", "가0나 다ab", "ㄱㄷㅇㄹ", "漢字", "にほんご", "vielfältig"}) + @ParameterizedTest + void 테이블_이름은_이모지를_제외한_글자만_가능하다(String validName) { + assertThatCode(() -> new TableName(validName)) + .doesNotThrowAnyException(); + } + + @ValueSource(strings = {"a😀bc가다9", "🐥", "🥦"}) + @ParameterizedTest + void 테이블_이름에_이모지를_넣을_수_없다(String invalidName) { + assertThatThrownBy(() -> new TableName(invalidName)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TABLE_NAME_FORM.getMessage()); + } + + @Test + void 테이블_이름은_정해진_길이_이내여야_한다() { + String longTableName = "f".repeat(TableName.NAME_MAX_LENGTH + 1); + + assertThatThrownBy(() -> new TableName(longTableName)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TABLE_NAME_LENGTH.getMessage()); + } + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/TeamNameTest.java b/src/test/java/com/debatetimer/domain/customize/TeamNameTest.java new file mode 100644 index 00000000..71bc434e --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/TeamNameTest.java @@ -0,0 +1,53 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThatCode; +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; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +class TeamNameTest { + + @Nested + class Validate { + + @Test + void 팀_이름은_정해진_길이_이내여야_한다() { + String longTeamName = "f".repeat(TeamName.NAME_MAX_LENGTH + 1); + + assertThatThrownBy( + () -> new TeamName(longTeamName)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TEAM_NAME_LENGTH.getMessage()); + } + + @ParameterizedTest + @EmptySource + @ValueSource(strings = {" "}) + void 팀_이름은_빈_값을_허용하지_않는다(String invalidTeamName) { + assertThatThrownBy(() -> new TeamName(invalidTeamName)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TEAM_NAME_LENGTH.getMessage()); + } + + @ValueSource(strings = {"a bc가다9", "가0나 다ab", "ㄱㄷㅇㄹ", "漢字", "にほんご", "vielfäl"}) + @ParameterizedTest + void 팀_이름은_이모지를_제외한_글자만_가능하다(String validTeamName) { + assertThatCode(() -> new TeamName(validTeamName)) + .doesNotThrowAnyException(); + } + + @ValueSource(strings = {"a😀가다9", "🐥", "🥦"}) + @ParameterizedTest + void 팀_이름에_이모지를_넣을_수_없다(String invalidTeamName) { + assertThatThrownBy(() -> new TeamName(invalidTeamName)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TEAM_NAME_FORM.getMessage()); + } + } +} diff --git a/src/test/java/com/debatetimer/entity/customize/CustomizeTableEntityTest.java b/src/test/java/com/debatetimer/entity/customize/CustomizeTableEntityTest.java new file mode 100644 index 00000000..6dbfa1c5 --- /dev/null +++ b/src/test/java/com/debatetimer/entity/customize/CustomizeTableEntityTest.java @@ -0,0 +1,82 @@ +package com.debatetimer.entity.customize; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.member.Member; +import com.debatetimer.dto.member.TableType; +import java.time.LocalDateTime; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class CustomizeTableEntityTest { + + @Nested + class GetType { + + @Test + void 사용자_지정_테이블_타입을_반환한다() { + CustomizeTableEntity customizeTableEntity = new CustomizeTableEntity(); + + assertThat(customizeTableEntity.getType()).isEqualTo(TableType.CUSTOMIZE); + } + } + + @Nested + class UpdateUsedAt { + + @Test + void 테이블의_사용_시각을_업데이트한다() { + Member member = new Member("default@gmail.com"); + CustomizeTable table = new CustomizeTable(member, "tableName", "agenda", "찬성", "반대", + true, true, LocalDateTime.now().minusNanos(1L)); + CustomizeTableEntity customizeTableEntity = new CustomizeTableEntity(table); + LocalDateTime beforeUsedAt = customizeTableEntity.getUsedAt(); + + customizeTableEntity.updateUsedAt(); + + assertThat(customizeTableEntity.getUsedAt()).isAfter(beforeUsedAt); + } + } + + @Nested + class Update { + + @Test + void 테이블_정보를_업데이트_할_수_있다() { + Member member = new Member("default@gmail.com"); + CustomizeTable table = new CustomizeTable(member, "tableName", "agenda", "찬성", "반대", + true, true, LocalDateTime.now().minusNanos(1L)); + CustomizeTableEntity customizeTableEntity = new CustomizeTableEntity(table); + CustomizeTable renewTable = new CustomizeTable(member, "newName", "newAgenda", + "newPros", "newCons", false, false, LocalDateTime.now()); + + customizeTableEntity.updateTable(renewTable); + + assertAll( + () -> assertThat(customizeTableEntity.getName()).isEqualTo("newName"), + () -> assertThat(customizeTableEntity.getAgenda()).isEqualTo("newAgenda"), + () -> assertThat(customizeTableEntity.getProsTeamName()).isEqualTo("newPros"), + () -> assertThat(customizeTableEntity.getConsTeamName()).isEqualTo("newCons"), + () -> assertThat(customizeTableEntity.isWarningBell()).isFalse(), + () -> assertThat(customizeTableEntity.isFinishBell()).isFalse() + ); + } + + @Test + void 테이블_업데이트_할_때_사용_시간을_변경한다() { + Member member = new Member("default@gmail.com"); + CustomizeTable table = new CustomizeTable(member, "tableName", "agenda", "찬성", "반대", + true, true, LocalDateTime.now().minusNanos(1L)); + CustomizeTableEntity customizeTableEntity = new CustomizeTableEntity(table); + CustomizeTable renewTable = new CustomizeTable(member, "newName", "newAgenda", + "newPros", "newCons", false, false, LocalDateTime.now()); + LocalDateTime beforeUsedAt = customizeTableEntity.getUsedAt(); + + customizeTableEntity.updateTable(renewTable); + + assertThat(customizeTableEntity.getUsedAt()).isAfter(beforeUsedAt); + } + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxTest.java b/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxTest.java similarity index 87% rename from src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxTest.java rename to src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxTest.java index 50fb562b..e535d2e3 100644 --- a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxTest.java +++ b/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxTest.java @@ -1,10 +1,11 @@ -package com.debatetimer.domain.customize; +package com.debatetimer.entity.customize; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import com.debatetimer.domain.Stance; +import com.debatetimer.domain.customize.CustomizeBoxType; +import com.debatetimer.domain.customize.Stance; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import org.junit.jupiter.api.Nested; @@ -21,7 +22,7 @@ class ValidateSequence { @ValueSource(ints = {0, -1}) @ParameterizedTest void 순서는_양수만_가능하다(int sequence) { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.TIME_BASED; assertThatThrownBy(() -> new CustomizeTimeBox(table, sequence, Stance.NEUTRAL, "자유토론", @@ -37,7 +38,7 @@ class ValidateTime { @ValueSource(ints = {0, -1}) @ParameterizedTest void 시간은_양수만_가능하다(int time) { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; assertThatThrownBy( @@ -53,7 +54,7 @@ class ValidateSpeaker { @Test void 발언자_이름은_일정길이_이내로_허용된다() { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; String speaker = "k".repeat(CustomizeTimeBox.SPEAKER_MAX_LENGTH + 1); @@ -66,7 +67,7 @@ class ValidateSpeaker { @NullSource @ParameterizedTest void 발언자는_빈_값이_허용된다(String speaker) { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; assertThatCode(() -> new CustomizeTimeBox(table, 1, Stance.CONS, "입론", @@ -77,7 +78,7 @@ class ValidateSpeaker { @ValueSource(strings = {" ", " "}) @ParameterizedTest void 발언자는_공백이_입력되면_null로_저장된다(String speaker) { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; CustomizeTimeBox timeBox = new CustomizeTimeBox(table, 1, Stance.CONS, "입론", @@ -92,7 +93,7 @@ class ValidateCustomizeTime { @Test void 자유토론_타입은_개인_발언_시간과_팀_발언_시간을_입력해야_한다() { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.TIME_BASED; assertThatCode(() -> new CustomizeTimeBox(table, 1, Stance.NEUTRAL, "자유토론", @@ -102,7 +103,7 @@ class ValidateCustomizeTime { @Test void 자유토론_타입이_개인_발언_시간과_팀_발언_시간을_입력하지_않을_경우_예외가_발생한다() { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.TIME_BASED; assertThatThrownBy(() -> new CustomizeTimeBox(table, 1, Stance.NEUTRAL, "자유토론", customizeBoxType, 10, @@ -112,7 +113,7 @@ class ValidateCustomizeTime { @Test void 자유토론_타입이_아닌_타임박스가_개인_발언_시간과_팀_발언_시간을_입력할_경우_예외가_발생한다() { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType notTimeBasedBoxType = CustomizeBoxType.NORMAL; assertThatThrownBy( @@ -123,7 +124,7 @@ class ValidateCustomizeTime { @Test void 팀_발언_시간은_있으며_개인_발언_시간은_없을_수_있다() { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); Integer timePerTeam = 60; Integer timePerSpeaking = null; @@ -133,7 +134,7 @@ class ValidateCustomizeTime { @Test void 개인_발언_시간은_팀_발언_시간보다_적거나_같아야_한다() { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); int timePerTeam = 60; int timePerSpeaking = 59; @@ -143,7 +144,7 @@ class ValidateCustomizeTime { @Test void 개인_발언_시간이_팀_발언_시간보다_길면_예외가_발생한다() { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); int timePerTeam = 60; int timePerSpeaking = 61; @@ -154,7 +155,7 @@ class ValidateCustomizeTime { @Test void 발언_유형의_길이는_일정_범위_이내여야_한다() { - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); String longSpeechType = "s".repeat(CustomizeTimeBox.SPEECH_TYPE_MAX_LENGTH + 1); assertThatThrownBy( @@ -171,7 +172,7 @@ class getTime { void 자유_토론_타임_박스의_시간은_팀_당_발언_시간의_배수이어야_한다() { int timePerTeam = 300; int timePerSpeaking = 120; - CustomizeTable table = new CustomizeTable(); + CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeTimeBox timeBasedTimeBox = new CustomizeTimeBox(table, 1, Stance.CONS, "자유 토론", CustomizeBoxType.TIME_BASED, timePerTeam, timePerSpeaking, "콜리"); diff --git a/src/test/java/com/debatetimer/fixture/CustomizeTableGenerator.java b/src/test/java/com/debatetimer/fixture/CustomizeTableGenerator.java index f7420bf5..80a7b8dc 100644 --- a/src/test/java/com/debatetimer/fixture/CustomizeTableGenerator.java +++ b/src/test/java/com/debatetimer/fixture/CustomizeTableGenerator.java @@ -2,7 +2,9 @@ import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; +import com.debatetimer.entity.customize.CustomizeTableEntity; import com.debatetimer.repository.customize.CustomizeTableRepository; +import java.time.LocalDateTime; import org.springframework.stereotype.Component; @Component @@ -14,16 +16,18 @@ public CustomizeTableGenerator(CustomizeTableRepository customizeTableRepository this.customizeTableRepository = customizeTableRepository; } - public CustomizeTable generate(Member member) { - CustomizeTable table = new CustomizeTable( + public CustomizeTableEntity generate(Member member) { + CustomizeTable customizeTable = new CustomizeTable( member, "토론 테이블", "주제", + "찬성", + "반대", false, false, - "찬성", - "반대" + LocalDateTime.now() ); + CustomizeTableEntity table = new CustomizeTableEntity(customizeTable); return customizeTableRepository.save(table); } } diff --git a/src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java b/src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java index df77fb3d..d7ab3acc 100644 --- a/src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java +++ b/src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java @@ -1,9 +1,9 @@ package com.debatetimer.fixture; -import com.debatetimer.domain.Stance; import com.debatetimer.domain.customize.CustomizeBoxType; -import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.Stance; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBox; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import org.springframework.stereotype.Component; @@ -16,7 +16,7 @@ public CustomizeTimeBoxGenerator(CustomizeTimeBoxRepository customizeTimeBoxRepo this.customizeTimeBoxRepository = customizeTimeBoxRepository; } - public CustomizeTimeBox generate(CustomizeTable testTable, CustomizeBoxType boxType, int sequence) { + public CustomizeTimeBox generate(CustomizeTableEntity testTable, CustomizeBoxType boxType, int sequence) { CustomizeTimeBox timeBox = new CustomizeTimeBox( testTable, sequence, @@ -29,7 +29,8 @@ public CustomizeTimeBox generate(CustomizeTable testTable, CustomizeBoxType boxT return customizeTimeBoxRepository.save(timeBox); } - public CustomizeTimeBox generateNotExistSpeaker(CustomizeTable testTable, CustomizeBoxType boxType, int sequence) { + public CustomizeTimeBox generateNotExistSpeaker(CustomizeTableEntity testTable, CustomizeBoxType boxType, + int sequence) { CustomizeTimeBox timeBox = new CustomizeTimeBox( testTable, sequence, diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTableEntityRepositoryTest.java similarity index 78% rename from src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java rename to src/test/java/com/debatetimer/repository/customize/CustomizeTableEntityRepositoryTest.java index 07b70037..ebc3e626 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTableEntityRepositoryTest.java @@ -3,8 +3,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; +import com.debatetimer.entity.customize.CustomizeTableEntity; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import com.debatetimer.repository.BaseRepositoryTest; @@ -25,11 +25,11 @@ class FindAllByMember { void 특정_회원의_테이블만_조회한다() { Member chan = memberGenerator.generate("default@gmail.com"); Member bito = memberGenerator.generate("default2@gmail.com"); - CustomizeTable chanTable1 = customizeTableGenerator.generate(chan); - CustomizeTable chanTable2 = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable1 = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable2 = customizeTableGenerator.generate(chan); customizeTableGenerator.generate(bito); - List foundKeoChanTables = tableRepository.findAllByMember(chan); + List foundKeoChanTables = tableRepository.findAllByMember(chan); assertThat(foundKeoChanTables).containsExactly(chanTable1, chanTable2); } @@ -41,9 +41,9 @@ class GetByIdAndMember { @Test void 특정_회원의_테이블을_조회한다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTable table = customizeTableGenerator.generate(chan); + CustomizeTableEntity table = customizeTableGenerator.generate(chan); - CustomizeTable foundTable = tableRepository.getByIdAndMember(table.getId(), chan); + CustomizeTableEntity foundTable = tableRepository.getByIdAndMember(table.getId(), chan); assertThat(foundTable).isEqualTo(table); } diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java index 8cf1aa1b..6b5f5df0 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java @@ -4,9 +4,9 @@ import static org.assertj.core.api.Assertions.assertThatCode; 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.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBox; import com.debatetimer.repository.BaseRepositoryTest; import java.util.List; import org.junit.jupiter.api.Nested; @@ -19,14 +19,14 @@ class CustomizeTimeBoxRepositoryTest extends BaseRepositoryTest { private CustomizeTimeBoxRepository customizeTimeBoxRepository; @Nested - class FindAllByCustomizeTable { + class FindAllByCustomizeTableEntity { @Test void 특정_테이블의_타임박스를_모두_조회한다() { Member chan = memberGenerator.generate("default@gmail.com"); Member bito = memberGenerator.generate("default2@gmail.com"); - CustomizeTable chanTable = customizeTableGenerator.generate(chan); - CustomizeTable bitoTable = customizeTableGenerator.generate(bito); + CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); CustomizeTimeBox chanBox1 = customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); CustomizeTimeBox chanBox2 = customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); customizeTimeBoxGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 2); @@ -44,7 +44,7 @@ class DeleteAllByTable { @Test void 특정_테이블의_타임박스를_모두_삭제한다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTable chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); @@ -57,10 +57,10 @@ class DeleteAllByTable { @Test void 특정_테이블의_타임_박스를_삭제해도_다른_테이블의_타임_박스는_삭제되지_않는다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTable filledTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity filledTable = customizeTableGenerator.generate(chan); customizeTimeBoxGenerator.generate(filledTable, CustomizeBoxType.NORMAL, 1); customizeTimeBoxGenerator.generate(filledTable, CustomizeBoxType.NORMAL, 2); - CustomizeTable deletedTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity deletedTable = customizeTableGenerator.generate(chan); customizeTimeBoxGenerator.generate(deletedTable, CustomizeBoxType.NORMAL, 1); customizeTimeBoxRepository.deleteAllByTable(deletedTable); @@ -72,7 +72,7 @@ class DeleteAllByTable { @Test void 테이블의_타임_박스가_없을_경우_타임_박스_삭제_시_예외가_발생하지_않는다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTable emptyTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity emptyTable = customizeTableGenerator.generate(chan); assertThatCode(() -> customizeTimeBoxRepository.deleteAllByTable(emptyTable)) .doesNotThrowAnyException(); diff --git a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java index 71a10611..ad4d9e16 100644 --- a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java +++ b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java @@ -4,15 +4,15 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; -import com.debatetimer.domain.Stance; import com.debatetimer.domain.customize.CustomizeBoxType; -import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.Stance; import com.debatetimer.domain.member.Member; 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.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBox; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import com.debatetimer.service.BaseServiceTest; @@ -46,7 +46,7 @@ class Save { ); CustomizeTableResponse savedTableResponse = customizeService.save(customizeTableCreateRequest, chan); - Optional foundTable = customizeTableRepository.findById(savedTableResponse.id()); + Optional foundTable = customizeTableRepository.findById(savedTableResponse.id()); List foundTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( foundTable.get()); @@ -63,7 +63,7 @@ class FindTable { @Test void 사용자_지정_토론_테이블을_조회한다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTable chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); @@ -79,7 +79,7 @@ class FindTable { void 회원_소유가_아닌_테이블_조회_시_예외를_발생시킨다() { Member chan = memberGenerator.generate("default@gmail.com"); Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTable chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); long chanTableId = chanTable.getId(); assertThatThrownBy(() -> customizeService.findTable(chanTableId, coli)) @@ -94,7 +94,7 @@ class UpdateTable { @Test void 사용자_지정_토론_테이블을_수정한다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTable chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); CustomizeTableCreateRequest renewTableRequest = new CustomizeTableCreateRequest( new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", "반대", true, true), @@ -108,7 +108,7 @@ class UpdateTable { customizeService.updateTable(renewTableRequest, chanTable.getId(), chan); - Optional updatedTable = customizeTableRepository.findById(chanTable.getId()); + Optional updatedTable = customizeTableRepository.findById(chanTable.getId()); List updatedTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( updatedTable.get()); @@ -123,7 +123,7 @@ class UpdateTable { void 회원_소유가_아닌_테이블_수정_시_예외를_발생시킨다() { Member chan = memberGenerator.generate("default@gmail.com"); Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTable chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); long chanTableId = chanTable.getId(); CustomizeTableCreateRequest renewTableRequest = new CustomizeTableCreateRequest( new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", @@ -144,7 +144,7 @@ class UpdateTable { @Test void 테이블_정보_수정을_동시에_요청할_때_동시에_처리하지_않는다() throws InterruptedException { Member member = memberGenerator.generate("default@gmail.com"); - CustomizeTable table = customizeTableGenerator.generate(member); + CustomizeTableEntity table = customizeTableGenerator.generate(member); CustomizeTableCreateRequest request = new CustomizeTableCreateRequest( new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", "반대", true, true), @@ -168,12 +168,12 @@ class UpdateUsedAt { @Test void 사용자_지정_토론_테이블의_사용_시각을_최신화한다() { Member member = memberGenerator.generate("default@gmail.com"); - CustomizeTable table = customizeTableGenerator.generate(member); + CustomizeTableEntity table = customizeTableGenerator.generate(member); LocalDateTime beforeUsedAt = table.getUsedAt(); customizeService.updateUsedAt(table.getId(), member); - Optional updatedTable = customizeTableRepository.findById(table.getId()); + Optional updatedTable = customizeTableRepository.findById(table.getId()); assertAll( () -> assertThat(updatedTable.get().getId()).isEqualTo(table.getId()), () -> assertThat(updatedTable.get().getUsedAt()).isAfter(beforeUsedAt) @@ -184,7 +184,7 @@ class UpdateUsedAt { void 회원_소유가_아닌_테이블_수정_시_예외를_발생시킨다() { Member chan = memberGenerator.generate("default@gmail.com"); Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTable chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); long chanTableId = chanTable.getId(); CustomizeTableCreateRequest renewTableRequest = new CustomizeTableCreateRequest( new CustomizeTableInfoCreateRequest("자유 테이블", "주제", "찬성", @@ -209,13 +209,13 @@ class DeleteTable { @Test void 사용자_지정_토론_테이블을_삭제한다() { Member chan = memberGenerator.generate("default@gmail.com"); - CustomizeTable chanTable = customizeTableGenerator.generate(chan); + 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()); + Optional foundTable = customizeTableRepository.findById(chanTable.getId()); List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( chanTable); @@ -229,7 +229,7 @@ class DeleteTable { void 회원_소유가_아닌_테이블_삭제_시_예외를_발생시킨다() { Member chan = memberGenerator.generate("default@gmail.com"); Member coli = memberGenerator.generate("default2@gmail.com"); - CustomizeTable chanTable = customizeTableGenerator.generate(chan); + CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); long chanTableId = chanTable.getId(); assertThatThrownBy(() -> customizeService.deleteTable(chanTableId, coli)) diff --git a/src/test/java/com/debatetimer/service/member/MemberServiceTest.java b/src/test/java/com/debatetimer/service/member/MemberServiceTest.java index 1777798b..d78c50e7 100644 --- a/src/test/java/com/debatetimer/service/member/MemberServiceTest.java +++ b/src/test/java/com/debatetimer/service/member/MemberServiceTest.java @@ -3,11 +3,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; -import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; import com.debatetimer.dto.member.MemberCreateResponse; import com.debatetimer.dto.member.MemberInfo; import com.debatetimer.dto.member.TableResponses; +import com.debatetimer.entity.customize.CustomizeTableEntity; import com.debatetimer.service.BaseServiceTest; import java.util.Optional; import org.junit.jupiter.api.Nested; @@ -63,7 +63,7 @@ class GetTables { @Test void 회원의_전체_토론_시간표는_정해진_순서대로_반환한다() throws InterruptedException { Member member = memberGenerator.generate("default@gmail.com"); - CustomizeTable table = customizeTableGenerator.generate(member); + CustomizeTableEntity table = customizeTableGenerator.generate(member); Thread.sleep(1); TableResponses response = memberService.getTables(member.getId()); From 51b9fd0519b6e856c48eddd79f86bb04dee7607e Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Tue, 1 Jul 2025 14:20:18 +0900 Subject: [PATCH 12/27] =?UTF-8?q?chore=20:=20=EC=BD=94=EB=93=9C=EB=A0=88?= =?UTF-8?q?=EB=B9=97=20=EC=84=A4=EC=A0=95=ED=8C=8C=EC=9D=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#189)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .coderabbit.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .coderabbit.yaml diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 00000000..fc874ada --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,16 @@ +language: "ko-KR" +early_access: false +reviews: + profile: "chill" + request_changes_workflow: false + high_level_summary: true + poem: false + review_status: true + collapse_walkthrough: false + auto_review: + enabled: true + drafts: false + base_branches: + - "develop" +chat: + auto_reply: true From 4c3baf532d746c96ccd0ce70b94b10fa789321ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B1=B4=EC=9A=B0?= Date: Fri, 11 Jul 2025 00:30:04 +0900 Subject: [PATCH 13/27] =?UTF-8?q?[FEAT]=20=ED=88=AC=ED=91=9C=20API=20?= =?UTF-8?q?=EA=B8=B0=EC=B4=88=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85=20(#196)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../debatetimer/domain/poll/PollStatus.java | 8 ++++ .../com/debatetimer/domain/poll/VoteTeam.java | 8 ++++ .../debatetimer/entity/poll/PollEntity.java | 39 +++++++++++++++++ .../debatetimer/entity/poll/VoteEntity.java | 42 +++++++++++++++++++ .../V9__create_poll_and_vote_table.sql | 25 +++++++++++ 5 files changed, 122 insertions(+) create mode 100644 src/main/java/com/debatetimer/domain/poll/PollStatus.java create mode 100644 src/main/java/com/debatetimer/domain/poll/VoteTeam.java create mode 100644 src/main/java/com/debatetimer/entity/poll/PollEntity.java create mode 100644 src/main/java/com/debatetimer/entity/poll/VoteEntity.java create mode 100644 src/main/resources/db/migration/V9__create_poll_and_vote_table.sql diff --git a/src/main/java/com/debatetimer/domain/poll/PollStatus.java b/src/main/java/com/debatetimer/domain/poll/PollStatus.java new file mode 100644 index 00000000..f2d0ebad --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/PollStatus.java @@ -0,0 +1,8 @@ +package com.debatetimer.domain.poll; + +public enum PollStatus { + + PROGRESS, + DONE, + ; +} diff --git a/src/main/java/com/debatetimer/domain/poll/VoteTeam.java b/src/main/java/com/debatetimer/domain/poll/VoteTeam.java new file mode 100644 index 00000000..d9648937 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/VoteTeam.java @@ -0,0 +1,8 @@ +package com.debatetimer.domain.poll; + +public enum VoteTeam { + + PROS, + CONS, + ; +} diff --git a/src/main/java/com/debatetimer/entity/poll/PollEntity.java b/src/main/java/com/debatetimer/entity/poll/PollEntity.java new file mode 100644 index 00000000..d4700a3d --- /dev/null +++ b/src/main/java/com/debatetimer/entity/poll/PollEntity.java @@ -0,0 +1,39 @@ +package com.debatetimer.entity.poll; + +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.entity.customize.BaseTimeEntity; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "poll") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class PollEntity extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private long tableId; + + @NotNull + @Enumerated(EnumType.STRING) + private PollStatus status; + + @NotBlank + private String prosTeamName; + + @NotBlank + private String consTeamName; + + private String agenda; +} diff --git a/src/main/java/com/debatetimer/entity/poll/VoteEntity.java b/src/main/java/com/debatetimer/entity/poll/VoteEntity.java new file mode 100644 index 00000000..9b01e7f4 --- /dev/null +++ b/src/main/java/com/debatetimer/entity/poll/VoteEntity.java @@ -0,0 +1,42 @@ +package com.debatetimer.entity.poll; + +import com.debatetimer.domain.poll.VoteTeam; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "vote") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class VoteEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "poll_id") + private PollEntity poll; + + @NotNull + @Enumerated(EnumType.STRING) + private VoteTeam team; + + @NotBlank + private String name; + + @NotBlank + private String participantCode; +} diff --git a/src/main/resources/db/migration/V9__create_poll_and_vote_table.sql b/src/main/resources/db/migration/V9__create_poll_and_vote_table.sql new file mode 100644 index 00000000..d5bf80ff --- /dev/null +++ b/src/main/resources/db/migration/V9__create_poll_and_vote_table.sql @@ -0,0 +1,25 @@ +CREATE TABLE poll +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + table_id BIGINT NOT NULL, + status ENUM ('PROGRESS','DONE') NOT NULL, + pros_team_name VARCHAR(255) NOT NULL, + cons_team_name VARCHAR(255) NOT NULL, + agenda VARCHAR(255) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +CREATE TABLE vote +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + poll_id BIGINT NOT NULL, + team ENUM ('CONS','PROS') NOT NULL, + name VARCHAR(255) NOT NULL, + participant_code VARCHAR(255) NOT NULL +); + +alter table vote + add constraint vote_to_poll + foreign key (poll_id) + references poll (id); From ded82b59ac70547cb4648b5599fe84f22aaa7d23 Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Fri, 11 Jul 2025 13:38:19 +0900 Subject: [PATCH 14/27] =?UTF-8?q?[CHORE]=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EB=A9=94=ED=8A=B8=EB=A6=AD=20=EC=A7=80=ED=91=9C=20=EB=85=BC?= =?UTF-8?q?=EC=9D=98=20=EB=B0=8F=20=EA=B5=AC=ED=98=84=20(#197)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/notifier/DiscordNotifier.java | 2 ++ .../debatetimer/client/oauth/OAuthClient.java | 3 +++ .../debatetimer/config/MonitoringConfig.java | 17 +++++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 src/main/java/com/debatetimer/config/MonitoringConfig.java diff --git a/src/main/java/com/debatetimer/client/notifier/DiscordNotifier.java b/src/main/java/com/debatetimer/client/notifier/DiscordNotifier.java index 5f5c952f..0597deca 100644 --- a/src/main/java/com/debatetimer/client/notifier/DiscordNotifier.java +++ b/src/main/java/com/debatetimer/client/notifier/DiscordNotifier.java @@ -2,6 +2,7 @@ import com.debatetimer.exception.custom.DTInitializationException; import com.debatetimer.exception.errorcode.InitializationErrorCode; +import io.micrometer.core.annotation.Timed; import java.util.Arrays; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -34,6 +35,7 @@ private JDA initializeJda(String token) { } } + @Timed(value = "discord.send_error_message") public void sendErrorMessage(Throwable throwable) { TextChannel channel = jda.getTextChannelById(properties.getChannelId()); String errorMessage = throwable.toString(); diff --git a/src/main/java/com/debatetimer/client/oauth/OAuthClient.java b/src/main/java/com/debatetimer/client/oauth/OAuthClient.java index ed10dcf4..973f99c9 100644 --- a/src/main/java/com/debatetimer/client/oauth/OAuthClient.java +++ b/src/main/java/com/debatetimer/client/oauth/OAuthClient.java @@ -5,6 +5,7 @@ import com.debatetimer.dto.member.MemberInfo; import com.debatetimer.dto.member.OAuthToken; import com.debatetimer.exception.handler.OAuthClientErrorHandler; +import io.micrometer.core.annotation.Timed; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; @@ -24,6 +25,7 @@ public OAuthClient(RestClient.Builder restClientBuilder, OAuthProperties oauthPr this.oauthProperties = oauthProperties; } + @Timed(value = "google.request_token") public OAuthToken requestToken(MemberCreateRequest request) { return restClient.post() .uri("https://oauth2.googleapis.com/token") @@ -34,6 +36,7 @@ public OAuthToken requestToken(MemberCreateRequest request) { .body(OAuthToken.class); } + @Timed(value = "google.request_member_info") public MemberInfo requestMemberInfo(OAuthToken response) { return restClient.get() .uri("https://www.googleapis.com/oauth2/v3/userinfo") diff --git a/src/main/java/com/debatetimer/config/MonitoringConfig.java b/src/main/java/com/debatetimer/config/MonitoringConfig.java new file mode 100644 index 00000000..b3bced6d --- /dev/null +++ b/src/main/java/com/debatetimer/config/MonitoringConfig.java @@ -0,0 +1,17 @@ +package com.debatetimer.config; + +import io.micrometer.core.aop.TimedAspect; +import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MonitoringConfig { + + @Bean + @ConditionalOnMissingBean(TimedAspect.class) + public TimedAspect timedAspect(MeterRegistry registry) { + return new TimedAspect(registry); + } +} From 91ebb7053981ec43660cc776498a76a7a2318e28 Mon Sep 17 00:00:00 2001 From: leegwichan Date: Mon, 14 Jul 2025 18:10:10 +0900 Subject: [PATCH 15/27] =?UTF-8?q?feat=20:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=ED=86=A0=EB=A1=A0=20=ED=83=80=EC=9E=84=20=EB=B0=95=EC=8A=A4=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customize/CustomizeTimeBoxDomain.java | 72 ++++++++++++++ .../domain/customize/NormalTimeBoxDomain.java | 50 ++++++++++ .../debatetimer/domain/customize/Stance.java | 5 + .../customize/TimeBasedTimeBoxDomain.java | 70 +++++++++++++ .../customize/CustomizeTimeBoxDomainTest.java | 98 +++++++++++++++++++ .../customize/NormalTimeBoxDomainTest.java | 43 ++++++++ .../customize/TimeBasedTimeBoxDomainTest.java | 95 ++++++++++++++++++ 7 files changed, 433 insertions(+) create mode 100644 src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java create mode 100644 src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java create mode 100644 src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java create mode 100644 src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java create mode 100644 src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java create mode 100644 src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java new file mode 100644 index 00000000..8f688379 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java @@ -0,0 +1,72 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import org.springframework.lang.Nullable; + +public abstract class CustomizeTimeBoxDomain { + + public static final int SPEECH_TYPE_MAX_LENGTH = 10; + public static final int SPEAKER_MAX_LENGTH = 5; + + private final Stance stance; + + private final String speechType; + + @Nullable + private final String speaker; + + protected CustomizeTimeBoxDomain(Stance stance, String speechType, @Nullable String speaker) { + validateStance(stance); + validateSpeechType(speechType); + validateSpeaker(speaker); + + this.stance = stance; + this.speechType = speechType; + this.speaker = speaker; + } + + private void validateStance(Stance stance) { + if (!isValidStance(stance)) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_STANCE); + } + } + + protected abstract boolean isValidStance(Stance stance); + + private void validateSpeechType(String speechType) { + if (speechType == null || speechType.isBlank() || speechType.length() > SPEECH_TYPE_MAX_LENGTH) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH); + } + } + + private void validateSpeaker(String speaker) { + if (speaker != null && speaker.length() > SPEAKER_MAX_LENGTH) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH); + } + } + + public final Stance getStance() { + return stance; + } + + public final String getSpeechType() { + return speechType; + } + + @Nullable + public final String getSpeaker() { + return speaker; + } + + public abstract CustomizeBoxType getBoxType(); + + @Nullable + public abstract Integer getTime(); + + @Nullable + public abstract Integer getTimePerTeam(); + + @Nullable + public abstract Integer getTimePerSpeaking(); +} diff --git a/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java new file mode 100644 index 00000000..a17800d8 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java @@ -0,0 +1,50 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import org.springframework.lang.Nullable; + +public final class NormalTimeBoxDomain extends CustomizeTimeBoxDomain { + + private final int time; + + public NormalTimeBoxDomain(Stance stance, String speechType, @Nullable String speaker, Integer time) { + super(stance, speechType, speaker); + + validateTime(time); + this.time = time; + } + + private void validateTime(Integer time) { + if (time == null || time <= 0) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_TIME); + } + } + + @Override + protected boolean isValidStance(Stance stance) { + return true; + } + + @Override + public CustomizeBoxType getBoxType() { + return CustomizeBoxType.NORMAL; + } + + @Override + public Integer getTime() { + return time; + } + + @Nullable + @Override + public Integer getTimePerTeam() { + return null; + } + + @Nullable + @Override + public Integer getTimePerSpeaking() { + return null; + } +} diff --git a/src/main/java/com/debatetimer/domain/customize/Stance.java b/src/main/java/com/debatetimer/domain/customize/Stance.java index fd9b0975..0df12c9d 100644 --- a/src/main/java/com/debatetimer/domain/customize/Stance.java +++ b/src/main/java/com/debatetimer/domain/customize/Stance.java @@ -5,4 +5,9 @@ public enum Stance { PROS, CONS, NEUTRAL, + ; + + public boolean isNeutralStance() { + return this == NEUTRAL; + } } diff --git a/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java new file mode 100644 index 00000000..7d26610d --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java @@ -0,0 +1,70 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import org.springframework.lang.Nullable; + +public class TimeBasedTimeBoxDomain extends CustomizeTimeBoxDomain { + + private final int timePerTeam; + + @Nullable + private final Integer timePerSpeaking; + + public TimeBasedTimeBoxDomain(Stance stance, + String speechType, + @Nullable String speaker, + Integer timePerTeam, + @Nullable Integer timePerSpeaking) { + super(stance, speechType, speaker); + + validateTimes(timePerTeam, timePerSpeaking); + this.timePerTeam = timePerTeam; + this.timePerSpeaking = timePerSpeaking; + } + + private void validateTimes(Integer timePerTeam, Integer timePerSpeaking) { + validateTime(timePerTeam); + if (timePerSpeaking == null) { + return; + } + + validateTime(timePerSpeaking); + if (timePerTeam < timePerSpeaking) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BASED_TIME); + } + } + + private void validateTime(Integer time) { + if (time == null || time <= 0) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_TIME); + } + } + + @Override + protected boolean isValidStance(Stance stance) { + return stance.isNeutralStance(); + } + + @Override + public CustomizeBoxType getBoxType() { + return CustomizeBoxType.TIME_BASED; + } + + @Override + @Nullable + public Integer getTime() { + return null; + } + + @Override + public Integer getTimePerTeam() { + return timePerTeam; + } + + @Override + @Nullable + public Integer getTimePerSpeaking() { + return timePerSpeaking; + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java new file mode 100644 index 00000000..9fbc48fe --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java @@ -0,0 +1,98 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThatCode; +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; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +class CustomizeTimeBoxDomainTest { + + @Nested + class ValidateSpeechType { + + @Test + void 발언_종류는_특정_글자를_초과할_수_없다() { + String speechType = "a".repeat(CustomizeTimeBoxDomain.SPEECH_TYPE_MAX_LENGTH + 1); + + assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, speechType, "비토")) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH.getMessage()); + } + + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = {" ", "\n\t"}) + void 발언_종류는_비어있을_수_없다(String emptySpeechType) { + assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, emptySpeechType, "비토")) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH.getMessage()); + } + + @Test + void 발언_종류는_특정_글자_이내이어야_한다() { + String speechType = "a".repeat(CustomizeTimeBoxDomain.SPEECH_TYPE_MAX_LENGTH); + + assertThatCode(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, speechType, "비토")) + .doesNotThrowAnyException(); + } + } + + @Nested + class ValidateSpeaker { + + @Test + void 발언자_이름은_특정_글자를_초과할_수_없다() { + String speaker = "a".repeat(CustomizeTimeBoxDomain.SPEAKER_MAX_LENGTH + 1); + + assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, "비토", speaker)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH.getMessage()); + } + + @Test + void 발언자_이름은_비어있을_수_있다() { + assertThatCode(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, "비토", null)) + .doesNotThrowAnyException(); + } + } + + + + static class InheritedCustomizeTimeBoxDomain extends CustomizeTimeBoxDomain { + + protected InheritedCustomizeTimeBoxDomain(Stance stance, String speechType, String speaker) { + super(stance, speechType, speaker); + } + + @Override + protected boolean isValidStance(Stance stance) { + return true; + } + + @Override + public CustomizeBoxType getBoxType() { + return null; + } + + @Override + public Integer getTime() { + return 0; + } + + @Override + public Integer getTimePerTeam() { + return 0; + } + + @Override + public Integer getTimePerSpeaking() { + return 0; + } + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java new file mode 100644 index 00000000..cfdd4d16 --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java @@ -0,0 +1,43 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThatCode; +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; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class NormalTimeBoxDomainTest { + + @Nested + class ValidateTime { + + @ParameterizedTest + @ValueSource(ints = {0, -1, Integer.MIN_VALUE}) + void 시간은_0보다_커야_한다(int time) { + assertThatThrownBy(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + + @Test + void 시간은_비어있지_않아야_한다() { + Integer time = null; + + assertThatThrownBy(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + + @Test + void 시간은_양수여야_한다() { + int time = 1; + + assertThatCode(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) + .doesNotThrowAnyException(); + } + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java new file mode 100644 index 00000000..1d970205 --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java @@ -0,0 +1,95 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.AssertionsForClassTypes.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; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class TimeBasedTimeBoxDomainTest { + + @Nested + class ValidateStance { + + @Test + void 중립_스탠스가_아니면_예외가_발생한다() { + Stance stance = Stance.PROS; + + assertThatThrownBy( + () -> new TimeBasedTimeBoxDomain(stance, "자유발언", "비토", 120, 60)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_STANCE.getMessage()); + } + + @Test + void 중립_스탠스면_예외가_발생하지_않는다() { + Stance stance = Stance.NEUTRAL; + + assertThatCode( + () -> new TimeBasedTimeBoxDomain(stance, "자유발언", "비토", 120, 60)) + .doesNotThrowAnyException(); + } + } + + @Nested + class ValidateTimes { + + @ParameterizedTest + @ValueSource(ints = {0, -1, -100}) + void 팀_당_발언_시간이_양수이어야_한다(int timePerTeam) { + int timePerSpeaking = 1; + + assertThatThrownBy( + () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + + @Test + void 팀_당_발언_시간이_비어있으면_안된다() { + Integer timePerTeam = null; + int timePerSpeaking = 1; + + assertThatThrownBy( + () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + + @ParameterizedTest + @ValueSource(ints = {0, -1, -100}) + void 개인_당_시간이_양수이어야_한다(int timePerSpeaking) { + int timePerTeam = 1; + + assertThatThrownBy( + () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + + @Test + void 개인_당_발언_시간은_비어있을_수_있다() { + Integer timePerSpeaking = null; + int timePerTeam = 1; + + assertThatCode( + () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + .doesNotThrowAnyException(); + } + + @ParameterizedTest + @ValueSource(ints = {1, 60, 120}) + void 팀_당_발언시간은_개인_발언시간보다_많거나_같아야_한다(int timePerTeam) { + int timePerSpeaking = timePerTeam + 1; + + assertThatThrownBy( + () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BASED_TIME.getMessage()); + } + } +} From 1d35c2d0eedde9a5d18ccb56c925a22e418027af Mon Sep 17 00:00:00 2001 From: leegwichan Date: Mon, 14 Jul 2025 18:14:01 +0900 Subject: [PATCH 16/27] =?UTF-8?q?Revert=20"feat=20:=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=ED=86=A0=EB=A1=A0=20=ED=83=80=EC=9E=84=20=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EA=B5=AC=ED=98=84"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 91ebb7053981ec43660cc776498a76a7a2318e28. --- .../customize/CustomizeTimeBoxDomain.java | 72 -------------- .../domain/customize/NormalTimeBoxDomain.java | 50 ---------- .../debatetimer/domain/customize/Stance.java | 5 - .../customize/TimeBasedTimeBoxDomain.java | 70 ------------- .../customize/CustomizeTimeBoxDomainTest.java | 98 ------------------- .../customize/NormalTimeBoxDomainTest.java | 43 -------- .../customize/TimeBasedTimeBoxDomainTest.java | 95 ------------------ 7 files changed, 433 deletions(-) delete mode 100644 src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java delete mode 100644 src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java delete mode 100644 src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java delete mode 100644 src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java delete mode 100644 src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java delete mode 100644 src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java deleted file mode 100644 index 8f688379..00000000 --- a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.debatetimer.domain.customize; - -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import org.springframework.lang.Nullable; - -public abstract class CustomizeTimeBoxDomain { - - public static final int SPEECH_TYPE_MAX_LENGTH = 10; - public static final int SPEAKER_MAX_LENGTH = 5; - - private final Stance stance; - - private final String speechType; - - @Nullable - private final String speaker; - - protected CustomizeTimeBoxDomain(Stance stance, String speechType, @Nullable String speaker) { - validateStance(stance); - validateSpeechType(speechType); - validateSpeaker(speaker); - - this.stance = stance; - this.speechType = speechType; - this.speaker = speaker; - } - - private void validateStance(Stance stance) { - if (!isValidStance(stance)) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_STANCE); - } - } - - protected abstract boolean isValidStance(Stance stance); - - private void validateSpeechType(String speechType) { - if (speechType == null || speechType.isBlank() || speechType.length() > SPEECH_TYPE_MAX_LENGTH) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH); - } - } - - private void validateSpeaker(String speaker) { - if (speaker != null && speaker.length() > SPEAKER_MAX_LENGTH) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH); - } - } - - public final Stance getStance() { - return stance; - } - - public final String getSpeechType() { - return speechType; - } - - @Nullable - public final String getSpeaker() { - return speaker; - } - - public abstract CustomizeBoxType getBoxType(); - - @Nullable - public abstract Integer getTime(); - - @Nullable - public abstract Integer getTimePerTeam(); - - @Nullable - public abstract Integer getTimePerSpeaking(); -} diff --git a/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java deleted file mode 100644 index a17800d8..00000000 --- a/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.debatetimer.domain.customize; - -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import org.springframework.lang.Nullable; - -public final class NormalTimeBoxDomain extends CustomizeTimeBoxDomain { - - private final int time; - - public NormalTimeBoxDomain(Stance stance, String speechType, @Nullable String speaker, Integer time) { - super(stance, speechType, speaker); - - validateTime(time); - this.time = time; - } - - private void validateTime(Integer time) { - if (time == null || time <= 0) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_TIME); - } - } - - @Override - protected boolean isValidStance(Stance stance) { - return true; - } - - @Override - public CustomizeBoxType getBoxType() { - return CustomizeBoxType.NORMAL; - } - - @Override - public Integer getTime() { - return time; - } - - @Nullable - @Override - public Integer getTimePerTeam() { - return null; - } - - @Nullable - @Override - public Integer getTimePerSpeaking() { - return null; - } -} diff --git a/src/main/java/com/debatetimer/domain/customize/Stance.java b/src/main/java/com/debatetimer/domain/customize/Stance.java index 0df12c9d..fd9b0975 100644 --- a/src/main/java/com/debatetimer/domain/customize/Stance.java +++ b/src/main/java/com/debatetimer/domain/customize/Stance.java @@ -5,9 +5,4 @@ public enum Stance { PROS, CONS, NEUTRAL, - ; - - public boolean isNeutralStance() { - return this == NEUTRAL; - } } diff --git a/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java deleted file mode 100644 index 7d26610d..00000000 --- a/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.debatetimer.domain.customize; - -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; -import org.springframework.lang.Nullable; - -public class TimeBasedTimeBoxDomain extends CustomizeTimeBoxDomain { - - private final int timePerTeam; - - @Nullable - private final Integer timePerSpeaking; - - public TimeBasedTimeBoxDomain(Stance stance, - String speechType, - @Nullable String speaker, - Integer timePerTeam, - @Nullable Integer timePerSpeaking) { - super(stance, speechType, speaker); - - validateTimes(timePerTeam, timePerSpeaking); - this.timePerTeam = timePerTeam; - this.timePerSpeaking = timePerSpeaking; - } - - private void validateTimes(Integer timePerTeam, Integer timePerSpeaking) { - validateTime(timePerTeam); - if (timePerSpeaking == null) { - return; - } - - validateTime(timePerSpeaking); - if (timePerTeam < timePerSpeaking) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BASED_TIME); - } - } - - private void validateTime(Integer time) { - if (time == null || time <= 0) { - throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_TIME); - } - } - - @Override - protected boolean isValidStance(Stance stance) { - return stance.isNeutralStance(); - } - - @Override - public CustomizeBoxType getBoxType() { - return CustomizeBoxType.TIME_BASED; - } - - @Override - @Nullable - public Integer getTime() { - return null; - } - - @Override - public Integer getTimePerTeam() { - return timePerTeam; - } - - @Override - @Nullable - public Integer getTimePerSpeaking() { - return timePerSpeaking; - } -} diff --git a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java deleted file mode 100644 index 9fbc48fe..00000000 --- a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.debatetimer.domain.customize; - -import static org.assertj.core.api.Assertions.assertThatCode; -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; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource; -import org.junit.jupiter.params.provider.ValueSource; - -class CustomizeTimeBoxDomainTest { - - @Nested - class ValidateSpeechType { - - @Test - void 발언_종류는_특정_글자를_초과할_수_없다() { - String speechType = "a".repeat(CustomizeTimeBoxDomain.SPEECH_TYPE_MAX_LENGTH + 1); - - assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, speechType, "비토")) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH.getMessage()); - } - - @ParameterizedTest - @NullAndEmptySource - @ValueSource(strings = {" ", "\n\t"}) - void 발언_종류는_비어있을_수_없다(String emptySpeechType) { - assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, emptySpeechType, "비토")) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH.getMessage()); - } - - @Test - void 발언_종류는_특정_글자_이내이어야_한다() { - String speechType = "a".repeat(CustomizeTimeBoxDomain.SPEECH_TYPE_MAX_LENGTH); - - assertThatCode(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, speechType, "비토")) - .doesNotThrowAnyException(); - } - } - - @Nested - class ValidateSpeaker { - - @Test - void 발언자_이름은_특정_글자를_초과할_수_없다() { - String speaker = "a".repeat(CustomizeTimeBoxDomain.SPEAKER_MAX_LENGTH + 1); - - assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, "비토", speaker)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH.getMessage()); - } - - @Test - void 발언자_이름은_비어있을_수_있다() { - assertThatCode(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, "비토", null)) - .doesNotThrowAnyException(); - } - } - - - - static class InheritedCustomizeTimeBoxDomain extends CustomizeTimeBoxDomain { - - protected InheritedCustomizeTimeBoxDomain(Stance stance, String speechType, String speaker) { - super(stance, speechType, speaker); - } - - @Override - protected boolean isValidStance(Stance stance) { - return true; - } - - @Override - public CustomizeBoxType getBoxType() { - return null; - } - - @Override - public Integer getTime() { - return 0; - } - - @Override - public Integer getTimePerTeam() { - return 0; - } - - @Override - public Integer getTimePerSpeaking() { - return 0; - } - } -} diff --git a/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java deleted file mode 100644 index cfdd4d16..00000000 --- a/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.debatetimer.domain.customize; - -import static org.assertj.core.api.Assertions.assertThatCode; -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; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -class NormalTimeBoxDomainTest { - - @Nested - class ValidateTime { - - @ParameterizedTest - @ValueSource(ints = {0, -1, Integer.MIN_VALUE}) - void 시간은_0보다_커야_한다(int time) { - assertThatThrownBy(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); - } - - @Test - void 시간은_비어있지_않아야_한다() { - Integer time = null; - - assertThatThrownBy(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); - } - - @Test - void 시간은_양수여야_한다() { - int time = 1; - - assertThatCode(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) - .doesNotThrowAnyException(); - } - } -} diff --git a/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java deleted file mode 100644 index 1d970205..00000000 --- a/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.debatetimer.domain.customize; - -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.AssertionsForClassTypes.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; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -class TimeBasedTimeBoxDomainTest { - - @Nested - class ValidateStance { - - @Test - void 중립_스탠스가_아니면_예외가_발생한다() { - Stance stance = Stance.PROS; - - assertThatThrownBy( - () -> new TimeBasedTimeBoxDomain(stance, "자유발언", "비토", 120, 60)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_STANCE.getMessage()); - } - - @Test - void 중립_스탠스면_예외가_발생하지_않는다() { - Stance stance = Stance.NEUTRAL; - - assertThatCode( - () -> new TimeBasedTimeBoxDomain(stance, "자유발언", "비토", 120, 60)) - .doesNotThrowAnyException(); - } - } - - @Nested - class ValidateTimes { - - @ParameterizedTest - @ValueSource(ints = {0, -1, -100}) - void 팀_당_발언_시간이_양수이어야_한다(int timePerTeam) { - int timePerSpeaking = 1; - - assertThatThrownBy( - () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); - } - - @Test - void 팀_당_발언_시간이_비어있으면_안된다() { - Integer timePerTeam = null; - int timePerSpeaking = 1; - - assertThatThrownBy( - () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); - } - - @ParameterizedTest - @ValueSource(ints = {0, -1, -100}) - void 개인_당_시간이_양수이어야_한다(int timePerSpeaking) { - int timePerTeam = 1; - - assertThatThrownBy( - () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); - } - - @Test - void 개인_당_발언_시간은_비어있을_수_있다() { - Integer timePerSpeaking = null; - int timePerTeam = 1; - - assertThatCode( - () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) - .doesNotThrowAnyException(); - } - - @ParameterizedTest - @ValueSource(ints = {1, 60, 120}) - void 팀_당_발언시간은_개인_발언시간보다_많거나_같아야_한다(int timePerTeam) { - int timePerSpeaking = timePerTeam + 1; - - assertThatThrownBy( - () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) - .isInstanceOf(DTClientErrorException.class) - .hasMessage(ClientErrorCode.INVALID_TIME_BASED_TIME.getMessage()); - } - } -} From 28b26608a78abd6261b18c70f0ea1f7d48973bb5 Mon Sep 17 00:00:00 2001 From: SANGHUN OH <121424793+unifolio0@users.noreply.github.com> Date: Wed, 16 Jul 2025 11:06:24 +0900 Subject: [PATCH 17/27] =?UTF-8?q?[FEAT]=20=EC=A2=85=EC=86=8C=EB=A6=AC=20?= =?UTF-8?q?=EC=BB=A4=EC=8A=A4=ED=85=80=20=EA=B8=B0=EB=8A=A5=20(#193)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/customize/request/BellRequest.java | 7 + .../CustomizeTimeBoxCreateRequest.java | 4 + .../dto/customize/response/BellResponse.java | 7 + .../response/CustomizeTableResponse.java | 7 + .../response/CustomizeTimeBoxResponse.java | 16 ++ .../entity/customize/BellEntity.java | 60 +++++ .../exception/errorcode/ClientErrorCode.java | 4 + .../repository/customize/BellRepository.java | 17 ++ .../service/customize/CustomizeService.java | 11 +- .../service/customize/CustomizeServiceV2.java | 136 ++++++++++ .../db/migration/V10__add_bell_table.sql | 13 + .../controller/BaseControllerTest.java | 8 + .../customize/CustomizeDocumentTest.java | 84 +++--- .../entity/customize/BellEntityTest.java | 32 +++ .../debatetimer/fixture/BellGenerator.java | 21 ++ .../debatetimer/service/BaseServiceTest.java | 8 + .../customize/CustomizeServiceTest.java | 51 ++-- .../customize/CustomizeServiceV2Test.java | 241 ++++++++++++++++++ 18 files changed, 656 insertions(+), 71 deletions(-) create mode 100644 src/main/java/com/debatetimer/dto/customize/request/BellRequest.java create mode 100644 src/main/java/com/debatetimer/dto/customize/response/BellResponse.java create mode 100644 src/main/java/com/debatetimer/entity/customize/BellEntity.java create mode 100644 src/main/java/com/debatetimer/repository/customize/BellRepository.java create mode 100644 src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java create mode 100644 src/main/resources/db/migration/V10__add_bell_table.sql create mode 100644 src/test/java/com/debatetimer/entity/customize/BellEntityTest.java create mode 100644 src/test/java/com/debatetimer/fixture/BellGenerator.java create mode 100644 src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java diff --git a/src/main/java/com/debatetimer/dto/customize/request/BellRequest.java b/src/main/java/com/debatetimer/dto/customize/request/BellRequest.java new file mode 100644 index 00000000..0c846580 --- /dev/null +++ b/src/main/java/com/debatetimer/dto/customize/request/BellRequest.java @@ -0,0 +1,7 @@ +package com.debatetimer.dto.customize.request; + +public record BellRequest( + int time, + int count +) { +} diff --git a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java index b3c125a1..97397215 100644 --- a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java +++ b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java @@ -7,6 +7,7 @@ import com.debatetimer.entity.customize.CustomizeTimeBox; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import java.util.List; import org.springframework.lang.Nullable; public record CustomizeTimeBoxCreateRequest( @@ -22,6 +23,9 @@ public record CustomizeTimeBoxCreateRequest( @Nullable Integer time, + @Nullable + List bell, + @Nullable Integer timePerTeam, diff --git a/src/main/java/com/debatetimer/dto/customize/response/BellResponse.java b/src/main/java/com/debatetimer/dto/customize/response/BellResponse.java new file mode 100644 index 00000000..ce2dae8c --- /dev/null +++ b/src/main/java/com/debatetimer/dto/customize/response/BellResponse.java @@ -0,0 +1,7 @@ +package com.debatetimer.dto.customize.response; + +public record BellResponse( + int time, + int count +) { +} diff --git a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java index a890db68..587e61c5 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java @@ -15,6 +15,13 @@ public CustomizeTableResponse( toTimeBoxResponses(customizeTimeBoxes)); } + public CustomizeTableResponse( + CustomizeTable customizeTable, + List timeBoxResponses + ) { + this(customizeTable.getId(), new CustomizeTableInfoResponse(customizeTable), timeBoxResponses); + } + private static List toTimeBoxResponses(CustomizeTimeBoxes timeBoxes) { List customizeTimeBoxes = timeBoxes.getTimeBoxes(); return customizeTimeBoxes diff --git a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java index cca2eff9..e62f17fb 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java @@ -3,12 +3,14 @@ import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.Stance; import com.debatetimer.entity.customize.CustomizeTimeBox; +import java.util.List; public record CustomizeTimeBoxResponse( Stance stance, String speechType, CustomizeBoxType boxType, Integer time, + List bell, Integer timePerTeam, Integer timePerSpeaking, String speaker @@ -20,6 +22,20 @@ public CustomizeTimeBoxResponse(CustomizeTimeBox customizeTimeBox) { customizeTimeBox.getSpeechType(), customizeTimeBox.getBoxType(), convertTime(customizeTimeBox), + null, + customizeTimeBox.getTimePerTeam(), + customizeTimeBox.getTimePerSpeaking(), + customizeTimeBox.getSpeaker() + ); + } + + public CustomizeTimeBoxResponse(CustomizeTimeBox customizeTimeBox, List bell) { + this( + customizeTimeBox.getStance(), + customizeTimeBox.getSpeechType(), + customizeTimeBox.getBoxType(), + convertTime(customizeTimeBox), + bell, customizeTimeBox.getTimePerTeam(), customizeTimeBox.getTimePerSpeaking(), customizeTimeBox.getSpeaker() diff --git a/src/main/java/com/debatetimer/entity/customize/BellEntity.java b/src/main/java/com/debatetimer/entity/customize/BellEntity.java new file mode 100644 index 00000000..43d4df71 --- /dev/null +++ b/src/main/java/com/debatetimer/entity/customize/BellEntity.java @@ -0,0 +1,60 @@ +package com.debatetimer.entity.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Table(name = "bell") +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class BellEntity { + + public static final int MAX_BELL_COUNT = 3; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "customize_time_box_id") + private CustomizeTimeBox customizeTimeBox; + + @Column(name = "bell_time") + private int time; + private int count; + + public BellEntity(CustomizeTimeBox customizeTimeBox, int time, int count) { + validateTime(time); + validateCount(count); + + this.customizeTimeBox = customizeTimeBox; + 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/exception/errorcode/ClientErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java index 35fee335..e0ac6cf3 100644 --- a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java +++ b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java @@ -3,6 +3,7 @@ import com.debatetimer.domain.customize.Agenda; import com.debatetimer.domain.customize.TableName; import com.debatetimer.domain.customize.TeamName; +import com.debatetimer.entity.customize.BellEntity; import com.debatetimer.entity.customize.CustomizeTimeBox; import lombok.Getter; import org.springframework.http.HttpStatus; @@ -65,6 +66,9 @@ public enum ClientErrorCode implements ResponseErrorCode { ALREADY_DISCONNECTED(HttpStatus.BAD_REQUEST, "이미 클라이언트에서 요청이 종료되었습니다."), NO_COOKIE_FOUND(HttpStatus.BAD_REQUEST, "필수 쿠키 값이 존재하지 않습니다."), FILE_UPLOAD_ERROR(HttpStatus.BAD_REQUEST, "파일 업로드에 실패했습니다."), + + INVALID_BELL_TIME(HttpStatus.BAD_REQUEST, "벨 시간은 0 이상의 정수여야 합니다."), + INVALID_BELL_COUNT(HttpStatus.BAD_REQUEST, "벨 카운트는 1 이상 %d 이하의 정수여야 합니다.".formatted(BellEntity.MAX_BELL_COUNT)), ; private final HttpStatus status; diff --git a/src/main/java/com/debatetimer/repository/customize/BellRepository.java b/src/main/java/com/debatetimer/repository/customize/BellRepository.java new file mode 100644 index 00000000..78569835 --- /dev/null +++ b/src/main/java/com/debatetimer/repository/customize/BellRepository.java @@ -0,0 +1,17 @@ +package com.debatetimer.repository.customize; + +import com.debatetimer.entity.customize.BellEntity; +import com.debatetimer.entity.customize.CustomizeTimeBox; +import java.util.List; +import org.springframework.data.repository.Repository; + +public interface BellRepository extends Repository { + + BellEntity save(BellEntity bell); + + List findByCustomizeTimeBox(CustomizeTimeBox customizeTimeBox); + + void deleteAllByCustomizeTimeBoxIn(List customizeTimeBoxes); + + List findAllByCustomizeTimeBoxIn(List timeBoxes); +} diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeService.java b/src/main/java/com/debatetimer/service/customize/CustomizeService.java index 840a60b0..7281a77a 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeService.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeService.java @@ -6,8 +6,6 @@ import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; import com.debatetimer.dto.customize.response.CustomizeTableResponse; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.exception.custom.DTClientErrorException; -import com.debatetimer.exception.errorcode.ClientErrorCode; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import java.util.List; @@ -33,8 +31,7 @@ public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateReques @Transactional(readOnly = true) public CustomizeTableResponse findTable(long tableId, Member member) { - CustomizeTableEntity tableEntity = tableRepository.findByIdAndMember(tableId, member) - .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxes); } @@ -56,8 +53,7 @@ public CustomizeTableResponse updateTable( @Transactional public CustomizeTableResponse updateUsedAt(long tableId, Member member) { - CustomizeTableEntity tableEntity = tableRepository.findByIdAndMember(tableId, member) - .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); tableEntity.updateUsedAt(); @@ -66,8 +62,7 @@ public CustomizeTableResponse updateUsedAt(long tableId, Member member) { @Transactional public void deleteTable(long tableId, Member member) { - CustomizeTableEntity table = tableRepository.findByIdAndMember(tableId, member) - .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + CustomizeTableEntity table = tableRepository.getByIdAndMember(tableId, member); timeBoxRepository.deleteAllByTable(table); tableRepository.delete(table); } diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java b/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java new file mode 100644 index 00000000..423948a7 --- /dev/null +++ b/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java @@ -0,0 +1,136 @@ +package com.debatetimer.service.customize; + +import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.customize.CustomizeTimeBoxes; +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.CustomizeTimeBoxCreateRequest; +import com.debatetimer.dto.customize.response.BellResponse; +import com.debatetimer.dto.customize.response.CustomizeTableResponse; +import com.debatetimer.dto.customize.response.CustomizeTimeBoxResponse; +import com.debatetimer.entity.customize.BellEntity; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.repository.customize.BellRepository; +import com.debatetimer.repository.customize.CustomizeTableRepository; +import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; +import java.util.List; +import java.util.stream.IntStream; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class CustomizeServiceV2 { + + 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); + CustomizeTableEntity savedTable = tableRepository.save(new CustomizeTableEntity(table)); + + return saveTimeBoxesAndBells(tableCreateRequest, savedTable.toDomain()); + } + + @Transactional(readOnly = true) + public CustomizeTableResponse findTable(long tableId, Member member) { + CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); + CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); + List timeBoxResponses = timeBoxes.getTimeBoxes() + .stream() + .map(this::getTimeBoxResponse) + .toList(); + return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxResponses); + } + + @Transactional + public CustomizeTableResponse updateTable( + CustomizeTableCreateRequest tableCreateRequest, + long tableId, + Member member + ) { + CustomizeTableEntity existingTable = tableRepository.getByIdAndMember(tableId, member); + CustomizeTable renewedTable = tableCreateRequest.toTable(member); + existingTable.updateTable(renewedTable); + + deleteBell(timeBoxRepository.findTableTimeBoxes(existingTable)); + timeBoxRepository.deleteAllByTable(existingTable); + return saveTimeBoxesAndBells(tableCreateRequest, existingTable.toDomain()); + } + + @Transactional + public CustomizeTableResponse updateUsedAt(long tableId, Member member) { + CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); + CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); + tableEntity.updateUsedAt(); + List timeBoxResponses = timeBoxes.getTimeBoxes() + .stream() + .map(this::getTimeBoxResponse) + .toList(); + return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxResponses); + } + + @Transactional + public void deleteTable(long tableId, Member member) { + CustomizeTableEntity table = tableRepository.getByIdAndMember(tableId, member); + + deleteBell(timeBoxRepository.findTableTimeBoxes(table)); + timeBoxRepository.deleteAllByTable(table); + tableRepository.delete(table); + } + + private CustomizeTableResponse saveTimeBoxesAndBells( + CustomizeTableCreateRequest tableCreateRequest, + CustomizeTable table + ) { + List timeBoxCreateRequests = tableCreateRequest.table(); + List timeBoxResponses = IntStream.range(0, timeBoxCreateRequests.size()) + .mapToObj(i -> createTimeBoxResponse(timeBoxCreateRequests.get(i), table, i + 1)) + .toList(); + return new CustomizeTableResponse(table, timeBoxResponses); + } + + private CustomizeTimeBoxResponse createTimeBoxResponse( + CustomizeTimeBoxCreateRequest request, + CustomizeTable table, + int sequence + ) { + CustomizeTimeBox savedTimeBox = timeBoxRepository.save(request.toTimeBox(table, sequence)); + return createTimeBoxResponse(request.bell(), savedTimeBox); + } + + private CustomizeTimeBoxResponse createTimeBoxResponse(List bellRequests, CustomizeTimeBox timeBox) { + if (timeBox.getBoxType().isTimeBased()) { + return new CustomizeTimeBoxResponse(timeBox, null); + } + + List bellResponses = bellRequests + .stream() + .map(bellRequest -> new BellEntity(timeBox, bellRequest.time(), bellRequest.count())) + .map(bellRepository::save) + .map(bell -> new BellResponse(bell.getTime(), bell.getCount())) + .toList(); + return new CustomizeTimeBoxResponse(timeBox, bellResponses); + } + + private CustomizeTimeBoxResponse getTimeBoxResponse(CustomizeTimeBox timeBox) { + if (timeBox.getBoxType().isTimeBased()) { + return new CustomizeTimeBoxResponse(timeBox, null); + } + + List bellResponses = bellRepository.findByCustomizeTimeBox(timeBox) + .stream() + .map(bell -> new BellResponse(bell.getTime(), bell.getCount())) + .toList(); + return new CustomizeTimeBoxResponse(timeBox, bellResponses); + } + + private void deleteBell(CustomizeTimeBoxes savedCustomizeTimeBoxes) { + bellRepository.deleteAllByCustomizeTimeBoxIn(savedCustomizeTimeBoxes.getTimeBoxes()); + } +} diff --git a/src/main/resources/db/migration/V10__add_bell_table.sql b/src/main/resources/db/migration/V10__add_bell_table.sql new file mode 100644 index 00000000..b6e4c5cf --- /dev/null +++ b/src/main/resources/db/migration/V10__add_bell_table.sql @@ -0,0 +1,13 @@ +create table bell +( + id bigint auto_increment, + bell_time bigint not null, + count bigint not null, + customize_time_box_id bigint not null, + primary key (id) +); + +alter table bell + add constraint bell_to_customize_time_box + foreign key (customize_time_box_id) + references customize_time_box(id); diff --git a/src/test/java/com/debatetimer/controller/BaseControllerTest.java b/src/test/java/com/debatetimer/controller/BaseControllerTest.java index 78976392..2ca415ea 100644 --- a/src/test/java/com/debatetimer/controller/BaseControllerTest.java +++ b/src/test/java/com/debatetimer/controller/BaseControllerTest.java @@ -4,6 +4,7 @@ import com.debatetimer.client.oauth.OAuthClient; import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.Stance; +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; @@ -97,8 +98,15 @@ private ArbitraryBuilder getCustomizeTimeBoxCreat .set("speechType", "입론1") .set("boxType", CustomizeBoxType.NORMAL) .set("time", 120) + .set("bell", getBellRequestBuilder().sampleList(2)) .set("timePerTeam", 60) .set("timePerSpeaking", null) .set("speaker", "발언자"); } + + private ArbitraryBuilder getBellRequestBuilder() { + return fixtureMonkey.giveMeBuilder(BellRequest.class) + .set("time", 30) + .set("count", 1); + } } diff --git a/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java b/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java index c14b37fd..e7545638 100644 --- a/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java +++ b/src/test/java/com/debatetimer/controller/customize/CustomizeDocumentTest.java @@ -20,9 +20,11 @@ import com.debatetimer.controller.Tag; import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.Stance; +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.BellResponse; import com.debatetimer.dto.customize.response.CustomizeTableInfoResponse; import com.debatetimer.dto.customize.response.CustomizeTableResponse; import com.debatetimer.dto.customize.response.CustomizeTimeBoxResponse; @@ -68,6 +70,9 @@ class Save { fieldWithPath("table[].speechType").type(STRING).description("발언 유형"), 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[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -89,6 +94,9 @@ class Save { fieldWithPath("table[].speechType").type(STRING).description("발언 유형"), 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[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -101,13 +109,13 @@ class Save { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "콜리"), + 120, List.of(new BellRequest(90, 1)), null, null, "콜리"), new CustomizeTimeBoxCreateRequest(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "비토"), + 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), null, null, "비토"), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, - null, 360, 120, null), + null, null, 360, 120, null), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, - null, 360, null, null) + null, null, 360, null, null) ) ); CustomizeTableResponse response = new CustomizeTableResponse( @@ -116,13 +124,13 @@ class Save { "찬성", "반대", true, true), List.of( new CustomizeTimeBoxResponse(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "콜리"), + 120, List.of(new BellResponse(90, 1)), null, null, "콜리"), new CustomizeTimeBoxResponse(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "비토"), + 120, List.of(new BellResponse(90, 1), new BellResponse(120, 2)), null, null, "비토"), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, - null, 360, 120, null), + null, null, 360, 120, null), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, - null, 360, null, null) + null, null, 360, null, null) ) ); doReturn(response).when(customizeService).save(eq(request), any()); @@ -160,13 +168,13 @@ class Save { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "콜리"), + 120, List.of(new BellRequest(90, 1)), null, null, "콜리"), new CustomizeTimeBoxCreateRequest(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "비토"), + 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), null, null, "비토"), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, - null, 360, 120, null), + null, null, 360, 120, null), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, - null, 360, null, null) + null, null, 360, null, null) ) ); doThrow(new DTClientErrorException(errorCode)).when(customizeService).save(eq(request), any()); @@ -221,6 +229,9 @@ class GetTable { fieldWithPath("table[].speechType").type(STRING).description("발언 유형"), 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[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -235,13 +246,13 @@ class GetTable { "찬성", "반대", true, true), List.of( new CustomizeTimeBoxResponse(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "콜리"), + 120, List.of(new BellResponse(90, 1)), null, null, "콜리"), new CustomizeTimeBoxResponse(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "비토"), + 120, List.of(new BellResponse(90, 1), new BellResponse(120, 2)), null, null, "비토"), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, - null, 360, 120, null), + null, null, 360, 120, null), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, - null, 360, null, null) + null, null, 360, null, null) ) ); doReturn(response).when(customizeService).findTable(eq(tableId), any()); @@ -311,6 +322,9 @@ class UpdateTable { fieldWithPath("table[].speechType").type(STRING).description("발언 유형"), 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[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -332,6 +346,9 @@ class UpdateTable { fieldWithPath("table[].speechType").type(STRING).description("발언 유형"), 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[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -345,13 +362,13 @@ class UpdateTable { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "콜리"), + 120, List.of(new BellRequest(90, 1)), null, null, "콜리"), new CustomizeTimeBoxCreateRequest(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "비토"), + 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), null, null, "비토"), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, - null, 360, 120, null), + null, null, 360, 120, null), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, - null, 360, null, null) + null, null, 360, null, null) ) ); CustomizeTableResponse response = new CustomizeTableResponse( @@ -360,13 +377,13 @@ class UpdateTable { "찬성", "반대", true, true), List.of( new CustomizeTimeBoxResponse(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "콜리"), + 120, List.of(new BellResponse(90, 1)), null, null, "콜리"), new CustomizeTimeBoxResponse(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "비토"), + 120, List.of(new BellResponse(90, 1), new BellResponse(120, 2)), null, null, "비토"), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, - null, 360, 120, null), + null, null, 360, 120, null), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, - null, 360, null, null) + null, null, 360, null, null) ) ); doReturn(response).when(customizeService).updateTable(eq(request), eq(tableId), any()); @@ -407,13 +424,13 @@ class UpdateTable { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "콜리"), + 120, List.of(new BellRequest(90, 1)), null, null, "콜리"), new CustomizeTimeBoxCreateRequest(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "비토"), + 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), null, null, "비토"), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, - null, 360, 120, null), + null, null, 360, 120, null), new CustomizeTimeBoxCreateRequest(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, - null, 360, null, null) + null, null, 360, null, null) ) ); doThrow(new DTClientErrorException(errorCode)).when(customizeService) @@ -470,6 +487,9 @@ class Debate { fieldWithPath("table[].speechType").type(STRING).description("발언 유형"), 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[].timePerTeam").type(NUMBER).description("팀당 발언 시간 (초)").optional(), fieldWithPath("table[].timePerSpeaking").type(NUMBER).description("1회 발언 시간 (초)").optional(), fieldWithPath("table[].speaker").type(STRING).description("발언자 이름").optional() @@ -485,13 +505,13 @@ class Debate { "찬성", "반대", true, true), List.of( new CustomizeTimeBoxResponse(Stance.PROS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "콜리"), + 120, List.of(new BellResponse(90, 1)), null, null, "콜리"), new CustomizeTimeBoxResponse(Stance.CONS, "입론", CustomizeBoxType.NORMAL, - 120, null, null, "비토"), + 120, List.of(new BellResponse(90, 1), new BellResponse(120, 2)), null, null, "비토"), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "난상 토론", CustomizeBoxType.TIME_BASED, - null, 360, 120, null), + null, null, 360, 120, null), new CustomizeTimeBoxResponse(Stance.NEUTRAL, "존중 토론", CustomizeBoxType.TIME_BASED, - null, 360, null, null) + null, null, 360, null, null) ) ); doReturn(response).when(customizeService).updateUsedAt(eq(tableId), any()); diff --git a/src/test/java/com/debatetimer/entity/customize/BellEntityTest.java b/src/test/java/com/debatetimer/entity/customize/BellEntityTest.java new file mode 100644 index 00000000..73535499 --- /dev/null +++ b/src/test/java/com/debatetimer/entity/customize/BellEntityTest.java @@ -0,0 +1,32 @@ +package com.debatetimer.entity.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; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class BellEntityTest { + + @Nested + class Validate { + + @Test + void 벨_시간은_0이상이어야_한다() { + assertThatThrownBy(() -> new BellEntity(null, -1, 1)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_BELL_TIME.getMessage()); + } + + @ValueSource(ints = {0, BellEntity.MAX_BELL_COUNT + 1}) + @ParameterizedTest + void 벨_횟수는_정해진_횟수_이내여야_한다(int count) { + assertThatThrownBy(() -> new BellEntity(null, 1, count)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_BELL_COUNT.getMessage()); + } + } +} diff --git a/src/test/java/com/debatetimer/fixture/BellGenerator.java b/src/test/java/com/debatetimer/fixture/BellGenerator.java new file mode 100644 index 00000000..b9698fd3 --- /dev/null +++ b/src/test/java/com/debatetimer/fixture/BellGenerator.java @@ -0,0 +1,21 @@ +package com.debatetimer.fixture; + +import com.debatetimer.entity.customize.BellEntity; +import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.repository.customize.BellRepository; +import org.springframework.stereotype.Component; + +@Component +public class BellGenerator { + + private final BellRepository bellRepository; + + public BellGenerator(BellRepository bellRepository) { + this.bellRepository = bellRepository; + } + + public BellEntity generate(CustomizeTimeBox timeBox, int time, int count) { + BellEntity bell = new BellEntity(timeBox, time, count); + return bellRepository.save(bell); + } +} diff --git a/src/test/java/com/debatetimer/service/BaseServiceTest.java b/src/test/java/com/debatetimer/service/BaseServiceTest.java index f512da4e..6220cc2a 100644 --- a/src/test/java/com/debatetimer/service/BaseServiceTest.java +++ b/src/test/java/com/debatetimer/service/BaseServiceTest.java @@ -1,9 +1,11 @@ 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.repository.customize.BellRepository; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import com.debatetimer.repository.member.MemberRepository; @@ -26,6 +28,9 @@ public abstract class BaseServiceTest { @Autowired protected CustomizeTimeBoxRepository customizeTimeBoxRepository; + @Autowired + protected BellRepository bellRepository; + @Autowired protected MemberGenerator memberGenerator; @@ -35,6 +40,9 @@ public abstract class BaseServiceTest { @Autowired protected CustomizeTimeBoxGenerator customizeTimeBoxGenerator; + @Autowired + protected BellGenerator bellGenerator; + protected void runAtSameTime(int count, Runnable task) throws InterruptedException { List threads = IntStream.range(0, count) .mapToObj(i -> new Thread(task)) diff --git a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java index ad4d9e16..0223170a 100644 --- a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java +++ b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java @@ -7,6 +7,7 @@ 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; @@ -39,19 +40,18 @@ class Save { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자1"), + 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자2") + 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") ) ); CustomizeTableResponse savedTableResponse = customizeService.save(customizeTableCreateRequest, chan); - Optional foundTable = customizeTableRepository.findById(savedTableResponse.id()); - List foundTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( - foundTable.get()); + CustomizeTableEntity foundTable = customizeTableRepository.getByIdAndMember(savedTableResponse.id(), chan); + List foundTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(foundTable); assertAll( - () -> assertThat(foundTable.get().getName()).isEqualTo(customizeTableCreateRequest.info().name()), + () -> assertThat(foundTable.getName()).isEqualTo(customizeTableCreateRequest.info().name()), () -> assertThat(foundTimeBoxes).hasSize(customizeTableCreateRequest.table().size()) ); } @@ -100,21 +100,20 @@ class UpdateTable { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자1"), + 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자2") + 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") ) ); customizeService.updateTable(renewTableRequest, chanTable.getId(), chan); - Optional updatedTable = customizeTableRepository.findById(chanTable.getId()); - List updatedTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( - updatedTable.get()); + CustomizeTableEntity updatedTable = customizeTableRepository.getByIdAndMember(chanTable.getId(), chan); + List updatedTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(updatedTable); assertAll( - () -> assertThat(updatedTable.get().getId()).isEqualTo(chanTable.getId()), - () -> assertThat(updatedTable.get().getName()).isEqualTo(renewTableRequest.info().name()), + () -> assertThat(updatedTable.getId()).isEqualTo(chanTable.getId()), + () -> assertThat(updatedTable.getName()).isEqualTo(renewTableRequest.info().name()), () -> assertThat(updatedTimeBoxes).hasSize(renewTableRequest.table().size()) ); } @@ -130,9 +129,9 @@ class UpdateTable { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자1"), + 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자2") + 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") ) ); @@ -150,9 +149,9 @@ class UpdateTable { "반대", true, true), List.of( new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론1", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자1"), + 120, List.of(new BellRequest(90, 1)), 60, null, "발언자1"), new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자2") + 120, List.of(new BellRequest(90, 1), new BellRequest(120, 2)), 60, null, "발언자2") ) ); @@ -173,10 +172,10 @@ class UpdateUsedAt { customizeService.updateUsedAt(table.getId(), member); - Optional updatedTable = customizeTableRepository.findById(table.getId()); + CustomizeTableEntity updatedTable = customizeTableRepository.getByIdAndMember(table.getId(), member); assertAll( - () -> assertThat(updatedTable.get().getId()).isEqualTo(table.getId()), - () -> assertThat(updatedTable.get().getUsedAt()).isAfter(beforeUsedAt) + () -> assertThat(updatedTable.getId()).isEqualTo(table.getId()), + () -> assertThat(updatedTable.getUsedAt()).isAfter(beforeUsedAt) ); } @@ -186,18 +185,8 @@ class UpdateUsedAt { 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, 60, null, "발언자1"), - new CustomizeTimeBoxCreateRequest(Stance.PROS, "입론2", CustomizeBoxType.NORMAL, - 120, 60, null, "발언자2") - ) - ); - assertThatThrownBy(() -> customizeService.updateTable(renewTableRequest, chanTableId, coli)) + assertThatThrownBy(() -> customizeService.updateUsedAt(chanTableId, coli)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.TABLE_NOT_FOUND.getMessage()); } diff --git a/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java b/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java new file mode 100644 index 00000000..50811628 --- /dev/null +++ b/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java @@ -0,0 +1,241 @@ +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.CustomizeTimeBox; +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); + CustomizeTimeBox 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()).hasSize(0) + ); + } + + @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()); + } + } +} From b25a8214857ebe16c4bd391d76078709211f5dc8 Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Wed, 16 Jul 2025 14:53:03 +0900 Subject: [PATCH 18/27] =?UTF-8?q?[REFACTOR]=20=ED=83=80=EC=9E=84=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EB=8F=84=EB=A9=94=EC=9D=B8=20-=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=B6=84=EB=A6=AC=20/=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B5=AC=ED=98=84=20(#198)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customize/CustomizeTimeBoxDomain.java | 72 +++++++++++ .../domain/customize/NormalTimeBoxDomain.java | 50 ++++++++ .../debatetimer/domain/customize/Stance.java | 5 + .../customize/TimeBasedTimeBoxDomain.java | 72 +++++++++++ .../customize/CustomizeTimeBoxDomainTest.java | 113 ++++++++++++++++++ .../customize/NormalTimeBoxDomainTest.java | 42 +++++++ .../customize/TimeBasedTimeBoxDomainTest.java | 93 ++++++++++++++ 7 files changed, 447 insertions(+) create mode 100644 src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java create mode 100644 src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java create mode 100644 src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java create mode 100644 src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java create mode 100644 src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java create mode 100644 src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java new file mode 100644 index 00000000..18ac7e84 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java @@ -0,0 +1,72 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import org.springframework.lang.Nullable; + +public abstract class CustomizeTimeBoxDomain { + + public static final int SPEECH_TYPE_MAX_LENGTH = 10; + public static final int SPEAKER_MAX_LENGTH = 5; + + private final Stance stance; + + private final String speechType; + + @Nullable + private final String speaker; + + protected CustomizeTimeBoxDomain(Stance stance, String speechType, @Nullable String speaker) { + validateStance(stance); + validateSpeechType(speechType); + validateSpeaker(speaker); + + this.stance = stance; + this.speechType = speechType; + this.speaker = speaker; + } + + private void validateStance(Stance stance) { + if (stance == null || !isValidStance(stance)) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_STANCE); + } + } + + protected abstract boolean isValidStance(Stance stance); + + private void validateSpeechType(String speechType) { + if (speechType == null || speechType.isBlank() || speechType.length() > SPEECH_TYPE_MAX_LENGTH) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH); + } + } + + private void validateSpeaker(String speaker) { + if (speaker != null && speaker.length() > SPEAKER_MAX_LENGTH) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH); + } + } + + public final Stance getStance() { + return stance; + } + + public final String getSpeechType() { + return speechType; + } + + @Nullable + public final String getSpeaker() { + return speaker; + } + + public abstract CustomizeBoxType getBoxType(); + + @Nullable + public abstract Integer getTime(); + + @Nullable + public abstract Integer getTimePerTeam(); + + @Nullable + public abstract Integer getTimePerSpeaking(); +} diff --git a/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java new file mode 100644 index 00000000..a17800d8 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java @@ -0,0 +1,50 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import org.springframework.lang.Nullable; + +public final class NormalTimeBoxDomain extends CustomizeTimeBoxDomain { + + private final int time; + + public NormalTimeBoxDomain(Stance stance, String speechType, @Nullable String speaker, Integer time) { + super(stance, speechType, speaker); + + validateTime(time); + this.time = time; + } + + private void validateTime(Integer time) { + if (time == null || time <= 0) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_TIME); + } + } + + @Override + protected boolean isValidStance(Stance stance) { + return true; + } + + @Override + public CustomizeBoxType getBoxType() { + return CustomizeBoxType.NORMAL; + } + + @Override + public Integer getTime() { + return time; + } + + @Nullable + @Override + public Integer getTimePerTeam() { + return null; + } + + @Nullable + @Override + public Integer getTimePerSpeaking() { + return null; + } +} diff --git a/src/main/java/com/debatetimer/domain/customize/Stance.java b/src/main/java/com/debatetimer/domain/customize/Stance.java index fd9b0975..0df12c9d 100644 --- a/src/main/java/com/debatetimer/domain/customize/Stance.java +++ b/src/main/java/com/debatetimer/domain/customize/Stance.java @@ -5,4 +5,9 @@ public enum Stance { PROS, CONS, NEUTRAL, + ; + + public boolean isNeutralStance() { + return this == NEUTRAL; + } } diff --git a/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java new file mode 100644 index 00000000..30dba5ab --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java @@ -0,0 +1,72 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import org.springframework.lang.Nullable; + +public class TimeBasedTimeBoxDomain extends CustomizeTimeBoxDomain { + + private final int timePerTeam; + + @Nullable + private final Integer timePerSpeaking; + + public TimeBasedTimeBoxDomain(Stance stance, + String speechType, + @Nullable String speaker, + Integer timePerTeam, + @Nullable Integer timePerSpeaking) { + super(stance, speechType, speaker); + + validateTimes(timePerTeam, timePerSpeaking); + this.timePerTeam = timePerTeam; + this.timePerSpeaking = timePerSpeaking; + } + + private void validateTimes(Integer timePerTeam, Integer timePerSpeaking) { + validateTimePerTeam(timePerTeam); + validateTimePerSpeaking(timePerSpeaking); + if (timePerSpeaking != null && timePerTeam < timePerSpeaking) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BASED_TIME); + } + } + + private void validateTimePerTeam(Integer time) { + if (time == null || time <= 0) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_TIME); + } + } + + private void validateTimePerSpeaking(Integer timePerSpeaking) { + if (timePerSpeaking != null && timePerSpeaking <= 0) { + throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_TIME); + } + } + + @Override + protected boolean isValidStance(Stance stance) { + return stance.isNeutralStance(); + } + + @Override + public CustomizeBoxType getBoxType() { + return CustomizeBoxType.TIME_BASED; + } + + @Override + @Nullable + public Integer getTime() { + return null; + } + + @Override + public Integer getTimePerTeam() { + return timePerTeam; + } + + @Override + @Nullable + public Integer getTimePerSpeaking() { + return timePerSpeaking; + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java new file mode 100644 index 00000000..be331821 --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java @@ -0,0 +1,113 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThatCode; +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; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +class CustomizeTimeBoxDomainTest { + + @Nested + class ValidateStance { + + @Test + void 발언_입장은_비어있을_수_없다() { + assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(null, "비토", "발언자")) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_STANCE.getMessage()); + } + + @Test + void 발언_입장은_유효한_값이어야_한다() { + assertThatCode(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, "비토", "발언자")) + .doesNotThrowAnyException(); + } + } + + @Nested + class ValidateSpeechType { + + @Test + void 발언_종류는_특정_글자를_초과할_수_없다() { + String speechType = "a".repeat(CustomizeTimeBoxDomain.SPEECH_TYPE_MAX_LENGTH + 1); + + assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, speechType, "비토")) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH.getMessage()); + } + + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = {" ", "\n\t"}) + void 발언_종류는_비어있을_수_없다(String emptySpeechType) { + assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, emptySpeechType, "비토")) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH.getMessage()); + } + + @Test + void 발언_종류는_특정_글자_이내이어야_한다() { + String speechType = "a".repeat(CustomizeTimeBoxDomain.SPEECH_TYPE_MAX_LENGTH); + + assertThatCode(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, speechType, "비토")) + .doesNotThrowAnyException(); + } + } + + @Nested + class ValidateSpeaker { + + @Test + void 발언자_이름은_특정_글자를_초과할_수_없다() { + String speaker = "a".repeat(CustomizeTimeBoxDomain.SPEAKER_MAX_LENGTH + 1); + + assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, "비토", speaker)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH.getMessage()); + } + + @Test + void 발언자_이름은_비어있을_수_있다() { + assertThatCode(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, "비토", null)) + .doesNotThrowAnyException(); + } + } + + static class InheritedCustomizeTimeBoxDomain extends CustomizeTimeBoxDomain { + + protected InheritedCustomizeTimeBoxDomain(Stance stance, String speechType, String speaker) { + super(stance, speechType, speaker); + } + + @Override + protected boolean isValidStance(Stance stance) { + return true; + } + + @Override + public CustomizeBoxType getBoxType() { + return null; + } + + @Override + public Integer getTime() { + return 0; + } + + @Override + public Integer getTimePerTeam() { + return 0; + } + + @Override + public Integer getTimePerSpeaking() { + return 0; + } + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java new file mode 100644 index 00000000..810ea4ce --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java @@ -0,0 +1,42 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThatCode; +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 NormalTimeBoxDomainTest { + + @Nested + class ValidateTime { + + @Test + void 시간은_0보다_커야_한다() { + Integer time = 0; + + assertThatThrownBy(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + + @Test + void 시간은_비어있지_않아야_한다() { + Integer time = null; + + assertThatThrownBy(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + + @Test + void 시간은_양수여야_한다() { + Integer time = 1; + + assertThatCode(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) + .doesNotThrowAnyException(); + } + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java new file mode 100644 index 00000000..484d0e1a --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java @@ -0,0 +1,93 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThatCode; +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 TimeBasedTimeBoxDomainTest { + + @Nested + class ValidateStance { + + @Test + void 중립_스탠스가_아니면_예외가_발생한다() { + Stance stance = Stance.PROS; + + assertThatThrownBy( + () -> new TimeBasedTimeBoxDomain(stance, "자유발언", "비토", 120, 60)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_STANCE.getMessage()); + } + + @Test + void 중립_스탠스면_예외가_발생하지_않는다() { + Stance stance = Stance.NEUTRAL; + + assertThatCode( + () -> new TimeBasedTimeBoxDomain(stance, "자유발언", "비토", 120, 60)) + .doesNotThrowAnyException(); + } + } + + @Nested + class ValidateTimes { + + @Test + void 팀_당_발언_시간이_양수이어야_한다() { + int timePerTeam = 0; + int timePerSpeaking = 1; + + assertThatThrownBy( + () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + + @Test + void 팀_당_발언_시간이_비어있으면_안된다() { + Integer timePerTeam = null; + int timePerSpeaking = 1; + + assertThatThrownBy( + () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + + @Test + void 회_당_시간이_양수이어야_한다() { + int timePerSpeaking = 0; + int timePerTeam = 1; + + assertThatThrownBy( + () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); + } + + @Test + void 회_당_발언_시간은_비어있을_수_있다() { + Integer timePerSpeaking = null; + int timePerTeam = 1; + + assertThatCode( + () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + .doesNotThrowAnyException(); + } + + @Test + void 팀_당_발언시간은_회_당_발언시간보다_많거나_같아야_한다() { + int timePerTeam = 60; + int timePerSpeaking = timePerTeam + 1; + + assertThatThrownBy( + () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_TIME_BASED_TIME.getMessage()); + } + } +} From 526872128fa661069af0307231069cc65a860e9b Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Thu, 17 Jul 2025 11:19:51 +0900 Subject: [PATCH 19/27] =?UTF-8?q?[REFACTOR]=20=ED=83=80=EC=9E=84=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EB=8F=84=EB=A9=94=EC=9D=B8,=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EB=B6=84=EB=A6=AC=20-=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95=20=EC=99=B8=20?= =?UTF-8?q?3=EA=B0=9C=20(#200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../debatetimer/domain/customize/Bell.java | 33 +++++++++++++ ...meBoxDomain.java => CustomizeTimeBox.java} | 7 ++- .../customize/CustomizeTimeBoxEntities.java | 21 +++++++++ .../domain/customize/CustomizeTimeBoxes.java | 21 --------- ...lTimeBoxDomain.java => NormalTimeBox.java} | 13 ++++-- ...meBoxDomain.java => TimeBasedTimeBox.java} | 19 +++++--- .../dto/customize/request/BellRequest.java | 6 +++ .../request/CustomizeTableCreateRequest.java | 17 +++++-- .../CustomizeTimeBoxCreateRequest.java | 29 ++++++++++-- .../dto/customize/response/BellResponse.java | 6 +++ .../response/CustomizeTableResponse.java | 27 +++++++++-- .../response/CustomizeTimeBoxResponse.java | 33 +++++++++++-- .../entity/customize/BellEntity.java | 4 +- ...meBox.java => CustomizeTimeBoxEntity.java} | 8 ++-- .../exception/errorcode/ClientErrorCode.java | 10 ++-- .../repository/customize/BellRepository.java | 8 ++-- .../customize/CustomizeTimeBoxRepository.java | 20 ++++---- .../service/customize/CustomizeService.java | 19 ++++---- .../service/customize/CustomizeServiceV2.java | 16 +++---- .../domain/customize/BellTest.java | 46 +++++++++++++++++++ ...java => CustomizeTimeBoxEntitiesTest.java} | 12 ++--- ...ainTest.java => CustomizeTimeBoxTest.java} | 33 +++++++------ ...DomainTest.java => NormalTimeBoxTest.java} | 9 ++-- ...ainTest.java => TimeBasedTimeBoxTest.java} | 16 +++---- ...t.java => CustomizeTimeBoxEntityTest.java} | 34 +++++++------- .../debatetimer/fixture/BellGenerator.java | 4 +- .../fixture/CustomizeTimeBoxGenerator.java | 12 ++--- .../CustomizeTimeBoxRepositoryTest.java | 12 ++--- .../customize/CustomizeServiceTest.java | 8 ++-- .../customize/CustomizeServiceV2Test.java | 14 +++--- 30 files changed, 354 insertions(+), 163 deletions(-) create mode 100644 src/main/java/com/debatetimer/domain/customize/Bell.java rename src/main/java/com/debatetimer/domain/customize/{CustomizeTimeBoxDomain.java => CustomizeTimeBox.java} (90%) create mode 100644 src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntities.java delete mode 100644 src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java rename src/main/java/com/debatetimer/domain/customize/{NormalTimeBoxDomain.java => NormalTimeBox.java} (74%) rename src/main/java/com/debatetimer/domain/customize/{TimeBasedTimeBoxDomain.java => TimeBasedTimeBox.java} (79%) rename src/main/java/com/debatetimer/entity/customize/{CustomizeTimeBox.java => CustomizeTimeBoxEntity.java} (96%) create mode 100644 src/test/java/com/debatetimer/domain/customize/BellTest.java rename src/test/java/com/debatetimer/domain/customize/{CustomizeTimeBoxesTest.java => CustomizeTimeBoxEntitiesTest.java} (66%) rename src/test/java/com/debatetimer/domain/customize/{CustomizeTimeBoxDomainTest.java => CustomizeTimeBoxTest.java} (74%) rename src/test/java/com/debatetimer/domain/customize/{NormalTimeBoxDomainTest.java => NormalTimeBoxTest.java} (73%) rename src/test/java/com/debatetimer/domain/customize/{TimeBasedTimeBoxDomainTest.java => TimeBasedTimeBoxTest.java} (75%) rename src/test/java/com/debatetimer/entity/customize/{CustomizeTimeBoxTest.java => CustomizeTimeBoxEntityTest.java} (79%) diff --git a/src/main/java/com/debatetimer/domain/customize/Bell.java b/src/main/java/com/debatetimer/domain/customize/Bell.java new file mode 100644 index 00000000..58fb4443 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/Bell.java @@ -0,0 +1,33 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import lombok.Getter; + +@Getter +public class Bell { + + public static final int MAX_BELL_COUNT = 3; + + private final int time; + private final int count; + + public Bell(int time, int count) { + validateTime(time); + validateCount(count); + 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/CustomizeTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBox.java similarity index 90% rename from src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java rename to src/main/java/com/debatetimer/domain/customize/CustomizeTimeBox.java index 18ac7e84..498c9179 100644 --- a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomain.java +++ b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBox.java @@ -2,9 +2,10 @@ import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; +import java.util.List; import org.springframework.lang.Nullable; -public abstract class CustomizeTimeBoxDomain { +public abstract class CustomizeTimeBox { public static final int SPEECH_TYPE_MAX_LENGTH = 10; public static final int SPEAKER_MAX_LENGTH = 5; @@ -16,7 +17,7 @@ public abstract class CustomizeTimeBoxDomain { @Nullable private final String speaker; - protected CustomizeTimeBoxDomain(Stance stance, String speechType, @Nullable String speaker) { + protected CustomizeTimeBox(Stance stance, String speechType, @Nullable String speaker) { validateStance(stance); validateSpeechType(speechType); validateSpeaker(speaker); @@ -69,4 +70,6 @@ public final String getSpeaker() { @Nullable public abstract Integer getTimePerSpeaking(); + + public abstract List getBells(); } diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntities.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntities.java new file mode 100644 index 00000000..442845ac --- /dev/null +++ b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntities.java @@ -0,0 +1,21 @@ +package com.debatetimer.domain.customize; + +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; +import java.util.Comparator; +import java.util.List; +import lombok.Getter; + +@Getter +public class CustomizeTimeBoxEntities { + + private static final Comparator TIME_BOX_COMPARATOR = Comparator + .comparing(CustomizeTimeBoxEntity::getSequence); + + private final List timeBoxes; + + public CustomizeTimeBoxEntities(List timeBoxes) { + this.timeBoxes = timeBoxes.stream() + .sorted(TIME_BOX_COMPARATOR) + .toList(); + } +} diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java deleted file mode 100644 index 585c8bd0..00000000 --- a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxes.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.debatetimer.domain.customize; - -import com.debatetimer.entity.customize.CustomizeTimeBox; -import java.util.Comparator; -import java.util.List; -import lombok.Getter; - -@Getter -public class CustomizeTimeBoxes { - - private static final Comparator TIME_BOX_COMPARATOR = Comparator - .comparing(CustomizeTimeBox::getSequence); - - private final List timeBoxes; - - public CustomizeTimeBoxes(List timeBoxes) { - this.timeBoxes = timeBoxes.stream() - .sorted(TIME_BOX_COMPARATOR) - .toList(); - } -} diff --git a/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/NormalTimeBox.java similarity index 74% rename from src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java rename to src/main/java/com/debatetimer/domain/customize/NormalTimeBox.java index a17800d8..4e413aad 100644 --- a/src/main/java/com/debatetimer/domain/customize/NormalTimeBoxDomain.java +++ b/src/main/java/com/debatetimer/domain/customize/NormalTimeBox.java @@ -2,17 +2,19 @@ import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; +import java.util.List; import org.springframework.lang.Nullable; -public final class NormalTimeBoxDomain extends CustomizeTimeBoxDomain { +public final class NormalTimeBox extends CustomizeTimeBox { private final int time; + private final List bells; - public NormalTimeBoxDomain(Stance stance, String speechType, @Nullable String speaker, Integer time) { + public NormalTimeBox(Stance stance, String speechType, @Nullable String speaker, Integer time, List bells) { super(stance, speechType, speaker); - validateTime(time); this.time = time; + this.bells = bells; } private void validateTime(Integer time) { @@ -47,4 +49,9 @@ public Integer getTimePerTeam() { public Integer getTimePerSpeaking() { return null; } + + @Override + public List getBells() { + return bells; + } } diff --git a/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java b/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBox.java similarity index 79% rename from src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java rename to src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBox.java index 30dba5ab..40018cab 100644 --- a/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomain.java +++ b/src/main/java/com/debatetimer/domain/customize/TimeBasedTimeBox.java @@ -2,20 +2,22 @@ import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; +import java.util.Collections; +import java.util.List; import org.springframework.lang.Nullable; -public class TimeBasedTimeBoxDomain extends CustomizeTimeBoxDomain { +public class TimeBasedTimeBox extends CustomizeTimeBox { private final int timePerTeam; @Nullable private final Integer timePerSpeaking; - public TimeBasedTimeBoxDomain(Stance stance, - String speechType, - @Nullable String speaker, - Integer timePerTeam, - @Nullable Integer timePerSpeaking) { + public TimeBasedTimeBox(Stance stance, + String speechType, + @Nullable String speaker, + Integer timePerTeam, + @Nullable Integer timePerSpeaking) { super(stance, speechType, speaker); validateTimes(timePerTeam, timePerSpeaking); @@ -69,4 +71,9 @@ public Integer getTimePerTeam() { public Integer getTimePerSpeaking() { return timePerSpeaking; } + + @Override + public List getBells() { + return Collections.emptyList(); + } } 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 0c846580..fa5f2d88 100644 --- a/src/main/java/com/debatetimer/dto/customize/request/BellRequest.java +++ b/src/main/java/com/debatetimer/dto/customize/request/BellRequest.java @@ -1,7 +1,13 @@ package com.debatetimer.dto.customize.request; +import com.debatetimer.domain.customize.Bell; + public record BellRequest( int time, int count ) { + + public Bell toDomain() { + return new Bell(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 e0eb509e..f4f9baf1 100644 --- a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableCreateRequest.java +++ b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableCreateRequest.java @@ -1,7 +1,8 @@ package com.debatetimer.dto.customize.request; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBoxes; +import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.CustomizeTimeBoxEntities; import com.debatetimer.domain.member.Member; import jakarta.validation.Valid; import java.util.List; @@ -13,13 +14,19 @@ public record CustomizeTableCreateRequest( @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 CustomizeTable toTable(Member member) { return info.toTable(member); } - public CustomizeTimeBoxes toTimeBoxes(CustomizeTable customizeTable) { - return IntStream.range(0, table.size()) - .mapToObj(i -> table.get(i).toTimeBox(customizeTable, i + 1)) - .collect(Collectors.collectingAndThen(Collectors.toList(), CustomizeTimeBoxes::new)); + public List toTimeBoxList() { // TODO 메서드 네이밍 변경 toTimeBoxList() -> toTimeBoxes() + return table.stream() + .map(CustomizeTimeBoxCreateRequest::toDomain) + .toList(); } } diff --git a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java index 97397215..0507ad3b 100644 --- a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java +++ b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTimeBoxCreateRequest.java @@ -1,12 +1,17 @@ package com.debatetimer.dto.customize.request; +import com.debatetimer.domain.customize.Bell; import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.NormalTimeBox; import com.debatetimer.domain.customize.Stance; +import com.debatetimer.domain.customize.TimeBasedTimeBox; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import java.util.Collections; import java.util.List; import org.springframework.lang.Nullable; @@ -36,12 +41,28 @@ public record CustomizeTimeBoxCreateRequest( String speaker ) { - public CustomizeTimeBox toTimeBox(CustomizeTable customizeTable, int sequence) { + public CustomizeTimeBoxEntity toTimeBox(CustomizeTable customizeTable, int sequence) { if (boxType.isTimeBased()) { - return new CustomizeTimeBox(new CustomizeTableEntity(customizeTable), sequence, stance, speechType, + return new CustomizeTimeBoxEntity(new CustomizeTableEntity(customizeTable), sequence, stance, speechType, boxType, timePerTeam, timePerSpeaking, speaker); } - return new CustomizeTimeBox(new CustomizeTableEntity(customizeTable), sequence, stance, speechType, boxType, + return new CustomizeTimeBoxEntity(new CustomizeTableEntity(customizeTable), sequence, stance, speechType, boxType, time, speaker); } + + public CustomizeTimeBox toDomain() { + if (boxType.isTimeBased()) { + return new TimeBasedTimeBox(stance, speechType, speaker, timePerTeam, timePerSpeaking); + } + return new NormalTimeBox(stance, speechType, speaker, time, toBells(bell)); + } + + private List toBells(List bellRequests) { + if (bellRequests == null) { + return Collections.emptyList(); + } + return bellRequests.stream() + .map(BellRequest::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 ce2dae8c..0ec2b060 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/BellResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/BellResponse.java @@ -1,7 +1,13 @@ package com.debatetimer.dto.customize.response; +import com.debatetimer.domain.customize.Bell; + public record BellResponse( int time, int count ) { + + public BellResponse(Bell bell) { + this(bell.getTime(), bell.getCount()); + } } diff --git a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java index 587e61c5..906bc27e 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java @@ -1,15 +1,16 @@ package com.debatetimer.dto.customize.response; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBoxes; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.CustomizeTimeBoxEntities; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import java.util.List; public record CustomizeTableResponse(long id, CustomizeTableInfoResponse info, List table) { public CustomizeTableResponse( CustomizeTable customizeTable, - CustomizeTimeBoxes customizeTimeBoxes + CustomizeTimeBoxEntities customizeTimeBoxes ) { this(customizeTable.getId(), new CustomizeTableInfoResponse(customizeTable), toTimeBoxResponses(customizeTimeBoxes)); @@ -22,8 +23,24 @@ public CustomizeTableResponse( this(customizeTable.getId(), new CustomizeTableInfoResponse(customizeTable), timeBoxResponses); } - private static List toTimeBoxResponses(CustomizeTimeBoxes timeBoxes) { - List customizeTimeBoxes = timeBoxes.getTimeBoxes(); + private static List toTimeBoxResponses(CustomizeTimeBoxEntities timeBoxes) { + List customizeTimeBoxes = timeBoxes.getTimeBoxes(); + return customizeTimeBoxes + .stream() + .map(CustomizeTimeBoxResponse::new) + .toList(); + } + + public static CustomizeTableResponse ofDomain(CustomizeTable customizeTable, + List customizeTimeBoxes) { // TODO 정팩매 -> 생성자로 전환 + return new CustomizeTableResponse( + customizeTable.getId(), + new CustomizeTableInfoResponse(customizeTable), + toTimeBoxResponses(customizeTimeBoxes) + ); + } + + private static List toTimeBoxResponses(List customizeTimeBoxes) { return customizeTimeBoxes .stream() .map(CustomizeTimeBoxResponse::new) diff --git a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java index e62f17fb..ce90f96a 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java @@ -1,8 +1,11 @@ package com.debatetimer.dto.customize.response; +import com.debatetimer.domain.customize.Bell; import com.debatetimer.domain.customize.CustomizeBoxType; +import com.debatetimer.domain.customize.CustomizeTimeBox; import com.debatetimer.domain.customize.Stance; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; +import java.util.Collections; import java.util.List; public record CustomizeTimeBoxResponse( @@ -16,7 +19,7 @@ public record CustomizeTimeBoxResponse( String speaker ) { - public CustomizeTimeBoxResponse(CustomizeTimeBox customizeTimeBox) { + public CustomizeTimeBoxResponse(CustomizeTimeBoxEntity customizeTimeBox) { this( customizeTimeBox.getStance(), customizeTimeBox.getSpeechType(), @@ -29,7 +32,7 @@ public CustomizeTimeBoxResponse(CustomizeTimeBox customizeTimeBox) { ); } - public CustomizeTimeBoxResponse(CustomizeTimeBox customizeTimeBox, List bell) { + public CustomizeTimeBoxResponse(CustomizeTimeBoxEntity customizeTimeBox, List bell) { this( customizeTimeBox.getStance(), customizeTimeBox.getSpeechType(), @@ -42,10 +45,32 @@ public CustomizeTimeBoxResponse(CustomizeTimeBox customizeTimeBox, List toResponses(List bells) { + if (bells.isEmpty()) { + return null; + } + return bells.stream() + .map(BellResponse::new) + .toList(); + } } diff --git a/src/main/java/com/debatetimer/entity/customize/BellEntity.java b/src/main/java/com/debatetimer/entity/customize/BellEntity.java index 43d4df71..fefc80ab 100644 --- a/src/main/java/com/debatetimer/entity/customize/BellEntity.java +++ b/src/main/java/com/debatetimer/entity/customize/BellEntity.java @@ -31,13 +31,13 @@ public class BellEntity { @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "customize_time_box_id") - private CustomizeTimeBox customizeTimeBox; + private CustomizeTimeBoxEntity customizeTimeBox; @Column(name = "bell_time") private int time; private int count; - public BellEntity(CustomizeTimeBox customizeTimeBox, int time, int count) { + public BellEntity(CustomizeTimeBoxEntity customizeTimeBox, int time, int count) { validateTime(time); validateCount(count); diff --git a/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBox.java b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java similarity index 96% rename from src/main/java/com/debatetimer/entity/customize/CustomizeTimeBox.java rename to src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java index 64e85be6..04fe115e 100644 --- a/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBox.java +++ b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java @@ -13,16 +13,18 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +@Table(name = "customize_time_box") @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class CustomizeTimeBox { +public class CustomizeTimeBoxEntity { public static final int SPEECH_TYPE_MAX_LENGTH = 10; public static final int TIME_MULTIPLIER = 2; @@ -56,7 +58,7 @@ public class CustomizeTimeBox { private Integer timePerTeam; private Integer timePerSpeaking; - public CustomizeTimeBox( + public CustomizeTimeBoxEntity( CustomizeTableEntity customizeTableEntity, int sequence, Stance stance, @@ -80,7 +82,7 @@ public CustomizeTimeBox( this.boxType = boxType; } - public CustomizeTimeBox( + public CustomizeTimeBoxEntity( CustomizeTableEntity customizeTableEntity, int sequence, Stance stance, diff --git a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java index e0ac6cf3..59eb8cb9 100644 --- a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java +++ b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java @@ -1,10 +1,10 @@ package com.debatetimer.exception.errorcode; import com.debatetimer.domain.customize.Agenda; +import com.debatetimer.domain.customize.Bell; import com.debatetimer.domain.customize.TableName; import com.debatetimer.domain.customize.TeamName; -import com.debatetimer.entity.customize.BellEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import lombok.Getter; import org.springframework.http.HttpStatus; @@ -29,11 +29,11 @@ public enum ClientErrorCode implements ResponseErrorCode { INVALID_TIME_BASED_TIME(HttpStatus.BAD_REQUEST, "팀 발언 시간은 개인 발언 시간보다 길어야합니다"), INVALID_TIME_BOX_SPEECH_TYPE_LENGTH( HttpStatus.BAD_REQUEST, - "발언 유형 이름은 1자 이상 %d자 이하여야 합니다.".formatted(CustomizeTimeBox.SPEECH_TYPE_MAX_LENGTH) + "발언 유형 이름은 1자 이상 %d자 이하여야 합니다.".formatted(CustomizeTimeBoxEntity.SPEECH_TYPE_MAX_LENGTH) ), INVALID_TIME_BOX_SPEAKER_LENGTH( HttpStatus.BAD_REQUEST, - "발언자 이름은 1자 이상 %d자 이하여야 합니다.".formatted(CustomizeTimeBox.SPEAKER_MAX_LENGTH) + "발언자 이름은 1자 이상 %d자 이하여야 합니다.".formatted(CustomizeTimeBoxEntity.SPEAKER_MAX_LENGTH) ), INVALID_TEAM_NAME_LENGTH( HttpStatus.BAD_REQUEST, @@ -68,7 +68,7 @@ public enum ClientErrorCode implements ResponseErrorCode { FILE_UPLOAD_ERROR(HttpStatus.BAD_REQUEST, "파일 업로드에 실패했습니다."), INVALID_BELL_TIME(HttpStatus.BAD_REQUEST, "벨 시간은 0 이상의 정수여야 합니다."), - INVALID_BELL_COUNT(HttpStatus.BAD_REQUEST, "벨 카운트는 1 이상 %d 이하의 정수여야 합니다.".formatted(BellEntity.MAX_BELL_COUNT)), + INVALID_BELL_COUNT(HttpStatus.BAD_REQUEST, "벨 카운트는 1 이상 %d 이하의 정수여야 합니다.".formatted(Bell.MAX_BELL_COUNT)), ; private final HttpStatus status; diff --git a/src/main/java/com/debatetimer/repository/customize/BellRepository.java b/src/main/java/com/debatetimer/repository/customize/BellRepository.java index 78569835..8dd571d6 100644 --- a/src/main/java/com/debatetimer/repository/customize/BellRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/BellRepository.java @@ -1,7 +1,7 @@ package com.debatetimer.repository.customize; import com.debatetimer.entity.customize.BellEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import java.util.List; import org.springframework.data.repository.Repository; @@ -9,9 +9,9 @@ public interface BellRepository extends Repository { BellEntity save(BellEntity bell); - List findByCustomizeTimeBox(CustomizeTimeBox customizeTimeBox); + List findByCustomizeTimeBox(CustomizeTimeBoxEntity customizeTimeBox); - void deleteAllByCustomizeTimeBoxIn(List customizeTimeBoxes); + void deleteAllByCustomizeTimeBoxIn(List customizeTimeBoxes); - List findAllByCustomizeTimeBoxIn(List timeBoxes); + List findAllByCustomizeTimeBoxIn(List timeBoxes); } diff --git a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java index aeae7418..298ea6c4 100644 --- a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java @@ -1,33 +1,33 @@ package com.debatetimer.repository.customize; -import com.debatetimer.domain.customize.CustomizeTimeBoxes; +import com.debatetimer.domain.customize.CustomizeTimeBoxEntities; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import java.util.List; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; import org.springframework.transaction.annotation.Transactional; -public interface CustomizeTimeBoxRepository extends Repository { +public interface CustomizeTimeBoxRepository extends Repository { - CustomizeTimeBox save(CustomizeTimeBox timeBox); + CustomizeTimeBoxEntity save(CustomizeTimeBoxEntity timeBox); @Transactional - default List saveAll(List timeBoxes) { + default List saveAll(List timeBoxes) { return timeBoxes.stream() .map(this::save) .toList(); } - List findAllByCustomizeTable(CustomizeTableEntity table); + List findAllByCustomizeTable(CustomizeTableEntity table); - default CustomizeTimeBoxes findTableTimeBoxes(CustomizeTableEntity table) { - List timeBoxes = findAllByCustomizeTable(table); - return new CustomizeTimeBoxes(timeBoxes); + default CustomizeTimeBoxEntities findTableTimeBoxes(CustomizeTableEntity table) { + List timeBoxes = findAllByCustomizeTable(table); + return new CustomizeTimeBoxEntities(timeBoxes); } - @Query("DELETE FROM CustomizeTimeBox ctb WHERE ctb.customizeTable = :table") + @Query("DELETE FROM CustomizeTimeBoxEntity ctb WHERE ctb.customizeTable = :table") @Modifying(clearAutomatically = true, flushAutomatically = true) void deleteAllByTable(CustomizeTableEntity table); } diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeService.java b/src/main/java/com/debatetimer/service/customize/CustomizeService.java index 7281a77a..cc9e7fee 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeService.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeService.java @@ -1,11 +1,12 @@ package com.debatetimer.service.customize; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBoxes; +import com.debatetimer.domain.customize.CustomizeTimeBoxEntities; 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.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import java.util.List; @@ -25,14 +26,14 @@ public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateReques CustomizeTable table = tableCreateRequest.toTable(member); CustomizeTableEntity savedTable = tableRepository.save(new CustomizeTableEntity(table)); - CustomizeTimeBoxes savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, savedTable.toDomain()); + CustomizeTimeBoxEntities savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, savedTable.toDomain()); return new CustomizeTableResponse(savedTable.toDomain(), savedCustomizeTimeBoxes); } @Transactional(readOnly = true) public CustomizeTableResponse findTable(long tableId, Member member) { CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); - CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); + CustomizeTimeBoxEntities timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxes); } @@ -47,14 +48,14 @@ public CustomizeTableResponse updateTable( existingTable.updateTable(renewedTable); timeBoxRepository.deleteAllByTable(existingTable); - CustomizeTimeBoxes savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, existingTable.toDomain()); + CustomizeTimeBoxEntities savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, existingTable.toDomain()); return new CustomizeTableResponse(existingTable.toDomain(), savedCustomizeTimeBoxes); } @Transactional public CustomizeTableResponse updateUsedAt(long tableId, Member member) { CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); - CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); + CustomizeTimeBoxEntities timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); tableEntity.updateUsedAt(); return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxes); @@ -67,13 +68,13 @@ public void deleteTable(long tableId, Member member) { tableRepository.delete(table); } - private CustomizeTimeBoxes saveTimeBoxes( + private CustomizeTimeBoxEntities saveTimeBoxes( CustomizeTableCreateRequest tableCreateRequest, CustomizeTable table ) { - CustomizeTimeBoxes customizeTimeBoxes = tableCreateRequest.toTimeBoxes(table); - List savedTimeBoxes = timeBoxRepository.saveAll( + CustomizeTimeBoxEntities customizeTimeBoxes = tableCreateRequest.toTimeBoxes(table); + List savedTimeBoxes = timeBoxRepository.saveAll( customizeTimeBoxes.getTimeBoxes()); - return new CustomizeTimeBoxes(savedTimeBoxes); + return new CustomizeTimeBoxEntities(savedTimeBoxes); } } diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java b/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java index 423948a7..f4e6cf48 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java @@ -1,7 +1,7 @@ package com.debatetimer.service.customize; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBoxes; +import com.debatetimer.domain.customize.CustomizeTimeBoxEntities; import com.debatetimer.domain.member.Member; import com.debatetimer.dto.customize.request.BellRequest; import com.debatetimer.dto.customize.request.CustomizeTableCreateRequest; @@ -11,7 +11,7 @@ import com.debatetimer.dto.customize.response.CustomizeTimeBoxResponse; import com.debatetimer.entity.customize.BellEntity; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.repository.customize.BellRepository; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; @@ -40,7 +40,7 @@ public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateReques @Transactional(readOnly = true) public CustomizeTableResponse findTable(long tableId, Member member) { CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); - CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); + CustomizeTimeBoxEntities timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); List timeBoxResponses = timeBoxes.getTimeBoxes() .stream() .map(this::getTimeBoxResponse) @@ -66,7 +66,7 @@ public CustomizeTableResponse updateTable( @Transactional public CustomizeTableResponse updateUsedAt(long tableId, Member member) { CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); - CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); + CustomizeTimeBoxEntities timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); tableEntity.updateUsedAt(); List timeBoxResponses = timeBoxes.getTimeBoxes() .stream() @@ -100,11 +100,11 @@ private CustomizeTimeBoxResponse createTimeBoxResponse( CustomizeTable table, int sequence ) { - CustomizeTimeBox savedTimeBox = timeBoxRepository.save(request.toTimeBox(table, sequence)); + CustomizeTimeBoxEntity savedTimeBox = timeBoxRepository.save(request.toTimeBox(table, sequence)); return createTimeBoxResponse(request.bell(), savedTimeBox); } - private CustomizeTimeBoxResponse createTimeBoxResponse(List bellRequests, CustomizeTimeBox timeBox) { + private CustomizeTimeBoxResponse createTimeBoxResponse(List bellRequests, CustomizeTimeBoxEntity timeBox) { if (timeBox.getBoxType().isTimeBased()) { return new CustomizeTimeBoxResponse(timeBox, null); } @@ -118,7 +118,7 @@ private CustomizeTimeBoxResponse createTimeBoxResponse(List bellReq return new CustomizeTimeBoxResponse(timeBox, bellResponses); } - private CustomizeTimeBoxResponse getTimeBoxResponse(CustomizeTimeBox timeBox) { + private CustomizeTimeBoxResponse getTimeBoxResponse(CustomizeTimeBoxEntity timeBox) { if (timeBox.getBoxType().isTimeBased()) { return new CustomizeTimeBoxResponse(timeBox, null); } @@ -130,7 +130,7 @@ private CustomizeTimeBoxResponse getTimeBoxResponse(CustomizeTimeBox timeBox) { return new CustomizeTimeBoxResponse(timeBox, bellResponses); } - private void deleteBell(CustomizeTimeBoxes savedCustomizeTimeBoxes) { + private void deleteBell(CustomizeTimeBoxEntities savedCustomizeTimeBoxes) { bellRepository.deleteAllByCustomizeTimeBoxIn(savedCustomizeTimeBoxes.getTimeBoxes()); } } diff --git a/src/test/java/com/debatetimer/domain/customize/BellTest.java b/src/test/java/com/debatetimer/domain/customize/BellTest.java new file mode 100644 index 00000000..a99f40eb --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/BellTest.java @@ -0,0 +1,46 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThatCode; +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; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +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)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_BELL_COUNT.getMessage()); + } + + @ValueSource(ints = {1, Bell.MAX_BELL_COUNT}) + @ParameterizedTest + void 벨_횟수는_정해진_횟수_이내여야_한다(int count) { + assertThatCode(() -> new Bell(1, count)) + .doesNotThrowAnyException(); + } + } +} diff --git a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntitiesTest.java similarity index 66% rename from src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java rename to src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntitiesTest.java index 57c3122a..6db5cee9 100644 --- a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java +++ b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntitiesTest.java @@ -4,7 +4,7 @@ import com.debatetimer.domain.member.Member; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; @@ -12,7 +12,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -class CustomizeTimeBoxesTest { +class CustomizeTimeBoxEntitiesTest { @Nested class SortedBySequence { @@ -23,13 +23,13 @@ class SortedBySequence { CustomizeTable table = new CustomizeTable(member, "토론 테이블", "주제", "찬성", "반대", true, true, LocalDateTime.now()); CustomizeTableEntity testTable = new CustomizeTableEntity(table); - CustomizeTimeBox firstBox = new CustomizeTimeBox(testTable, 1, Stance.PROS, "입론", + CustomizeTimeBoxEntity firstBox = new CustomizeTimeBoxEntity(testTable, 1, Stance.PROS, "입론", CustomizeBoxType.NORMAL, 300, "콜리"); - CustomizeTimeBox secondBox = new CustomizeTimeBox(testTable, 2, Stance.PROS, "입론", + CustomizeTimeBoxEntity secondBox = new CustomizeTimeBoxEntity(testTable, 2, Stance.PROS, "입론", CustomizeBoxType.NORMAL, 300, "콜리2"); - List timeBoxes = new ArrayList<>(Arrays.asList(secondBox, firstBox)); + List timeBoxes = new ArrayList<>(Arrays.asList(secondBox, firstBox)); - CustomizeTimeBoxes actual = new CustomizeTimeBoxes(timeBoxes); + CustomizeTimeBoxEntities actual = new CustomizeTimeBoxEntities(timeBoxes); assertThat(actual.getTimeBoxes()).containsExactly(firstBox, secondBox); } diff --git a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxTest.java similarity index 74% rename from src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java rename to src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxTest.java index be331821..cab0b3d7 100644 --- a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxDomainTest.java +++ b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxTest.java @@ -5,27 +5,29 @@ import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; +import java.util.Collections; +import java.util.List; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; -class CustomizeTimeBoxDomainTest { +class CustomizeTimeBoxTest { @Nested class ValidateStance { @Test void 발언_입장은_비어있을_수_없다() { - assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(null, "비토", "발언자")) + assertThatThrownBy(() -> new InheritedCustomizeTimeBox(null, "비토", "발언자")) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_STANCE.getMessage()); } @Test void 발언_입장은_유효한_값이어야_한다() { - assertThatCode(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, "비토", "발언자")) + assertThatCode(() -> new InheritedCustomizeTimeBox(Stance.PROS, "비토", "발언자")) .doesNotThrowAnyException(); } } @@ -35,9 +37,9 @@ class ValidateSpeechType { @Test void 발언_종류는_특정_글자를_초과할_수_없다() { - String speechType = "a".repeat(CustomizeTimeBoxDomain.SPEECH_TYPE_MAX_LENGTH + 1); + String speechType = "a".repeat(CustomizeTimeBox.SPEECH_TYPE_MAX_LENGTH + 1); - assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, speechType, "비토")) + assertThatThrownBy(() -> new InheritedCustomizeTimeBox(Stance.PROS, speechType, "비토")) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH.getMessage()); } @@ -46,16 +48,16 @@ class ValidateSpeechType { @NullAndEmptySource @ValueSource(strings = {" ", "\n\t"}) void 발언_종류는_비어있을_수_없다(String emptySpeechType) { - assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, emptySpeechType, "비토")) + assertThatThrownBy(() -> new InheritedCustomizeTimeBox(Stance.PROS, emptySpeechType, "비토")) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH.getMessage()); } @Test void 발언_종류는_특정_글자_이내이어야_한다() { - String speechType = "a".repeat(CustomizeTimeBoxDomain.SPEECH_TYPE_MAX_LENGTH); + String speechType = "a".repeat(CustomizeTimeBox.SPEECH_TYPE_MAX_LENGTH); - assertThatCode(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, speechType, "비토")) + assertThatCode(() -> new InheritedCustomizeTimeBox(Stance.PROS, speechType, "비토")) .doesNotThrowAnyException(); } } @@ -65,23 +67,23 @@ class ValidateSpeaker { @Test void 발언자_이름은_특정_글자를_초과할_수_없다() { - String speaker = "a".repeat(CustomizeTimeBoxDomain.SPEAKER_MAX_LENGTH + 1); + String speaker = "a".repeat(CustomizeTimeBox.SPEAKER_MAX_LENGTH + 1); - assertThatThrownBy(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, "비토", speaker)) + assertThatThrownBy(() -> new InheritedCustomizeTimeBox(Stance.PROS, "비토", speaker)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH.getMessage()); } @Test void 발언자_이름은_비어있을_수_있다() { - assertThatCode(() -> new InheritedCustomizeTimeBoxDomain(Stance.PROS, "비토", null)) + assertThatCode(() -> new InheritedCustomizeTimeBox(Stance.PROS, "비토", null)) .doesNotThrowAnyException(); } } - static class InheritedCustomizeTimeBoxDomain extends CustomizeTimeBoxDomain { + static class InheritedCustomizeTimeBox extends CustomizeTimeBox { - protected InheritedCustomizeTimeBoxDomain(Stance stance, String speechType, String speaker) { + protected InheritedCustomizeTimeBox(Stance stance, String speechType, String speaker) { super(stance, speechType, speaker); } @@ -109,5 +111,10 @@ public Integer getTimePerTeam() { public Integer getTimePerSpeaking() { return 0; } + + @Override + public List getBells() { + return Collections.emptyList(); + } } } diff --git a/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxTest.java similarity index 73% rename from src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java rename to src/test/java/com/debatetimer/domain/customize/NormalTimeBoxTest.java index 810ea4ce..c70bf53a 100644 --- a/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxDomainTest.java +++ b/src/test/java/com/debatetimer/domain/customize/NormalTimeBoxTest.java @@ -5,10 +5,11 @@ import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; +import java.util.Collections; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -class NormalTimeBoxDomainTest { +class NormalTimeBoxTest { @Nested class ValidateTime { @@ -17,7 +18,7 @@ class ValidateTime { void 시간은_0보다_커야_한다() { Integer time = 0; - assertThatThrownBy(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) + assertThatThrownBy(() -> new NormalTimeBox(Stance.PROS, "비토", null, time, Collections.emptyList())) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); } @@ -26,7 +27,7 @@ class ValidateTime { void 시간은_비어있지_않아야_한다() { Integer time = null; - assertThatThrownBy(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) + assertThatThrownBy(() -> new NormalTimeBox(Stance.PROS, "비토", null, time, Collections.emptyList())) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); } @@ -35,7 +36,7 @@ class ValidateTime { void 시간은_양수여야_한다() { Integer time = 1; - assertThatCode(() -> new NormalTimeBoxDomain(Stance.PROS, "비토", null, time)) + assertThatCode(() -> new NormalTimeBox(Stance.PROS, "비토", null, time, Collections.emptyList())) .doesNotThrowAnyException(); } } diff --git a/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java b/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxTest.java similarity index 75% rename from src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java rename to src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxTest.java index 484d0e1a..41d27641 100644 --- a/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxDomainTest.java +++ b/src/test/java/com/debatetimer/domain/customize/TimeBasedTimeBoxTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -class TimeBasedTimeBoxDomainTest { +class TimeBasedTimeBoxTest { @Nested class ValidateStance { @@ -18,7 +18,7 @@ class ValidateStance { Stance stance = Stance.PROS; assertThatThrownBy( - () -> new TimeBasedTimeBoxDomain(stance, "자유발언", "비토", 120, 60)) + () -> new TimeBasedTimeBox(stance, "자유발언", "비토", 120, 60)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_STANCE.getMessage()); } @@ -28,7 +28,7 @@ class ValidateStance { Stance stance = Stance.NEUTRAL; assertThatCode( - () -> new TimeBasedTimeBoxDomain(stance, "자유발언", "비토", 120, 60)) + () -> new TimeBasedTimeBox(stance, "자유발언", "비토", 120, 60)) .doesNotThrowAnyException(); } } @@ -42,7 +42,7 @@ class ValidateTimes { int timePerSpeaking = 1; assertThatThrownBy( - () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + () -> new TimeBasedTimeBox(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); } @@ -53,7 +53,7 @@ class ValidateTimes { int timePerSpeaking = 1; assertThatThrownBy( - () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + () -> new TimeBasedTimeBox(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); } @@ -64,7 +64,7 @@ class ValidateTimes { int timePerTeam = 1; assertThatThrownBy( - () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + () -> new TimeBasedTimeBox(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); } @@ -75,7 +75,7 @@ class ValidateTimes { int timePerTeam = 1; assertThatCode( - () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + () -> new TimeBasedTimeBox(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) .doesNotThrowAnyException(); } @@ -85,7 +85,7 @@ class ValidateTimes { int timePerSpeaking = timePerTeam + 1; assertThatThrownBy( - () -> new TimeBasedTimeBoxDomain(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) + () -> new TimeBasedTimeBox(Stance.NEUTRAL, "자유발언", "비토", timePerTeam, timePerSpeaking)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BASED_TIME.getMessage()); } diff --git a/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxTest.java b/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntityTest.java similarity index 79% rename from src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxTest.java rename to src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntityTest.java index e535d2e3..3db1b1d4 100644 --- a/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxTest.java +++ b/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntityTest.java @@ -14,7 +14,7 @@ import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; -class CustomizeTimeBoxTest { +class CustomizeTimeBoxEntityTest { @Nested class ValidateSequence { @@ -25,7 +25,7 @@ class ValidateSequence { CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.TIME_BASED; - assertThatThrownBy(() -> new CustomizeTimeBox(table, sequence, Stance.NEUTRAL, "자유토론", + assertThatThrownBy(() -> new CustomizeTimeBoxEntity(table, sequence, Stance.NEUTRAL, "자유토론", customizeBoxType, 120, 60, "발언자")) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SEQUENCE.getMessage()); @@ -42,7 +42,7 @@ class ValidateTime { CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; assertThatThrownBy( - () -> new CustomizeTimeBox(table, 1, Stance.CONS, "자유토론", + () -> new CustomizeTimeBoxEntity(table, 1, Stance.CONS, "자유토론", customizeBoxType, time, 60, "발언자")) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_TIME.getMessage()); @@ -56,9 +56,9 @@ class ValidateSpeaker { void 발언자_이름은_일정길이_이내로_허용된다() { CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; - String speaker = "k".repeat(CustomizeTimeBox.SPEAKER_MAX_LENGTH + 1); + String speaker = "k".repeat(CustomizeTimeBoxEntity.SPEAKER_MAX_LENGTH + 1); - assertThatThrownBy(() -> new CustomizeTimeBox(table, 1, Stance.CONS, "입론", + assertThatThrownBy(() -> new CustomizeTimeBoxEntity(table, 1, Stance.CONS, "입론", customizeBoxType, 120, speaker)) .isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEAKER_LENGTH.getMessage()); @@ -70,7 +70,7 @@ class ValidateSpeaker { CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; - assertThatCode(() -> new CustomizeTimeBox(table, 1, Stance.CONS, "입론", + assertThatCode(() -> new CustomizeTimeBoxEntity(table, 1, Stance.CONS, "입론", customizeBoxType, 120, speaker)) .doesNotThrowAnyException(); } @@ -81,7 +81,7 @@ class ValidateSpeaker { CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.NORMAL; - CustomizeTimeBox timeBox = new CustomizeTimeBox(table, 1, Stance.CONS, "입론", + CustomizeTimeBoxEntity timeBox = new CustomizeTimeBoxEntity(table, 1, Stance.CONS, "입론", customizeBoxType, 120, speaker); assertThat(timeBox.getSpeaker()).isNull(); @@ -96,7 +96,7 @@ class ValidateCustomizeTime { CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.TIME_BASED; - assertThatCode(() -> new CustomizeTimeBox(table, 1, Stance.NEUTRAL, "자유토론", + assertThatCode(() -> new CustomizeTimeBoxEntity(table, 1, Stance.NEUTRAL, "자유토론", customizeBoxType, 120, 60, "발언자") ).doesNotThrowAnyException(); } @@ -106,7 +106,7 @@ class ValidateCustomizeTime { CustomizeTableEntity table = new CustomizeTableEntity(); CustomizeBoxType customizeBoxType = CustomizeBoxType.TIME_BASED; - assertThatThrownBy(() -> new CustomizeTimeBox(table, 1, Stance.NEUTRAL, "자유토론", customizeBoxType, 10, + assertThatThrownBy(() -> new CustomizeTimeBoxEntity(table, 1, Stance.NEUTRAL, "자유토론", customizeBoxType, 10, "발언자")).isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_FORMAT.getMessage()); } @@ -117,7 +117,7 @@ class ValidateCustomizeTime { CustomizeBoxType notTimeBasedBoxType = CustomizeBoxType.NORMAL; assertThatThrownBy( - () -> new CustomizeTimeBox(table, 1, Stance.NEUTRAL, "자유토론", notTimeBasedBoxType, 120, 60, + () -> new CustomizeTimeBoxEntity(table, 1, Stance.NEUTRAL, "자유토론", notTimeBasedBoxType, 120, 60, "발언자")).isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_FORMAT.getMessage()); } @@ -128,7 +128,7 @@ class ValidateCustomizeTime { Integer timePerTeam = 60; Integer timePerSpeaking = null; - assertThatCode(() -> new CustomizeTimeBox(table, 1, Stance.NEUTRAL, "자유토론", CustomizeBoxType.TIME_BASED, + assertThatCode(() -> new CustomizeTimeBoxEntity(table, 1, Stance.NEUTRAL, "자유토론", CustomizeBoxType.TIME_BASED, timePerTeam, timePerSpeaking, "발언자")).doesNotThrowAnyException(); } @@ -138,7 +138,7 @@ class ValidateCustomizeTime { int timePerTeam = 60; int timePerSpeaking = 59; - assertThatCode(() -> new CustomizeTimeBox(table, 1, Stance.NEUTRAL, "자유토론", CustomizeBoxType.TIME_BASED, + assertThatCode(() -> new CustomizeTimeBoxEntity(table, 1, Stance.NEUTRAL, "자유토론", CustomizeBoxType.TIME_BASED, timePerTeam, timePerSpeaking, "발언자")).doesNotThrowAnyException(); } @@ -148,7 +148,7 @@ class ValidateCustomizeTime { int timePerTeam = 60; int timePerSpeaking = 61; - assertThatThrownBy(() -> new CustomizeTimeBox(table, 1, Stance.NEUTRAL, "자유토론", CustomizeBoxType.TIME_BASED, + assertThatThrownBy(() -> new CustomizeTimeBoxEntity(table, 1, Stance.NEUTRAL, "자유토론", CustomizeBoxType.TIME_BASED, timePerTeam, timePerSpeaking, "발언자")).isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BASED_TIME.getMessage()); } @@ -156,10 +156,10 @@ class ValidateCustomizeTime { @Test void 발언_유형의_길이는_일정_범위_이내여야_한다() { CustomizeTableEntity table = new CustomizeTableEntity(); - String longSpeechType = "s".repeat(CustomizeTimeBox.SPEECH_TYPE_MAX_LENGTH + 1); + String longSpeechType = "s".repeat(CustomizeTimeBoxEntity.SPEECH_TYPE_MAX_LENGTH + 1); assertThatThrownBy( - () -> new CustomizeTimeBox(table, 1, Stance.NEUTRAL, longSpeechType, CustomizeBoxType.TIME_BASED, + () -> new CustomizeTimeBoxEntity(table, 1, Stance.NEUTRAL, longSpeechType, CustomizeBoxType.TIME_BASED, 120, 60, "발언자")).isInstanceOf(DTClientErrorException.class) .hasMessage(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH.getMessage()); } @@ -173,10 +173,10 @@ class getTime { int timePerTeam = 300; int timePerSpeaking = 120; CustomizeTableEntity table = new CustomizeTableEntity(); - CustomizeTimeBox timeBasedTimeBox = new CustomizeTimeBox(table, 1, Stance.CONS, "자유 토론", + CustomizeTimeBoxEntity timeBasedTimeBox = new CustomizeTimeBoxEntity(table, 1, Stance.CONS, "자유 토론", CustomizeBoxType.TIME_BASED, timePerTeam, timePerSpeaking, "콜리"); - assertThat(timeBasedTimeBox.getTime()).isEqualTo(timePerTeam * CustomizeTimeBox.TIME_MULTIPLIER); + assertThat(timeBasedTimeBox.getTime()).isEqualTo(timePerTeam * CustomizeTimeBoxEntity.TIME_MULTIPLIER); } } } diff --git a/src/test/java/com/debatetimer/fixture/BellGenerator.java b/src/test/java/com/debatetimer/fixture/BellGenerator.java index b9698fd3..1c2392ce 100644 --- a/src/test/java/com/debatetimer/fixture/BellGenerator.java +++ b/src/test/java/com/debatetimer/fixture/BellGenerator.java @@ -1,7 +1,7 @@ package com.debatetimer.fixture; import com.debatetimer.entity.customize.BellEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.repository.customize.BellRepository; import org.springframework.stereotype.Component; @@ -14,7 +14,7 @@ public BellGenerator(BellRepository bellRepository) { this.bellRepository = bellRepository; } - public BellEntity generate(CustomizeTimeBox timeBox, int time, int count) { + public BellEntity generate(CustomizeTimeBoxEntity timeBox, int time, int count) { BellEntity bell = new BellEntity(timeBox, time, count); return bellRepository.save(bell); } diff --git a/src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java b/src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java index d7ab3acc..e068269d 100644 --- a/src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java +++ b/src/test/java/com/debatetimer/fixture/CustomizeTimeBoxGenerator.java @@ -3,7 +3,7 @@ import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.customize.Stance; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import org.springframework.stereotype.Component; @@ -16,8 +16,8 @@ public CustomizeTimeBoxGenerator(CustomizeTimeBoxRepository customizeTimeBoxRepo this.customizeTimeBoxRepository = customizeTimeBoxRepository; } - public CustomizeTimeBox generate(CustomizeTableEntity testTable, CustomizeBoxType boxType, int sequence) { - CustomizeTimeBox timeBox = new CustomizeTimeBox( + public CustomizeTimeBoxEntity generate(CustomizeTableEntity testTable, CustomizeBoxType boxType, int sequence) { + CustomizeTimeBoxEntity timeBox = new CustomizeTimeBoxEntity( testTable, sequence, Stance.PROS, @@ -29,9 +29,9 @@ public CustomizeTimeBox generate(CustomizeTableEntity testTable, CustomizeBoxTyp return customizeTimeBoxRepository.save(timeBox); } - public CustomizeTimeBox generateNotExistSpeaker(CustomizeTableEntity testTable, CustomizeBoxType boxType, - int sequence) { - CustomizeTimeBox timeBox = new CustomizeTimeBox( + public CustomizeTimeBoxEntity generateNotExistSpeaker(CustomizeTableEntity testTable, CustomizeBoxType boxType, + int sequence) { + CustomizeTimeBoxEntity timeBox = new CustomizeTimeBoxEntity( testTable, sequence, Stance.PROS, diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java index 6b5f5df0..c0d42904 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java @@ -6,7 +6,7 @@ import com.debatetimer.domain.customize.CustomizeBoxType; import com.debatetimer.domain.member.Member; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.repository.BaseRepositoryTest; import java.util.List; import org.junit.jupiter.api.Nested; @@ -27,12 +27,12 @@ class FindAllByCustomizeTableEntity { Member bito = memberGenerator.generate("default2@gmail.com"); CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); CustomizeTableEntity bitoTable = customizeTableGenerator.generate(bito); - CustomizeTimeBox chanBox1 = customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); - CustomizeTimeBox chanBox2 = customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); + 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); - List foundBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(chanTable); + List foundBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(chanTable); assertThat(foundBoxes).containsExactly(chanBox1, chanBox2); } @@ -50,7 +50,7 @@ class DeleteAllByTable { customizeTimeBoxRepository.deleteAllByTable(chanTable); - List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(chanTable); + List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(chanTable); assertThat(timeBoxes).isEmpty(); } @@ -65,7 +65,7 @@ class DeleteAllByTable { customizeTimeBoxRepository.deleteAllByTable(deletedTable); - List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(filledTable); + List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(filledTable); assertThat(timeBoxes).hasSize(2); } diff --git a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java index 0223170a..410c0042 100644 --- a/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java +++ b/src/test/java/com/debatetimer/service/customize/CustomizeServiceTest.java @@ -13,7 +13,7 @@ import com.debatetimer.dto.customize.request.CustomizeTimeBoxCreateRequest; import com.debatetimer.dto.customize.response.CustomizeTableResponse; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import com.debatetimer.service.BaseServiceTest; @@ -48,7 +48,7 @@ class Save { CustomizeTableResponse savedTableResponse = customizeService.save(customizeTableCreateRequest, chan); CustomizeTableEntity foundTable = customizeTableRepository.getByIdAndMember(savedTableResponse.id(), chan); - List foundTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(foundTable); + List foundTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(foundTable); assertAll( () -> assertThat(foundTable.getName()).isEqualTo(customizeTableCreateRequest.info().name()), @@ -109,7 +109,7 @@ class UpdateTable { customizeService.updateTable(renewTableRequest, chanTable.getId(), chan); CustomizeTableEntity updatedTable = customizeTableRepository.getByIdAndMember(chanTable.getId(), chan); - List updatedTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(updatedTable); + List updatedTimeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(updatedTable); assertAll( () -> assertThat(updatedTable.getId()).isEqualTo(chanTable.getId()), @@ -205,7 +205,7 @@ class DeleteTable { customizeService.deleteTable(chanTable.getId(), chan); Optional foundTable = customizeTableRepository.findById(chanTable.getId()); - List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( + List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( chanTable); assertAll( diff --git a/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java b/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java index 50811628..252be593 100644 --- a/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java +++ b/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java @@ -14,7 +14,7 @@ import com.debatetimer.dto.customize.response.CustomizeTableResponse; import com.debatetimer.entity.customize.BellEntity; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBox; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import com.debatetimer.service.BaseServiceTest; @@ -49,7 +49,8 @@ class Save { 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( @@ -67,8 +68,8 @@ class FindTable { void 사용자_지정_토론_테이블을_조회한다() { Member chan = memberGenerator.generate("default@gmail.com"); CustomizeTableEntity chanTable = customizeTableGenerator.generate(chan); - CustomizeTimeBox customizeTimeBox = customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, - 1); + CustomizeTimeBoxEntity customizeTimeBox = customizeTimeBoxGenerator.generate( + chanTable, CustomizeBoxType.NORMAL, 1); customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); bellGenerator.generate(customizeTimeBox, 1, 1); bellGenerator.generate(customizeTimeBox, 1, 2); @@ -117,7 +118,8 @@ class UpdateTable { 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( @@ -215,7 +217,7 @@ class DeleteTable { customizeService.deleteTable(chanTable.getId(), chan); Optional foundTable = customizeTableRepository.findById(chanTable.getId()); - List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( + List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable( chanTable); List bells = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxes); From cbdcbd3773fa5af543c513900576078274cbb963 Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:12:26 +0900 Subject: [PATCH 20/27] =?UTF-8?q?[REFACTOR]=20=ED=83=80=EC=9E=84=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EB=8F=84=EB=A9=94=EC=9D=B8=20-=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=B6=84=EB=A6=AC=20/=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?(#201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customize/CustomizeTimeBoxEntities.java | 21 ---- .../request/CustomizeTableCreateRequest.java | 2 +- .../response/CustomizeTableResponse.java | 19 +-- .../response/CustomizeTimeBoxResponse.java | 14 --- .../entity/customize/BellEntity.java | 15 +++ .../customize/CustomizeTimeBoxEntities.java | 46 ++++++++ .../customize/CustomizeTimeBoxEntity.java | 26 ++++- .../repository/customize/BellRepository.java | 8 +- .../customize/CustomizeTimeBoxRepository.java | 6 +- .../service/customize/CustomizeService.java | 6 +- .../service/customize/CustomizeServiceV2.java | 108 ++++++------------ .../CustomizeTimeBoxEntitiesTest.java | 7 +- .../repository/BaseRepositoryTest.java | 7 +- .../customize/BellRepositoryTest.java | 47 ++++++++ .../CustomizeTimeBoxRepositoryTest.java | 6 +- .../customize/CustomizeServiceV2Test.java | 2 +- 16 files changed, 199 insertions(+), 141 deletions(-) delete mode 100644 src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntities.java create mode 100644 src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntities.java rename src/test/java/com/debatetimer/{domain => entity}/customize/CustomizeTimeBoxEntitiesTest.java (87%) create mode 100644 src/test/java/com/debatetimer/repository/customize/BellRepositoryTest.java diff --git a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntities.java b/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntities.java deleted file mode 100644 index 442845ac..00000000 --- a/src/main/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntities.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.debatetimer.domain.customize; - -import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; -import java.util.Comparator; -import java.util.List; -import lombok.Getter; - -@Getter -public class CustomizeTimeBoxEntities { - - private static final Comparator TIME_BOX_COMPARATOR = Comparator - .comparing(CustomizeTimeBoxEntity::getSequence); - - private final List timeBoxes; - - public CustomizeTimeBoxEntities(List timeBoxes) { - this.timeBoxes = timeBoxes.stream() - .sorted(TIME_BOX_COMPARATOR) - .toList(); - } -} 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 f4f9baf1..8dc05809 100644 --- a/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableCreateRequest.java +++ b/src/main/java/com/debatetimer/dto/customize/request/CustomizeTableCreateRequest.java @@ -2,7 +2,7 @@ import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.customize.CustomizeTimeBox; -import com.debatetimer.domain.customize.CustomizeTimeBoxEntities; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; import com.debatetimer.domain.member.Member; import jakarta.validation.Valid; import java.util.List; diff --git a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java index 906bc27e..2309b490 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java @@ -2,7 +2,7 @@ import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.customize.CustomizeTimeBox; -import com.debatetimer.domain.customize.CustomizeTimeBoxEntities; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import java.util.List; @@ -16,13 +16,6 @@ public CustomizeTableResponse( toTimeBoxResponses(customizeTimeBoxes)); } - public CustomizeTableResponse( - CustomizeTable customizeTable, - List timeBoxResponses - ) { - this(customizeTable.getId(), new CustomizeTableInfoResponse(customizeTable), timeBoxResponses); - } - private static List toTimeBoxResponses(CustomizeTimeBoxEntities timeBoxes) { List customizeTimeBoxes = timeBoxes.getTimeBoxes(); return customizeTimeBoxes @@ -31,13 +24,11 @@ private static List toTimeBoxResponses(CustomizeTimeBo .toList(); } - public static CustomizeTableResponse ofDomain(CustomizeTable customizeTable, - List customizeTimeBoxes) { // TODO 정팩매 -> 생성자로 전환 - return new CustomizeTableResponse( - customizeTable.getId(), + public CustomizeTableResponse(CustomizeTable customizeTable, + List customizeTimeBoxes) { + this(customizeTable.getId(), new CustomizeTableInfoResponse(customizeTable), - toTimeBoxResponses(customizeTimeBoxes) - ); + toTimeBoxResponses(customizeTimeBoxes)); } private static List toTimeBoxResponses(List customizeTimeBoxes) { diff --git a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java index ce90f96a..5bb6bb7a 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTimeBoxResponse.java @@ -5,7 +5,6 @@ import com.debatetimer.domain.customize.CustomizeTimeBox; import com.debatetimer.domain.customize.Stance; import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; -import java.util.Collections; import java.util.List; public record CustomizeTimeBoxResponse( @@ -32,19 +31,6 @@ public CustomizeTimeBoxResponse(CustomizeTimeBoxEntity customizeTimeBox) { ); } - public CustomizeTimeBoxResponse(CustomizeTimeBoxEntity customizeTimeBox, List bell) { - this( - customizeTimeBox.getStance(), - customizeTimeBox.getSpeechType(), - customizeTimeBox.getBoxType(), - convertTime(customizeTimeBox), - bell, - customizeTimeBox.getTimePerTeam(), - customizeTimeBox.getTimePerSpeaking(), - customizeTimeBox.getSpeaker() - ); - } - private static Integer convertTime(CustomizeTimeBoxEntity customizeTimeBox) { if (customizeTimeBox.getBoxType() == CustomizeBoxType.TIME_BASED) { return null; diff --git a/src/main/java/com/debatetimer/entity/customize/BellEntity.java b/src/main/java/com/debatetimer/entity/customize/BellEntity.java index fefc80ab..9c1f9982 100644 --- a/src/main/java/com/debatetimer/entity/customize/BellEntity.java +++ b/src/main/java/com/debatetimer/entity/customize/BellEntity.java @@ -1,5 +1,6 @@ package com.debatetimer.entity.customize; +import com.debatetimer.domain.customize.Bell; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import jakarta.persistence.Column; @@ -46,6 +47,12 @@ public BellEntity(CustomizeTimeBoxEntity customizeTimeBox, int time, int count) this.count = count; } + public BellEntity(CustomizeTimeBoxEntity customizeTimeBox, Bell bell) { + this.customizeTimeBox = customizeTimeBox; + this.time = bell.getTime(); + this.count = bell.getCount(); + } + private void validateTime(int time) { if (time < 0) { throw new DTClientErrorException(ClientErrorCode.INVALID_BELL_TIME); @@ -57,4 +64,12 @@ private void validateCount(int count) { throw new DTClientErrorException(ClientErrorCode.INVALID_BELL_COUNT); } } + + public Bell toDomain() { + return new Bell(time, count); + } + + public boolean isContained(CustomizeTimeBoxEntity timeBox) { + return this.customizeTimeBox.getId().equals(timeBox.getId()); + } } diff --git a/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntities.java b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntities.java new file mode 100644 index 00000000..32f5c383 --- /dev/null +++ b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntities.java @@ -0,0 +1,46 @@ +package com.debatetimer.entity.customize; + +import com.debatetimer.domain.customize.Bell; +import com.debatetimer.domain.customize.CustomizeTimeBox; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import lombok.Getter; + +public class CustomizeTimeBoxEntities { + + private static final Comparator TIME_BOX_COMPARATOR = Comparator + .comparing(CustomizeTimeBoxEntity::getSequence); + + @Getter + private final List timeBoxes; + + private final List bells; + + public CustomizeTimeBoxEntities(List timeBoxes) { + this.timeBoxes = timeBoxes.stream() + .sorted(TIME_BOX_COMPARATOR) + .toList(); + this.bells = Collections.emptyList(); + } + + public CustomizeTimeBoxEntities(List timeBoxes, List bells) { + this.timeBoxes = timeBoxes.stream() + .sorted(TIME_BOX_COMPARATOR) + .toList(); + this.bells = bells; + } + + public List toDomain() { + return timeBoxes.stream() + .map(timebox -> timebox.toDomain(getBells(timebox))) + .toList(); + } + + private List getBells(CustomizeTimeBoxEntity timeBox) { + return bells.stream() + .filter(bell -> bell.isContained(timeBox)) + .map(BellEntity::toDomain) + .toList(); + } +} diff --git a/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java index 04fe115e..9480b05e 100644 --- a/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java +++ b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java @@ -1,7 +1,11 @@ package com.debatetimer.entity.customize; +import com.debatetimer.domain.customize.Bell; import com.debatetimer.domain.customize.CustomizeBoxType; +import com.debatetimer.domain.customize.CustomizeTimeBox; +import com.debatetimer.domain.customize.NormalTimeBox; import com.debatetimer.domain.customize.Stance; +import com.debatetimer.domain.customize.TimeBasedTimeBox; import com.debatetimer.exception.custom.DTClientErrorException; import com.debatetimer.exception.errorcode.ClientErrorCode; import jakarta.persistence.Entity; @@ -16,6 +20,7 @@ import jakarta.persistence.Table; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import java.util.List; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -45,7 +50,7 @@ public class CustomizeTimeBoxEntity { @Enumerated(EnumType.STRING) private Stance stance; - private int time; + private Integer time; private String speaker; @NotBlank @@ -110,6 +115,18 @@ public CustomizeTimeBoxEntity( this.timePerSpeaking = timePerSpeaking; } + public CustomizeTimeBoxEntity(CustomizeTableEntity customizeTable, CustomizeTimeBox timeBox, int sequence) { + this.customizeTable = customizeTable; + this.sequence = sequence; + this.stance = timeBox.getStance(); + this.time = timeBox.getTime(); + this.speaker = timeBox.getSpeaker(); + this.speechType = timeBox.getSpeechType(); + this.boxType = timeBox.getBoxType(); + this.timePerTeam = timeBox.getTimePerTeam(); + this.timePerSpeaking = timeBox.getTimePerSpeaking(); + } + private static int convertToTime(Integer timePerTeam) { if (timePerTeam == null) { throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_FORMAT); @@ -178,4 +195,11 @@ private void validateSpeechType(String speechType) { throw new DTClientErrorException(ClientErrorCode.INVALID_TIME_BOX_SPEECH_TYPE_LENGTH); } } + + public CustomizeTimeBox toDomain(List bells) { + if (boxType.isTimeBased()) { + return new TimeBasedTimeBox(stance, speechType, speaker, timePerTeam, timePerSpeaking); + } + return new NormalTimeBox(stance, speechType, speaker, time, bells); + } } diff --git a/src/main/java/com/debatetimer/repository/customize/BellRepository.java b/src/main/java/com/debatetimer/repository/customize/BellRepository.java index 8dd571d6..8653266b 100644 --- a/src/main/java/com/debatetimer/repository/customize/BellRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/BellRepository.java @@ -3,15 +3,17 @@ import com.debatetimer.entity.customize.BellEntity; import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import java.util.List; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; public interface BellRepository extends Repository { BellEntity save(BellEntity bell); - List findByCustomizeTimeBox(CustomizeTimeBoxEntity customizeTimeBox); - - void deleteAllByCustomizeTimeBoxIn(List customizeTimeBoxes); + @Query("DELETE FROM BellEntity b WHERE b.customizeTimeBox.customizeTable.id = :tableId") + @Modifying(clearAutomatically = true, flushAutomatically = true) + void deleteAllByTable(long tableId); List findAllByCustomizeTimeBoxIn(List timeBoxes); } diff --git a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java index 298ea6c4..680391f6 100644 --- a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java @@ -1,7 +1,7 @@ package com.debatetimer.repository.customize; -import com.debatetimer.domain.customize.CustomizeTimeBoxEntities; import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import java.util.List; import org.springframework.data.jpa.repository.Modifying; @@ -27,7 +27,7 @@ default CustomizeTimeBoxEntities findTableTimeBoxes(CustomizeTableEntity table) return new CustomizeTimeBoxEntities(timeBoxes); } - @Query("DELETE FROM CustomizeTimeBoxEntity ctb WHERE ctb.customizeTable = :table") + @Query("DELETE FROM CustomizeTimeBoxEntity ctb WHERE ctb.customizeTable.id = :tableId") @Modifying(clearAutomatically = true, flushAutomatically = true) - void deleteAllByTable(CustomizeTableEntity table); + 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 cc9e7fee..13b3e44d 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeService.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeService.java @@ -1,11 +1,11 @@ package com.debatetimer.service.customize; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBoxEntities; 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.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; @@ -47,7 +47,7 @@ public CustomizeTableResponse updateTable( CustomizeTable renewedTable = tableCreateRequest.toTable(member); existingTable.updateTable(renewedTable); - timeBoxRepository.deleteAllByTable(existingTable); + timeBoxRepository.deleteAllByTable(existingTable.getId()); CustomizeTimeBoxEntities savedCustomizeTimeBoxes = saveTimeBoxes(tableCreateRequest, existingTable.toDomain()); return new CustomizeTableResponse(existingTable.toDomain(), savedCustomizeTimeBoxes); } @@ -64,7 +64,7 @@ public CustomizeTableResponse updateUsedAt(long tableId, Member member) { @Transactional public void deleteTable(long tableId, Member member) { CustomizeTableEntity table = tableRepository.getByIdAndMember(tableId, member); - timeBoxRepository.deleteAllByTable(table); + timeBoxRepository.deleteAllByTable(table.getId()); tableRepository.delete(table); } diff --git a/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java b/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java index f4e6cf48..392f8c89 100644 --- a/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java +++ b/src/main/java/com/debatetimer/service/customize/CustomizeServiceV2.java @@ -1,16 +1,13 @@ package com.debatetimer.service.customize; import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.CustomizeTimeBoxEntities; +import com.debatetimer.domain.customize.CustomizeTimeBox; 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.CustomizeTimeBoxCreateRequest; -import com.debatetimer.dto.customize.response.BellResponse; import com.debatetimer.dto.customize.response.CustomizeTableResponse; -import com.debatetimer.dto.customize.response.CustomizeTimeBoxResponse; import com.debatetimer.entity.customize.BellEntity; import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.repository.customize.BellRepository; import com.debatetimer.repository.customize.CustomizeTableRepository; @@ -32,20 +29,21 @@ public class CustomizeServiceV2 { @Transactional public CustomizeTableResponse save(CustomizeTableCreateRequest tableCreateRequest, Member member) { CustomizeTable table = tableCreateRequest.toTable(member); - CustomizeTableEntity savedTable = tableRepository.save(new CustomizeTableEntity(table)); + List timeBoxes = tableCreateRequest.toTimeBoxList(); - return saveTimeBoxesAndBells(tableCreateRequest, savedTable.toDomain()); + CustomizeTableEntity savedTableEntity = tableRepository.save(new CustomizeTableEntity(table)); + saveTimeBoxes(savedTableEntity, timeBoxes); + return new CustomizeTableResponse(savedTableEntity.toDomain(), timeBoxes); } @Transactional(readOnly = true) public CustomizeTableResponse findTable(long tableId, Member member) { CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); - CustomizeTimeBoxEntities timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); - List timeBoxResponses = timeBoxes.getTimeBoxes() - .stream() - .map(this::getTimeBoxResponse) - .toList(); - return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxResponses); + List timeBoxEntityList = timeBoxRepository.findAllByCustomizeTable(tableEntity); + List bellEntityList = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntityList); + CustomizeTimeBoxEntities timeBoxEntities = new CustomizeTimeBoxEntities(timeBoxEntityList, bellEntityList); + + return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxEntities.toDomain()); } @Transactional @@ -54,83 +52,47 @@ public CustomizeTableResponse updateTable( long tableId, Member member ) { - CustomizeTableEntity existingTable = tableRepository.getByIdAndMember(tableId, member); - CustomizeTable renewedTable = tableCreateRequest.toTable(member); - existingTable.updateTable(renewedTable); + CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); + tableEntity.updateTable(tableCreateRequest.toTable(member)); - deleteBell(timeBoxRepository.findTableTimeBoxes(existingTable)); - timeBoxRepository.deleteAllByTable(existingTable); - return saveTimeBoxesAndBells(tableCreateRequest, existingTable.toDomain()); + bellRepository.deleteAllByTable(tableEntity.getId()); + timeBoxRepository.deleteAllByTable(tableEntity.getId()); + List timeBoxes = tableCreateRequest.toTimeBoxList(); + saveTimeBoxes(tableEntity, timeBoxes); + return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxes); } @Transactional public CustomizeTableResponse updateUsedAt(long tableId, Member member) { CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); - CustomizeTimeBoxEntities timeBoxes = timeBoxRepository.findTableTimeBoxes(tableEntity); + List timeBoxEntityList = timeBoxRepository.findAllByCustomizeTable(tableEntity); + List bellEntityList = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntityList); + CustomizeTimeBoxEntities timeBoxEntities = new CustomizeTimeBoxEntities(timeBoxEntityList, bellEntityList); + tableEntity.updateUsedAt(); - List timeBoxResponses = timeBoxes.getTimeBoxes() - .stream() - .map(this::getTimeBoxResponse) - .toList(); - return new CustomizeTableResponse(tableEntity.toDomain(), timeBoxResponses); + CustomizeTable table = tableEntity.toDomain(); + List timeBoxes = timeBoxEntities.toDomain(); + return new CustomizeTableResponse(table, timeBoxes); } @Transactional public void deleteTable(long tableId, Member member) { CustomizeTableEntity table = tableRepository.getByIdAndMember(tableId, member); - deleteBell(timeBoxRepository.findTableTimeBoxes(table)); - timeBoxRepository.deleteAllByTable(table); + bellRepository.deleteAllByTable(table.getId()); + timeBoxRepository.deleteAllByTable(table.getId()); tableRepository.delete(table); } - private CustomizeTableResponse saveTimeBoxesAndBells( - CustomizeTableCreateRequest tableCreateRequest, - CustomizeTable table - ) { - List timeBoxCreateRequests = tableCreateRequest.table(); - List timeBoxResponses = IntStream.range(0, timeBoxCreateRequests.size()) - .mapToObj(i -> createTimeBoxResponse(timeBoxCreateRequests.get(i), table, i + 1)) - .toList(); - return new CustomizeTableResponse(table, timeBoxResponses); - } - - private CustomizeTimeBoxResponse createTimeBoxResponse( - CustomizeTimeBoxCreateRequest request, - CustomizeTable table, - int sequence - ) { - CustomizeTimeBoxEntity savedTimeBox = timeBoxRepository.save(request.toTimeBox(table, sequence)); - return createTimeBoxResponse(request.bell(), savedTimeBox); - } - - private CustomizeTimeBoxResponse createTimeBoxResponse(List bellRequests, CustomizeTimeBoxEntity timeBox) { - if (timeBox.getBoxType().isTimeBased()) { - return new CustomizeTimeBoxResponse(timeBox, null); - } - - List bellResponses = bellRequests - .stream() - .map(bellRequest -> new BellEntity(timeBox, bellRequest.time(), bellRequest.count())) - .map(bellRepository::save) - .map(bell -> new BellResponse(bell.getTime(), bell.getCount())) - .toList(); - return new CustomizeTimeBoxResponse(timeBox, bellResponses); - } - - private CustomizeTimeBoxResponse getTimeBoxResponse(CustomizeTimeBoxEntity timeBox) { - if (timeBox.getBoxType().isTimeBased()) { - return new CustomizeTimeBoxResponse(timeBox, null); - } - - List bellResponses = bellRepository.findByCustomizeTimeBox(timeBox) - .stream() - .map(bell -> new BellResponse(bell.getTime(), bell.getCount())) - .toList(); - return new CustomizeTimeBoxResponse(timeBox, bellResponses); + private void saveTimeBoxes(CustomizeTableEntity tableEntity, List timeBoxes) { + IntStream.range(0, timeBoxes.size()) + .forEach(i -> saveTimeBox(tableEntity, timeBoxes.get(i), i + 1)); } - private void deleteBell(CustomizeTimeBoxEntities savedCustomizeTimeBoxes) { - bellRepository.deleteAllByCustomizeTimeBoxIn(savedCustomizeTimeBoxes.getTimeBoxes()); + 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/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntitiesTest.java b/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntitiesTest.java similarity index 87% rename from src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntitiesTest.java rename to src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntitiesTest.java index 6db5cee9..48d0fb16 100644 --- a/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxEntitiesTest.java +++ b/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntitiesTest.java @@ -1,10 +1,11 @@ -package com.debatetimer.domain.customize; +package com.debatetimer.entity.customize; import static org.assertj.core.api.Assertions.assertThat; +import com.debatetimer.domain.customize.CustomizeBoxType; +import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.customize.Stance; import com.debatetimer.domain.member.Member; -import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java b/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java index 338f7430..2b7d1c04 100644 --- a/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java @@ -1,6 +1,7 @@ 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; @@ -12,7 +13,8 @@ JpaAuditingConfig.class, MemberGenerator.class, CustomizeTableGenerator.class, - CustomizeTimeBoxGenerator.class + CustomizeTimeBoxGenerator.class, + BellGenerator.class }) @DataJpaTest public abstract class BaseRepositoryTest { @@ -25,4 +27,7 @@ public abstract class BaseRepositoryTest { @Autowired protected CustomizeTimeBoxGenerator customizeTimeBoxGenerator; + + @Autowired + protected BellGenerator bellGenerator; } diff --git a/src/test/java/com/debatetimer/repository/customize/BellRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/BellRepositoryTest.java new file mode 100644 index 00000000..6dbb6919 --- /dev/null +++ b/src/test/java/com/debatetimer/repository/customize/BellRepositoryTest.java @@ -0,0 +1,47 @@ +package com.debatetimer.repository.customize; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.domain.customize.CustomizeBoxType; +import com.debatetimer.domain.member.Member; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; +import com.debatetimer.repository.BaseRepositoryTest; +import java.util.List; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class BellRepositoryTest extends BaseRepositoryTest { + + @Autowired + private BellRepository bellRepository; + + @Nested + 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, + CustomizeBoxType.NORMAL, 1); + CustomizeTimeBoxEntity otherTimeBox = customizeTimeBoxGenerator.generate(otherTable, + CustomizeBoxType.NORMAL, 1); + bellGenerator.generate(deleteBellTimeBox, 45, 1); + bellGenerator.generate(deleteBellTimeBox, 60, 1); + bellGenerator.generate(otherTimeBox, 45, 1); + + bellRepository.deleteAllByTable(deleteBellTable.getId()); + + assertAll( + () -> assertThat(bellRepository.findAllByCustomizeTimeBoxIn(List.of(deleteBellTimeBox))).isEmpty(), + () -> assertThat(bellRepository.findAllByCustomizeTimeBoxIn(List.of(otherTimeBox))).hasSize(1) + ); + + } + } + +} diff --git a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java index c0d42904..eb94339f 100644 --- a/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java @@ -48,7 +48,7 @@ class DeleteAllByTable { customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 1); customizeTimeBoxGenerator.generate(chanTable, CustomizeBoxType.NORMAL, 2); - customizeTimeBoxRepository.deleteAllByTable(chanTable); + customizeTimeBoxRepository.deleteAllByTable(chanTable.getId()); List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(chanTable); assertThat(timeBoxes).isEmpty(); @@ -63,7 +63,7 @@ class DeleteAllByTable { CustomizeTableEntity deletedTable = customizeTableGenerator.generate(chan); customizeTimeBoxGenerator.generate(deletedTable, CustomizeBoxType.NORMAL, 1); - customizeTimeBoxRepository.deleteAllByTable(deletedTable); + customizeTimeBoxRepository.deleteAllByTable(deletedTable.getId()); List timeBoxes = customizeTimeBoxRepository.findAllByCustomizeTable(filledTable); assertThat(timeBoxes).hasSize(2); @@ -74,7 +74,7 @@ class DeleteAllByTable { Member chan = memberGenerator.generate("default@gmail.com"); CustomizeTableEntity emptyTable = customizeTableGenerator.generate(chan); - assertThatCode(() -> customizeTimeBoxRepository.deleteAllByTable(emptyTable)) + assertThatCode(() -> customizeTimeBoxRepository.deleteAllByTable(emptyTable.getId())) .doesNotThrowAnyException(); } } diff --git a/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java b/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java index 252be593..1e0b58b7 100644 --- a/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java +++ b/src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java @@ -80,7 +80,7 @@ class FindTable { () -> 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()).hasSize(0) + () -> assertThat(foundResponse.table().get(1).bell()).isNull() ); } From ca1c4c128ccfbcca88f38043689a8e4bf6634a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B1=B4=EC=9A=B0?= Date: Sat, 26 Jul 2025 09:00:46 +0900 Subject: [PATCH 21/27] =?UTF-8?q?[FEAT]=20=ED=88=AC=ED=91=9C=20=EA=B0=9C?= =?UTF-8?q?=EC=B5=9C=EC=9E=90=20API=20=EA=B5=AC=ED=98=84=20(#203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/poll/PollController.java | 49 +++++ .../domain/poll/ParticipantName.java | 22 +++ .../domain/poll/ParticipateCode.java | 22 +++ .../com/debatetimer/domain/poll/Poll.java | 29 +++ .../com/debatetimer/domain/poll/Vote.java | 19 ++ .../com/debatetimer/domain/poll/VoteInfo.java | 19 ++ .../poll/CustomizeTableDomainRepository.java | 24 +++ .../poll/PollDomainRepository.java | 42 +++++ .../poll/VoteDomainRepository.java | 31 ++++ .../dto/poll/response/PollCreateResponse.java | 16 ++ .../dto/poll/response/PollInfoResponse.java | 28 +++ .../debatetimer/entity/poll/PollEntity.java | 26 +++ .../debatetimer/entity/poll/VoteEntity.java | 9 + .../exception/errorcode/ClientErrorCode.java | 4 + .../repository/poll/PollRepository.java | 10 ++ .../repository/poll/VoteRepository.java | 10 ++ .../debatetimer/service/poll/PollService.java | 46 +++++ .../migration/V11__add_memberId_into_poll.sql | 2 + .../controller/BaseControllerTest.java | 8 + .../controller/BaseDocumentTest.java | 4 + .../java/com/debatetimer/controller/Tag.java | 1 + .../controller/poll/PollControllerTest.java | 93 ++++++++++ .../controller/poll/PollDocumentTest.java | 168 ++++++++++++++++++ .../domain/poll/ParticipantNameTest.java | 24 +++ .../domain/poll/ParticipateCodeTest.java | 24 +++ .../BaseDomainRepositoryTest.java | 31 ++++ .../poll/PollDomainRepositoryTest.java | 74 ++++++++ .../poll/VoteDomainRepositoryTest.java | 45 +++++ .../debatetimer/fixture/PollGenerator.java | 31 ++++ .../debatetimer/fixture/VoteGenerator.java | 23 +++ .../repository/BaseRepositoryTest.java | 2 +- .../debatetimer/service/BaseServiceTest.java | 12 ++ .../service/poll/PollServiceTest.java | 79 ++++++++ 33 files changed, 1026 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/debatetimer/controller/poll/PollController.java create mode 100644 src/main/java/com/debatetimer/domain/poll/ParticipantName.java create mode 100644 src/main/java/com/debatetimer/domain/poll/ParticipateCode.java create mode 100644 src/main/java/com/debatetimer/domain/poll/Poll.java create mode 100644 src/main/java/com/debatetimer/domain/poll/Vote.java create mode 100644 src/main/java/com/debatetimer/domain/poll/VoteInfo.java create mode 100644 src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java create mode 100644 src/main/java/com/debatetimer/domainrepository/poll/PollDomainRepository.java create mode 100644 src/main/java/com/debatetimer/domainrepository/poll/VoteDomainRepository.java create mode 100644 src/main/java/com/debatetimer/dto/poll/response/PollCreateResponse.java create mode 100644 src/main/java/com/debatetimer/dto/poll/response/PollInfoResponse.java create mode 100644 src/main/java/com/debatetimer/repository/poll/PollRepository.java create mode 100644 src/main/java/com/debatetimer/repository/poll/VoteRepository.java create mode 100644 src/main/java/com/debatetimer/service/poll/PollService.java create mode 100644 src/main/resources/db/migration/V11__add_memberId_into_poll.sql create mode 100644 src/test/java/com/debatetimer/controller/poll/PollControllerTest.java create mode 100644 src/test/java/com/debatetimer/controller/poll/PollDocumentTest.java create mode 100644 src/test/java/com/debatetimer/domain/poll/ParticipantNameTest.java create mode 100644 src/test/java/com/debatetimer/domain/poll/ParticipateCodeTest.java create mode 100644 src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java create mode 100644 src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java create mode 100644 src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java create mode 100644 src/test/java/com/debatetimer/fixture/PollGenerator.java create mode 100644 src/test/java/com/debatetimer/fixture/VoteGenerator.java create mode 100644 src/test/java/com/debatetimer/service/poll/PollServiceTest.java diff --git a/src/main/java/com/debatetimer/controller/poll/PollController.java b/src/main/java/com/debatetimer/controller/poll/PollController.java new file mode 100644 index 00000000..88da0830 --- /dev/null +++ b/src/main/java/com/debatetimer/controller/poll/PollController.java @@ -0,0 +1,49 @@ +package com.debatetimer.controller.poll; + +import com.debatetimer.controller.auth.AuthMember; +import com.debatetimer.domain.member.Member; +import com.debatetimer.dto.poll.response.PollCreateResponse; +import com.debatetimer.dto.poll.response.PollInfoResponse; +import com.debatetimer.service.poll.PollService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class PollController { + + private final PollService pollService; + + @PostMapping("/api/polls/{tableId}") + @ResponseStatus(HttpStatus.CREATED) + public PollCreateResponse createPoll( + @AuthMember Member member, + @PathVariable(name = "tableId") long tableId + ) { + return pollService.create(tableId, member); + } + + @GetMapping("/api/polls/{pollId}") + @ResponseStatus(HttpStatus.OK) + public PollInfoResponse getPollInfo( + @AuthMember Member member, + @PathVariable(name = "pollId") long pollId + ) { + return pollService.getPollInfo(pollId, member); + } + + @PatchMapping("/api/polls/{pollId}") + @ResponseStatus(HttpStatus.OK) + public PollInfoResponse finishPoll( + @AuthMember Member member, + @PathVariable(name = "pollId") long pollId + ) { + return pollService.finishPoll(pollId, member); + } +} diff --git a/src/main/java/com/debatetimer/domain/poll/ParticipantName.java b/src/main/java/com/debatetimer/domain/poll/ParticipantName.java new file mode 100644 index 00000000..86c012a2 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/ParticipantName.java @@ -0,0 +1,22 @@ +package com.debatetimer.domain.poll; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import lombok.Getter; + +@Getter +public class ParticipantName { + + private final String value; + + public ParticipantName(String value) { + validateName(value); + this.value = value; + } + + private void validateName(String value) { + if (value == null || value.isBlank()) { + throw new DTClientErrorException(ClientErrorCode.INVALID_POLL_PARTICIPANT_NAME); + } + } +} diff --git a/src/main/java/com/debatetimer/domain/poll/ParticipateCode.java b/src/main/java/com/debatetimer/domain/poll/ParticipateCode.java new file mode 100644 index 00000000..29c323a2 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/ParticipateCode.java @@ -0,0 +1,22 @@ +package com.debatetimer.domain.poll; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import lombok.Getter; + +@Getter +public class ParticipateCode { + + private final String value; + + public ParticipateCode(String value) { + validateName(value); + this.value = value; + } + + private void validateName(String value) { + if (value == null || value.isBlank()) { + throw new DTClientErrorException(ClientErrorCode.INVALID_POLL_PARTICIPANT_CODE); + } + } +} diff --git a/src/main/java/com/debatetimer/domain/poll/Poll.java b/src/main/java/com/debatetimer/domain/poll/Poll.java new file mode 100644 index 00000000..fd1c15a5 --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/Poll.java @@ -0,0 +1,29 @@ +package com.debatetimer.domain.poll; + +import com.debatetimer.domain.customize.Agenda; +import com.debatetimer.domain.customize.TeamName; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class Poll { + + private final Long id; + private final long tableId; + private final long memberId; + private final PollStatus status; + private final TeamName prosTeamName; + private final TeamName consTeamName; + private final Agenda agenda; + + public Poll(long tableId, long memberId, String prosTeamName, String consTeamName, String agenda) { + this(null, tableId, memberId, PollStatus.PROGRESS, + new TeamName(prosTeamName), new TeamName(consTeamName), new Agenda(agenda)); + } + + public Poll(Long id, long tableId, long memberId, PollStatus status, + String prosTeamName, String consTeamName, String agenda) { + this(id, tableId, memberId, status, new TeamName(prosTeamName), new TeamName(consTeamName), new Agenda(agenda)); + } +} diff --git a/src/main/java/com/debatetimer/domain/poll/Vote.java b/src/main/java/com/debatetimer/domain/poll/Vote.java new file mode 100644 index 00000000..a69f946a --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/Vote.java @@ -0,0 +1,19 @@ +package com.debatetimer.domain.poll; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class Vote { + + private final Long id; + private final long pollId; + private final VoteTeam team; + private final ParticipantName name; + private final ParticipateCode code; + + public Vote(Long id, long pollId, VoteTeam team, String name, String code) { + this(id, pollId, team, new ParticipantName(name), new ParticipateCode(code)); + } +} diff --git a/src/main/java/com/debatetimer/domain/poll/VoteInfo.java b/src/main/java/com/debatetimer/domain/poll/VoteInfo.java new file mode 100644 index 00000000..36734ebb --- /dev/null +++ b/src/main/java/com/debatetimer/domain/poll/VoteInfo.java @@ -0,0 +1,19 @@ +package com.debatetimer.domain.poll; + +import lombok.Getter; + +@Getter +public class VoteInfo { + + private final long pollId; + private final long totalCount; + private final long prosCount; + private final long consCount; + + public VoteInfo(long pollId, long prosCount, long consCount) { + this.pollId = pollId; + this.totalCount = prosCount + consCount; + this.prosCount = prosCount; + this.consCount = consCount; + } +} diff --git a/src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java new file mode 100644 index 00000000..850c53bd --- /dev/null +++ b/src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java @@ -0,0 +1,24 @@ +package com.debatetimer.domainrepository.poll; + +import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.member.Member; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.repository.customize.CustomizeTableRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +@Repository +@RequiredArgsConstructor +public class CustomizeTableDomainRepository { + + private final CustomizeTableRepository customizeTableRepository; + + @Transactional(readOnly = true) + public CustomizeTable getByIdAndMember(long tableId, Member member) { + return customizeTableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)) + .toDomain(); + } +} diff --git a/src/main/java/com/debatetimer/domainrepository/poll/PollDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/poll/PollDomainRepository.java new file mode 100644 index 00000000..fa46adfb --- /dev/null +++ b/src/main/java/com/debatetimer/domainrepository/poll/PollDomainRepository.java @@ -0,0 +1,42 @@ +package com.debatetimer.domainrepository.poll; + +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.repository.poll.PollRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +@Repository +@RequiredArgsConstructor +public class PollDomainRepository { + + private final PollRepository pollRepository; + + @Transactional + public Poll create(Poll poll) { + PollEntity pollEntity = new PollEntity(poll); + return pollRepository.save(pollEntity) + .toDomain(); + } + + @Transactional(readOnly = true) + public Poll getByIdAndMemberId(long id, long memberId) { + return findPoll(id, memberId) + .toDomain(); + } + + @Transactional + public Poll finishPoll(long pollId, long memberId) { + PollEntity pollEntity = findPoll(pollId, memberId); + pollEntity.updateToDone(); + return pollEntity.toDomain(); + } + + private PollEntity findPoll(long pollId, long memberId) { + return pollRepository.findByIdAndMemberId(pollId, memberId) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.POLL_NOT_FOUND)); + } +} diff --git a/src/main/java/com/debatetimer/domainrepository/poll/VoteDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/poll/VoteDomainRepository.java new file mode 100644 index 00000000..97f8dd1f --- /dev/null +++ b/src/main/java/com/debatetimer/domainrepository/poll/VoteDomainRepository.java @@ -0,0 +1,31 @@ +package com.debatetimer.domainrepository.poll; + +import com.debatetimer.domain.poll.VoteInfo; +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.entity.poll.VoteEntity; +import com.debatetimer.repository.poll.VoteRepository; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class VoteDomainRepository { + + private final VoteRepository voteRepository; + + public VoteInfo findVoteInfoByPollId(long pollId) { + List pollVotes = voteRepository.findAllByPollId(pollId); + return countVotes(pollId, pollVotes); + } + + private VoteInfo countVotes(long pollId, List voteEntities) { + Map teamCount = voteEntities.stream() + .collect(Collectors.groupingBy(VoteEntity::getTeam, Collectors.counting())); + long prosCount = teamCount.getOrDefault(VoteTeam.PROS, 0L); + long consCount = teamCount.getOrDefault(VoteTeam.CONS, 0L); + return new VoteInfo(pollId, prosCount, consCount); + } +} diff --git a/src/main/java/com/debatetimer/dto/poll/response/PollCreateResponse.java b/src/main/java/com/debatetimer/dto/poll/response/PollCreateResponse.java new file mode 100644 index 00000000..462ec3d5 --- /dev/null +++ b/src/main/java/com/debatetimer/dto/poll/response/PollCreateResponse.java @@ -0,0 +1,16 @@ +package com.debatetimer.dto.poll.response; + +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.PollStatus; + +public record PollCreateResponse( + long id, + PollStatus status, + String prosTeamName, + String consTeamName +) { + + public PollCreateResponse(Poll poll) { + this(poll.getId(), poll.getStatus(), poll.getProsTeamName().getValue(), poll.getConsTeamName().getValue()); + } +} diff --git a/src/main/java/com/debatetimer/dto/poll/response/PollInfoResponse.java b/src/main/java/com/debatetimer/dto/poll/response/PollInfoResponse.java new file mode 100644 index 00000000..69f3f6ae --- /dev/null +++ b/src/main/java/com/debatetimer/dto/poll/response/PollInfoResponse.java @@ -0,0 +1,28 @@ +package com.debatetimer.dto.poll.response; + +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domain.poll.VoteInfo; + +public record PollInfoResponse( + long id, + PollStatus status, + String prosTeamName, + String consTeamName, + long totalCount, + long prosCount, + long consCount +) { + + public PollInfoResponse(Poll poll, VoteInfo voteInfo) { + this( + poll.getId(), + poll.getStatus(), + poll.getProsTeamName().getValue(), + poll.getConsTeamName().getValue(), + voteInfo.getTotalCount(), + voteInfo.getProsCount(), + voteInfo.getConsCount() + ); + } +} diff --git a/src/main/java/com/debatetimer/entity/poll/PollEntity.java b/src/main/java/com/debatetimer/entity/poll/PollEntity.java index d4700a3d..bb4e5433 100644 --- a/src/main/java/com/debatetimer/entity/poll/PollEntity.java +++ b/src/main/java/com/debatetimer/entity/poll/PollEntity.java @@ -1,7 +1,9 @@ package com.debatetimer.entity.poll; +import com.debatetimer.domain.poll.Poll; import com.debatetimer.domain.poll.PollStatus; import com.debatetimer.entity.customize.BaseTimeEntity; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -12,9 +14,11 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity +@Getter @Table(name = "poll") @NoArgsConstructor(access = AccessLevel.PROTECTED) public class PollEntity extends BaseTimeEntity { @@ -23,8 +27,12 @@ public class PollEntity extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(name = "table_id") private long tableId; + @Column(name = "member_id") + private long memberId; + @NotNull @Enumerated(EnumType.STRING) private PollStatus status; @@ -36,4 +44,22 @@ public class PollEntity extends BaseTimeEntity { private String consTeamName; private String agenda; + + public PollEntity(Poll poll) { + this.id = poll.getId(); + this.tableId = poll.getTableId(); + this.memberId = poll.getMemberId(); + this.status = poll.getStatus(); + this.prosTeamName = poll.getProsTeamName().getValue(); + this.consTeamName = poll.getConsTeamName().getValue(); + this.agenda = poll.getAgenda().getValue(); + } + + public void updateToDone() { + this.status = PollStatus.DONE; + } + + public Poll toDomain() { + return new Poll(id, tableId, memberId, status, prosTeamName, consTeamName, agenda); + } } diff --git a/src/main/java/com/debatetimer/entity/poll/VoteEntity.java b/src/main/java/com/debatetimer/entity/poll/VoteEntity.java index 9b01e7f4..47dd0a2c 100644 --- a/src/main/java/com/debatetimer/entity/poll/VoteEntity.java +++ b/src/main/java/com/debatetimer/entity/poll/VoteEntity.java @@ -1,5 +1,6 @@ package com.debatetimer.entity.poll; +import com.debatetimer.domain.poll.Vote; import com.debatetimer.domain.poll.VoteTeam; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -14,11 +15,15 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity +@Getter @Table(name = "vote") @NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor public class VoteEntity { @Id @@ -39,4 +44,8 @@ public class VoteEntity { @NotBlank private String participantCode; + + public Vote toDomain() { + return new Vote(id, poll.getId(), team, name, participantCode); + } } diff --git a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java index 59eb8cb9..f0672c70 100644 --- a/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java +++ b/src/main/java/com/debatetimer/exception/errorcode/ClientErrorCode.java @@ -48,8 +48,12 @@ public enum ClientErrorCode implements ResponseErrorCode { "토론 주제는 1자 이상 %d자 이하여야 합니다.".formatted(Agenda.AGENDA_MAX_LENGTH) ), + INVALID_POLL_PARTICIPANT_NAME(HttpStatus.BAD_REQUEST, "잘못된 투표자 이름입니다"), + INVALID_POLL_PARTICIPANT_CODE(HttpStatus.BAD_REQUEST, "잘못된 투표참여 코드입니다"), + TABLE_NOT_FOUND(HttpStatus.NOT_FOUND, "토론 테이블을 찾을 수 없습니다."), NOT_TABLE_OWNER(HttpStatus.UNAUTHORIZED, "테이블을 소유한 회원이 아닙니다."), + POLL_NOT_FOUND(HttpStatus.NOT_FOUND, "투표를 찾을 수 없습니다."), UNAUTHORIZED_MEMBER(HttpStatus.UNAUTHORIZED, "접근 권한이 없습니다"), EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "토큰 기한이 만료되었습니다"), diff --git a/src/main/java/com/debatetimer/repository/poll/PollRepository.java b/src/main/java/com/debatetimer/repository/poll/PollRepository.java new file mode 100644 index 00000000..121d1627 --- /dev/null +++ b/src/main/java/com/debatetimer/repository/poll/PollRepository.java @@ -0,0 +1,10 @@ +package com.debatetimer.repository.poll; + +import com.debatetimer.entity.poll.PollEntity; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PollRepository extends JpaRepository { + + Optional findByIdAndMemberId(long id, long memberId); +} diff --git a/src/main/java/com/debatetimer/repository/poll/VoteRepository.java b/src/main/java/com/debatetimer/repository/poll/VoteRepository.java new file mode 100644 index 00000000..a9709200 --- /dev/null +++ b/src/main/java/com/debatetimer/repository/poll/VoteRepository.java @@ -0,0 +1,10 @@ +package com.debatetimer.repository.poll; + +import com.debatetimer.entity.poll.VoteEntity; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface VoteRepository extends JpaRepository { + + List findAllByPollId(long pollId); +} diff --git a/src/main/java/com/debatetimer/service/poll/PollService.java b/src/main/java/com/debatetimer/service/poll/PollService.java new file mode 100644 index 00000000..a6409fdb --- /dev/null +++ b/src/main/java/com/debatetimer/service/poll/PollService.java @@ -0,0 +1,46 @@ +package com.debatetimer.service.poll; + +import com.debatetimer.domain.customize.CustomizeTable; +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.VoteInfo; +import com.debatetimer.domainrepository.poll.CustomizeTableDomainRepository; +import com.debatetimer.domainrepository.poll.PollDomainRepository; +import com.debatetimer.domainrepository.poll.VoteDomainRepository; +import com.debatetimer.dto.poll.response.PollCreateResponse; +import com.debatetimer.dto.poll.response.PollInfoResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class PollService { + + private final CustomizeTableDomainRepository customizeTableDomainRepository; + private final PollDomainRepository pollDomainRepository; + private final VoteDomainRepository voteDomainRepository; + + @Transactional + public PollCreateResponse create(long tableId, Member member) { + CustomizeTable table = customizeTableDomainRepository.getByIdAndMember(tableId, member); + Poll poll = new Poll(table.getId(), member.getId(), table.getProsTeamName(), + table.getConsTeamName(), table.getAgenda()); + Poll savedPoll = pollDomainRepository.create(poll); + return new PollCreateResponse(savedPoll); + } + + @Transactional(readOnly = true) + public PollInfoResponse getPollInfo(long pollId, Member member) { + Poll poll = pollDomainRepository.getByIdAndMemberId(pollId, member.getId()); + VoteInfo voteInfo = voteDomainRepository.findVoteInfoByPollId(pollId); + return new PollInfoResponse(poll, voteInfo); + } + + @Transactional + public PollInfoResponse finishPoll(long pollId, Member member) { + Poll poll = pollDomainRepository.finishPoll(pollId, member.getId()); + VoteInfo voteInfo = voteDomainRepository.findVoteInfoByPollId(pollId); + return new PollInfoResponse(poll, voteInfo); + } +} diff --git a/src/main/resources/db/migration/V11__add_memberId_into_poll.sql b/src/main/resources/db/migration/V11__add_memberId_into_poll.sql new file mode 100644 index 00000000..08772417 --- /dev/null +++ b/src/main/resources/db/migration/V11__add_memberId_into_poll.sql @@ -0,0 +1,2 @@ +ALTER TABLE poll + ADD COLUMN member_id BIGINT NOT NULL diff --git a/src/test/java/com/debatetimer/controller/BaseControllerTest.java b/src/test/java/com/debatetimer/controller/BaseControllerTest.java index 2ca415ea..83d35dda 100644 --- a/src/test/java/com/debatetimer/controller/BaseControllerTest.java +++ b/src/test/java/com/debatetimer/controller/BaseControllerTest.java @@ -12,7 +12,9 @@ import com.debatetimer.fixture.CustomizeTimeBoxGenerator; import com.debatetimer.fixture.HeaderGenerator; import com.debatetimer.fixture.MemberGenerator; +import com.debatetimer.fixture.PollGenerator; import com.debatetimer.fixture.TokenGenerator; +import com.debatetimer.fixture.VoteGenerator; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.navercorp.fixturemonkey.ArbitraryBuilder; import com.navercorp.fixturemonkey.FixtureMonkey; @@ -45,6 +47,12 @@ public abstract class BaseControllerTest { @Autowired protected CustomizeTimeBoxGenerator customizeTimeBoxGenerator; + @Autowired + protected PollGenerator pollGenerator; + + @Autowired + protected VoteGenerator voteGenerator; + @Autowired protected HeaderGenerator headerGenerator; diff --git a/src/test/java/com/debatetimer/controller/BaseDocumentTest.java b/src/test/java/com/debatetimer/controller/BaseDocumentTest.java index 206c8cda..40c80cc1 100644 --- a/src/test/java/com/debatetimer/controller/BaseDocumentTest.java +++ b/src/test/java/com/debatetimer/controller/BaseDocumentTest.java @@ -12,6 +12,7 @@ import com.debatetimer.service.auth.AuthService; import com.debatetimer.service.customize.CustomizeService; import com.debatetimer.service.member.MemberService; +import com.debatetimer.service.poll.PollService; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.filter.log.RequestLoggingFilter; @@ -62,6 +63,9 @@ public abstract class BaseDocumentTest { @MockitoBean protected AuthService authService; + @MockitoBean + protected PollService pollService; + @MockitoBean protected AuthManager authManager; diff --git a/src/test/java/com/debatetimer/controller/Tag.java b/src/test/java/com/debatetimer/controller/Tag.java index c7dfa897..39a3c588 100644 --- a/src/test/java/com/debatetimer/controller/Tag.java +++ b/src/test/java/com/debatetimer/controller/Tag.java @@ -6,6 +6,7 @@ public enum Tag { PARLIAMENTARY_API("Parliamentary Table API"), TIME_BASED_API("Time Based Table API"), CUSTOMIZE_API("Customize Table API"), + POLL_API("Poll API"), ; private final String displayName; diff --git a/src/test/java/com/debatetimer/controller/poll/PollControllerTest.java b/src/test/java/com/debatetimer/controller/poll/PollControllerTest.java new file mode 100644 index 00000000..22487d85 --- /dev/null +++ b/src/test/java/com/debatetimer/controller/poll/PollControllerTest.java @@ -0,0 +1,93 @@ +package com.debatetimer.controller.poll; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.controller.BaseControllerTest; +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.dto.poll.response.PollInfoResponse; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import io.restassured.http.ContentType; +import io.restassured.http.Headers; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; + +class PollControllerTest extends BaseControllerTest { + + @Nested + class CreatePoll { + + @Test + void 선거를_생성할_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + Headers headers = headerGenerator.generateAccessTokenHeader(member); + + given() + .contentType(ContentType.JSON) + .headers(headers) + .pathParam("tableId", table.getId()) + .when().post("/api/polls/{tableId}") + .then().statusCode(HttpStatus.CREATED.value()); + } + } + + @Nested + class GetPollInfo { + + @Test + void 선거정보를_읽을_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); + Headers headers = headerGenerator.generateAccessTokenHeader(member); + + PollInfoResponse response = given() + .contentType(ContentType.JSON) + .headers(headers) + .pathParam("pollId", pollEntity.getId()) + .when().get("/api/polls/{pollId}") + .then().statusCode(HttpStatus.OK.value()) + .extract().as(PollInfoResponse.class); + + assertAll( + () -> assertThat(response.id()).isEqualTo(pollEntity.getId()), + () -> assertThat(response.prosTeamName()).isEqualTo(pollEntity.getProsTeamName()), + () -> assertThat(response.consTeamName()).isEqualTo(pollEntity.getConsTeamName()), + () -> assertThat(response.status()).isEqualTo(pollEntity.getStatus()), + () -> assertThat(response.totalCount()).isEqualTo(3L), + () -> assertThat(response.prosCount()).isEqualTo(2L), + () -> assertThat(response.consCount()).isEqualTo(1L) + ); + } + } + + @Nested + class FinishPoll { + + @Test + void 선거정보를_완료상태로_변경한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + Headers headers = headerGenerator.generateAccessTokenHeader(member); + + PollInfoResponse response = given() + .contentType(ContentType.JSON) + .headers(headers) + .pathParam("pollId", pollEntity.getId()) + .when().patch("/api/polls/{pollId}") + .then().statusCode(HttpStatus.OK.value()) + .extract().as(PollInfoResponse.class); + + assertThat(response.status()).isEqualTo(PollStatus.DONE); + } + } +} diff --git a/src/test/java/com/debatetimer/controller/poll/PollDocumentTest.java b/src/test/java/com/debatetimer/controller/poll/PollDocumentTest.java new file mode 100644 index 00000000..e0cd3b42 --- /dev/null +++ b/src/test/java/com/debatetimer/controller/poll/PollDocumentTest.java @@ -0,0 +1,168 @@ +package com.debatetimer.controller.poll; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doReturn; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; + +import com.debatetimer.controller.BaseDocumentTest; +import com.debatetimer.controller.RestDocumentationRequest; +import com.debatetimer.controller.RestDocumentationResponse; +import com.debatetimer.controller.Tag; +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.dto.poll.response.PollCreateResponse; +import com.debatetimer.dto.poll.response.PollInfoResponse; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; + +public class PollDocumentTest extends BaseDocumentTest { + + @Nested + class CreatePoll { + + private final RestDocumentationRequest requestDocument = request() + .tag(Tag.POLL_API) + .summary("선거 생성") + .requestHeader( + headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") + ) + .pathParameter( + parameterWithName("tableId").description("테이블 ID") + ); + + private final RestDocumentationResponse responseDocument = response() + .responseBodyField( + fieldWithPath("id").type(NUMBER).description("선거 ID"), + fieldWithPath("status").type(STRING).description("선거 상태 - 진행중 : PROGRESS, 완료 : DONE"), + fieldWithPath("prosTeamName").type(STRING).description("찬성측 팀 이름"), + fieldWithPath("consTeamName").type(STRING).description("반대측 팀 이름") + ); + + @Test + void 선거_생성_성공() { + PollCreateResponse response = new PollCreateResponse(1l, PollStatus.PROGRESS, "찬성", "반대"); + doReturn(response).when(pollService).create(anyLong(), any(Member.class)); + + var document = document("poll/post", 201) + .request(requestDocument) + .response(responseDocument) + .build(); + + given(document) + .contentType(ContentType.JSON) + .headers(EXIST_MEMBER_HEADER) + .pathParam("tableId", 1l) + .when().post("/api/polls/{tableId}") + .then().statusCode(201); + } + } + + @Nested + class GetPollInfo { + + private final RestDocumentationRequest requestDocument = request() + .tag(Tag.POLL_API) + .summary("선거 정보 조회") + .requestHeader( + headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") + ) + .pathParameter( + parameterWithName("pollId").description("선거 ID") + ); + + private final RestDocumentationResponse responseDocument = response() + .responseBodyField( + fieldWithPath("id").type(NUMBER).description("선거 ID"), + fieldWithPath("status").type(STRING).description("선거 상태 - 진행중 : PROGRESS, 완료 : DONE"), + fieldWithPath("prosTeamName").type(STRING).description("찬성측 팀 이름"), + fieldWithPath("consTeamName").type(STRING).description("반대측 팀 이름"), + fieldWithPath("totalCount").type(NUMBER).description("전체 투표 수"), + fieldWithPath("prosCount").type(NUMBER).description("찬성 투표 수"), + fieldWithPath("consCount").type(NUMBER).description("반대 투표 수") + ); + + @Test + void 선거_정보_조회() { + PollInfoResponse response = new PollInfoResponse( + 1L, + PollStatus.PROGRESS, + "찬성", + "반대", + 3L, + 2L, + 1L + ); + doReturn(response).when(pollService).getPollInfo(anyLong(), any(Member.class)); + + var document = document("poll/get", 200) + .request(requestDocument) + .response(responseDocument) + .build(); + + given(document) + .contentType(ContentType.JSON) + .headers(EXIST_MEMBER_HEADER) + .pathParam("pollId", 1l) + .when().get("/api/polls/{pollId}") + .then().statusCode(200); + } + } + + @Nested + class FinishPoll { + + private final RestDocumentationRequest requestDocument = request() + .tag(Tag.POLL_API) + .summary("선거 완료") + .requestHeader( + headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰") + ) + .pathParameter( + parameterWithName("pollId").description("선거 ID") + ); + + private final RestDocumentationResponse responseDocument = response() + .responseBodyField( + fieldWithPath("id").type(NUMBER).description("선거 ID"), + fieldWithPath("status").type(STRING).description("선거 상태 - 진행중 : PROGRESS, 완료 : DONE"), + fieldWithPath("prosTeamName").type(STRING).description("찬성측 팀 이름"), + fieldWithPath("consTeamName").type(STRING).description("반대측 팀 이름"), + fieldWithPath("totalCount").type(NUMBER).description("전체 투표 수"), + fieldWithPath("prosCount").type(NUMBER).description("찬성 투표 수"), + fieldWithPath("consCount").type(NUMBER).description("반대 투표 수") + ); + + @Test + void 선거_완료() { + PollInfoResponse response = new PollInfoResponse( + 1L, + PollStatus.DONE, + "찬성", + "반대", + 3L, + 2L, + 1L + ); + doReturn(response).when(pollService).finishPoll(anyLong(), any(Member.class)); + + var document = document("poll/patch", 200) + .request(requestDocument) + .response(responseDocument) + .build(); + + given(document) + .contentType(ContentType.JSON) + .headers(EXIST_MEMBER_HEADER) + .pathParam("pollId", 1l) + .when().patch("/api/polls/{pollId}") + .then().statusCode(200); + } + } +} diff --git a/src/test/java/com/debatetimer/domain/poll/ParticipantNameTest.java b/src/test/java/com/debatetimer/domain/poll/ParticipantNameTest.java new file mode 100644 index 00000000..a6f25f32 --- /dev/null +++ b/src/test/java/com/debatetimer/domain/poll/ParticipantNameTest.java @@ -0,0 +1,24 @@ +package com.debatetimer.domain.poll; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.fixture.NullAndEmptyAndBlankSource; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; + +class ParticipantNameTest { + + @Nested + class Validate { + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 투표참여자_이름은_널이거나_빈_값_일_수_없다(String name) { + assertThatThrownBy(() -> new ParticipantName(name)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_POLL_PARTICIPANT_NAME.getMessage()); + } + } +} diff --git a/src/test/java/com/debatetimer/domain/poll/ParticipateCodeTest.java b/src/test/java/com/debatetimer/domain/poll/ParticipateCodeTest.java new file mode 100644 index 00000000..d8d5245c --- /dev/null +++ b/src/test/java/com/debatetimer/domain/poll/ParticipateCodeTest.java @@ -0,0 +1,24 @@ +package com.debatetimer.domain.poll; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.debatetimer.exception.custom.DTClientErrorException; +import com.debatetimer.exception.errorcode.ClientErrorCode; +import com.debatetimer.fixture.NullAndEmptyAndBlankSource; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; + +class ParticipateCodeTest { + + @Nested + class Validate { + + @ParameterizedTest + @NullAndEmptyAndBlankSource + void 투표_참여_코드는_널이거나_빈_값_일_수_없다(String participatecode) { + assertThatThrownBy(() -> new ParticipateCode(participatecode)) + .isInstanceOf(DTClientErrorException.class) + .hasMessage(ClientErrorCode.INVALID_POLL_PARTICIPANT_CODE.getMessage()); + } + } +} diff --git a/src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java new file mode 100644 index 00000000..37ac4fba --- /dev/null +++ b/src/test/java/com/debatetimer/domainrepository/BaseDomainRepositoryTest.java @@ -0,0 +1,31 @@ +package com.debatetimer.domainrepository; + +import com.debatetimer.DataBaseCleaner; +import com.debatetimer.fixture.CustomizeTableGenerator; +import com.debatetimer.fixture.MemberGenerator; +import com.debatetimer.fixture.PollGenerator; +import com.debatetimer.fixture.VoteGenerator; +import com.debatetimer.repository.poll.PollRepository; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@ExtendWith(DataBaseCleaner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +public abstract class BaseDomainRepositoryTest { + + @Autowired + protected MemberGenerator memberGenerator; + + @Autowired + protected CustomizeTableGenerator customizeTableGenerator; + + @Autowired + protected PollGenerator pollGenerator; + + @Autowired + protected VoteGenerator voteGenerator; + + @Autowired + protected PollRepository pollRepository; +} diff --git a/src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java new file mode 100644 index 00000000..c97849e4 --- /dev/null +++ b/src/test/java/com/debatetimer/domainrepository/poll/PollDomainRepositoryTest.java @@ -0,0 +1,74 @@ +package com.debatetimer.domainrepository.poll; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domainrepository.BaseDomainRepositoryTest; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import java.util.Optional; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class PollDomainRepositoryTest extends BaseDomainRepositoryTest { + + @Autowired + private PollDomainRepository pollDomainRepository; + + @Nested + class Create { + + @Test + void 선거를_생성한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + Poll poll = new Poll(table.getId(), member.getId(), "찬성", "반대", "주제"); + + Poll createdPoll = pollDomainRepository.create(poll); + + Optional foundPollEntity = pollRepository.findById(createdPoll.getId()); + assertThat(foundPollEntity).isPresent(); + } + } + + @Nested + class GetByIdAndMemberId { + + @Test + void 회원이_개최한_선거를_가져온다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + + Poll foundPoll = pollDomainRepository.getByIdAndMemberId(pollEntity.getId(), member.getId()); + + assertAll( + () -> assertThat(foundPoll.getId()).isEqualTo(pollEntity.getId()), + () -> assertThat(foundPoll.getAgenda().getValue()).isEqualTo(pollEntity.getAgenda()), + () -> assertThat(foundPoll.getStatus()).isEqualTo(pollEntity.getStatus()), + () -> assertThat(foundPoll.getMemberId()).isEqualTo(pollEntity.getMemberId()), + () -> assertThat(foundPoll.getProsTeamName().getValue()).isEqualTo(pollEntity.getProsTeamName()), + () -> assertThat(foundPoll.getConsTeamName().getValue()).isEqualTo(pollEntity.getConsTeamName()) + ); + } + } + + @Nested + class FinishPoll { + + @Test + void 선거를_완료_상태로_변경한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + + Poll updatedPoll = pollDomainRepository.finishPoll(pollEntity.getId(), member.getId()); + + assertThat(updatedPoll.getStatus()).isEqualTo(PollStatus.DONE); + } + } +} diff --git a/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java new file mode 100644 index 00000000..068e782e --- /dev/null +++ b/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java @@ -0,0 +1,45 @@ +package com.debatetimer.domainrepository.poll; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domain.poll.VoteInfo; +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.domainrepository.BaseDomainRepositoryTest; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class VoteDomainRepositoryTest extends BaseDomainRepositoryTest { + + @Autowired + private VoteDomainRepository voteDomainRepository; + + @Nested + class GetVoteInfo { + + @Test + void 팀별_투표_현황을_알_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); + + VoteInfo voteInfo = voteDomainRepository.findVoteInfoByPollId(pollEntity.getId()); + + assertAll( + () -> assertThat(voteInfo.getPollId()).isEqualTo(pollEntity.getId()), + () -> assertThat(voteInfo.getTotalCount()).isEqualTo(3L), + () -> assertThat(voteInfo.getProsCount()).isEqualTo(2L), + () -> assertThat(voteInfo.getConsCount()).isEqualTo(1L) + ); + } + } + +} diff --git a/src/test/java/com/debatetimer/fixture/PollGenerator.java b/src/test/java/com/debatetimer/fixture/PollGenerator.java new file mode 100644 index 00000000..24059cba --- /dev/null +++ b/src/test/java/com/debatetimer/fixture/PollGenerator.java @@ -0,0 +1,31 @@ +package com.debatetimer.fixture; + +import com.debatetimer.domain.poll.Poll; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.repository.poll.PollRepository; +import org.springframework.stereotype.Component; + +@Component +public class PollGenerator { + + private final PollRepository pollRepository; + + public PollGenerator(final PollRepository pollRepository) { + this.pollRepository = pollRepository; + } + + public PollEntity generate(CustomizeTableEntity customizeTableEntity, PollStatus status) { + Poll poll = new Poll( + null, + customizeTableEntity.getId(), + customizeTableEntity.getMember().getId(), + status, + "찬성", + "반대", + "주제" + ); + return pollRepository.save(new PollEntity(poll)); + } +} diff --git a/src/test/java/com/debatetimer/fixture/VoteGenerator.java b/src/test/java/com/debatetimer/fixture/VoteGenerator.java new file mode 100644 index 00000000..9efdd80c --- /dev/null +++ b/src/test/java/com/debatetimer/fixture/VoteGenerator.java @@ -0,0 +1,23 @@ +package com.debatetimer.fixture; + +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.entity.poll.VoteEntity; +import com.debatetimer.repository.poll.VoteRepository; +import java.util.UUID; +import org.springframework.stereotype.Component; + +@Component +public class VoteGenerator { + + private final VoteRepository voteRepository; + + public VoteGenerator(VoteRepository voteRepository) { + this.voteRepository = voteRepository; + } + + public VoteEntity generate(PollEntity pollEntity, VoteTeam team, String name) { + VoteEntity vote = new VoteEntity(null, pollEntity, team, name, UUID.randomUUID().toString()); + return voteRepository.save(vote); + } +} diff --git a/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java b/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java index 2b7d1c04..87b5404b 100644 --- a/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java +++ b/src/test/java/com/debatetimer/repository/BaseRepositoryTest.java @@ -14,7 +14,7 @@ MemberGenerator.class, CustomizeTableGenerator.class, CustomizeTimeBoxGenerator.class, - BellGenerator.class + BellGenerator.class, }) @DataJpaTest public abstract class BaseRepositoryTest { diff --git a/src/test/java/com/debatetimer/service/BaseServiceTest.java b/src/test/java/com/debatetimer/service/BaseServiceTest.java index 6220cc2a..a68e418d 100644 --- a/src/test/java/com/debatetimer/service/BaseServiceTest.java +++ b/src/test/java/com/debatetimer/service/BaseServiceTest.java @@ -5,10 +5,13 @@ import com.debatetimer.fixture.CustomizeTableGenerator; import com.debatetimer.fixture.CustomizeTimeBoxGenerator; import com.debatetimer.fixture.MemberGenerator; +import com.debatetimer.fixture.PollGenerator; +import com.debatetimer.fixture.VoteGenerator; import com.debatetimer.repository.customize.BellRepository; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; import com.debatetimer.repository.member.MemberRepository; +import com.debatetimer.repository.poll.PollRepository; import java.util.List; import java.util.stream.IntStream; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,6 +34,9 @@ public abstract class BaseServiceTest { @Autowired protected BellRepository bellRepository; + @Autowired + protected PollRepository pollRepository; + @Autowired protected MemberGenerator memberGenerator; @@ -43,6 +49,12 @@ public abstract class BaseServiceTest { @Autowired protected BellGenerator bellGenerator; + @Autowired + protected PollGenerator pollGenerator; + + @Autowired + protected VoteGenerator voteGenerator; + protected void runAtSameTime(int count, Runnable task) throws InterruptedException { List threads = IntStream.range(0, count) .mapToObj(i -> new Thread(task)) diff --git a/src/test/java/com/debatetimer/service/poll/PollServiceTest.java b/src/test/java/com/debatetimer/service/poll/PollServiceTest.java new file mode 100644 index 00000000..fd5c96c2 --- /dev/null +++ b/src/test/java/com/debatetimer/service/poll/PollServiceTest.java @@ -0,0 +1,79 @@ +package com.debatetimer.service.poll; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.debatetimer.domain.member.Member; +import com.debatetimer.domain.poll.PollStatus; +import com.debatetimer.domain.poll.VoteTeam; +import com.debatetimer.dto.poll.response.PollCreateResponse; +import com.debatetimer.dto.poll.response.PollInfoResponse; +import com.debatetimer.entity.customize.CustomizeTableEntity; +import com.debatetimer.entity.poll.PollEntity; +import com.debatetimer.service.BaseServiceTest; +import java.util.Optional; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class PollServiceTest extends BaseServiceTest { + + @Autowired + private PollService pollService; + + @Nested + class CreatePoll { + + @Test + void 선거를_생성한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + + PollCreateResponse createdPoll = pollService.create(table.getId(), member); + + Optional foundPoll = pollRepository.findById(createdPoll.id()); + assertThat(foundPoll).isPresent(); + } + } + + @Nested + class GetPollInfo { + + @Test + void 선거_정보를_읽어온다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + voteGenerator.generate(pollEntity, VoteTeam.PROS, "비토"); + voteGenerator.generate(pollEntity, VoteTeam.CONS, "커찬"); + + PollInfoResponse pollInfo = pollService.getPollInfo(table.getId(), member); + + assertAll( + () -> assertThat(pollInfo.id()).isEqualTo(pollEntity.getId()), + () -> assertThat(pollInfo.prosTeamName()).isEqualTo(pollEntity.getProsTeamName()), + () -> assertThat(pollInfo.consTeamName()).isEqualTo(pollEntity.getConsTeamName()), + () -> assertThat(pollInfo.status()).isEqualTo(pollEntity.getStatus()), + () -> assertThat(pollInfo.totalCount()).isEqualTo(3L), + () -> assertThat(pollInfo.prosCount()).isEqualTo(2L), + () -> assertThat(pollInfo.consCount()).isEqualTo(1L) + ); + } + } + + @Nested + class FinishPoll { + + @Test + void 선거를_완료상태로_변경한다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + pollGenerator.generate(table, PollStatus.PROGRESS); + + PollInfoResponse pollInfo = pollService.finishPoll(table.getId(), member); + + assertThat(pollInfo.status()).isEqualTo(PollStatus.DONE); + } + } +} From 7a8c985956f10301fa288e759987284e75470a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B1=B4=EC=9A=B0?= Date: Thu, 31 Jul 2025 03:41:30 +0900 Subject: [PATCH 22/27] =?UTF-8?q?[FEAT]=20=ED=88=AC=ED=91=9C=EC=9E=90=20AP?= =?UTF-8?q?I=20=EA=B5=AC=ED=98=84=20(#205)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/ErrorDecoderConfig.java | 33 ++++ .../controller/poll/VoteController.java | 37 ++++ .../com/debatetimer/domain/member/Member.java | 2 +- .../com/debatetimer/domain/poll/Poll.java | 4 + .../debatetimer/domain/poll/PollStatus.java | 4 + .../com/debatetimer/domain/poll/Vote.java | 4 + .../poll/PollDomainRepository.java | 17 +- .../poll/VoteDomainRepository.java | 28 +++ .../CustomizeTableDomainRepository.java | 2 +- .../dto/poll/request/VoteRequest.java | 13 ++ .../dto/poll/response/VoteCreateResponse.java | 16 ++ .../poll/response/VoterPollInfoResponse.java | 31 ++++ .../{customize => }/BaseTimeEntity.java | 2 +- .../customize/CustomizeTableEntity.java | 3 +- .../debatetimer/entity/poll/PollEntity.java | 2 +- .../debatetimer/entity/poll/VoteEntity.java | 15 +- .../exception/decoder/H2ErrorDecoder.java | 22 +++ .../exception/decoder/MySqlErrorDecoder.java | 27 +++ .../decoder/RepositoryErrorDecoder.java | 8 + .../exception/errorcode/ClientErrorCode.java | 2 + .../repository/poll/PollRepository.java | 12 ++ .../repository/poll/VoteRepository.java | 2 + .../debatetimer/service/poll/PollService.java | 2 +- .../debatetimer/service/poll/VoteService.java | 56 ++++++ ...add_participate_code_unique_constraint.sql | 5 + .../controller/BaseControllerTest.java | 10 + .../controller/BaseDocumentTest.java | 4 + .../controller/poll/VoteControllerTest.java | 173 ++++++++++++++++++ .../controller/poll/VoteDocumentTest.java | 156 ++++++++++++++++ .../poll/VoteDomainRepositoryTest.java | 62 +++++++ .../exception/decoder/H2ErrorDecoderTest.java | 51 ++++++ .../decoder/MySqlErrorDecoderTest.java | 54 ++++++ .../debatetimer/fixture/VoteGenerator.java | 6 +- .../service/poll/VoteServiceTest.java | 121 ++++++++++++ 34 files changed, 967 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/debatetimer/config/ErrorDecoderConfig.java create mode 100644 src/main/java/com/debatetimer/controller/poll/VoteController.java rename src/main/java/com/debatetimer/domainrepository/{poll => table}/CustomizeTableDomainRepository.java (95%) create mode 100644 src/main/java/com/debatetimer/dto/poll/request/VoteRequest.java create mode 100644 src/main/java/com/debatetimer/dto/poll/response/VoteCreateResponse.java create mode 100644 src/main/java/com/debatetimer/dto/poll/response/VoterPollInfoResponse.java rename src/main/java/com/debatetimer/entity/{customize => }/BaseTimeEntity.java (93%) create mode 100644 src/main/java/com/debatetimer/exception/decoder/H2ErrorDecoder.java create mode 100644 src/main/java/com/debatetimer/exception/decoder/MySqlErrorDecoder.java create mode 100644 src/main/java/com/debatetimer/exception/decoder/RepositoryErrorDecoder.java create mode 100644 src/main/java/com/debatetimer/service/poll/VoteService.java create mode 100644 src/main/resources/db/migration/V12__add_participate_code_unique_constraint.sql create mode 100644 src/test/java/com/debatetimer/controller/poll/VoteControllerTest.java create mode 100644 src/test/java/com/debatetimer/controller/poll/VoteDocumentTest.java create mode 100644 src/test/java/com/debatetimer/exception/decoder/H2ErrorDecoderTest.java create mode 100644 src/test/java/com/debatetimer/exception/decoder/MySqlErrorDecoderTest.java create mode 100644 src/test/java/com/debatetimer/service/poll/VoteServiceTest.java 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/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/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/domainrepository/poll/CustomizeTableDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/table/CustomizeTableDomainRepository.java similarity index 95% rename from src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java rename to src/main/java/com/debatetimer/domainrepository/table/CustomizeTableDomainRepository.java index 850c53bd..f48ffc33 100644 --- a/src/main/java/com/debatetimer/domainrepository/poll/CustomizeTableDomainRepository.java +++ b/src/main/java/com/debatetimer/domainrepository/table/CustomizeTableDomainRepository.java @@ -1,4 +1,4 @@ -package com.debatetimer.domainrepository.poll; +package com.debatetimer.domainrepository.table; import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.member.Member; 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/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/poll/PollService.java b/src/main/java/com/debatetimer/service/poll/PollService.java index a6409fdb..7d21bffb 100644 --- a/src/main/java/com/debatetimer/service/poll/PollService.java +++ b/src/main/java/com/debatetimer/service/poll/PollService.java @@ -4,9 +4,9 @@ import com.debatetimer.domain.member.Member; import com.debatetimer.domain.poll.Poll; import com.debatetimer.domain.poll.VoteInfo; -import com.debatetimer.domainrepository.poll.CustomizeTableDomainRepository; import com.debatetimer.domainrepository.poll.PollDomainRepository; import com.debatetimer.domainrepository.poll.VoteDomainRepository; +import com.debatetimer.domainrepository.table.CustomizeTableDomainRepository; import com.debatetimer.dto.poll.response.PollCreateResponse; import com.debatetimer.dto.poll.response.PollInfoResponse; import lombok.RequiredArgsConstructor; 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/test/java/com/debatetimer/controller/BaseControllerTest.java b/src/test/java/com/debatetimer/controller/BaseControllerTest.java index 83d35dda..c63a61fb 100644 --- a/src/test/java/com/debatetimer/controller/BaseControllerTest.java +++ b/src/test/java/com/debatetimer/controller/BaseControllerTest.java @@ -4,10 +4,12 @@ import com.debatetimer.client.oauth.OAuthClient; 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.dto.poll.request.VoteRequest; import com.debatetimer.fixture.CustomizeTableGenerator; import com.debatetimer.fixture.CustomizeTimeBoxGenerator; import com.debatetimer.fixture.HeaderGenerator; @@ -24,6 +26,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; @@ -90,6 +93,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", "자유 테이블") 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/poll/VoteControllerTest.java b/src/test/java/com/debatetimer/controller/poll/VoteControllerTest.java new file mode 100644 index 00000000..06276c5b --- /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 = 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, "커찬"); + + 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 = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.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 = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.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 = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.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 = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.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 = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + String participatecode = UUID.randomUUID().toString(); + voteGenerator.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 = customizeTableGenerator.generate(member); + PollEntity alreadyDonePoll = pollGenerator.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/domainrepository/poll/VoteDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java index 068e782e..11ed5d33 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; @@ -42,4 +48,60 @@ class GetVoteInfo { } } + @Nested + class isExists { + + @Test + void 이미_참여한_투표인지_알_수_있다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity table = customizeTableGenerator.generate(member); + PollEntity alreadyParticipatedPoll = pollGenerator.generate(table, PollStatus.PROGRESS); + PollEntity notYetParticipatedPoll = pollGenerator.generate(table, PollStatus.PROGRESS); + ParticipateCode participateCode = new ParticipateCode(UUID.randomUUID().toString()); + voteGenerator.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 = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.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 = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + String participateCode = UUID.randomUUID().toString(); + voteGenerator.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/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/VoteGenerator.java b/src/test/java/com/debatetimer/fixture/VoteGenerator.java index 9efdd80c..ed97b1f1 100644 --- a/src/test/java/com/debatetimer/fixture/VoteGenerator.java +++ b/src/test/java/com/debatetimer/fixture/VoteGenerator.java @@ -17,7 +17,11 @@ public VoteGenerator(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/service/poll/VoteServiceTest.java b/src/test/java/com/debatetimer/service/poll/VoteServiceTest.java new file mode 100644 index 00000000..52626c73 --- /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 = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + voteGenerator.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 = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); + String participateCode = UUID.randomUUID().toString(); + voteGenerator.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 = customizeTableGenerator.generate(member); + PollEntity pollEntity = pollGenerator.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 = customizeTableGenerator.generate(member); + PollEntity alreadyDonePoll = pollGenerator.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 = 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, "커찬"); + + 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) + ); + } + } +} From 69d839dd60b98eec522e09951db20ae4848d9c91 Mon Sep 17 00:00:00 2001 From: leegwichan Date: Thu, 31 Jul 2025 11:05:59 +0900 Subject: [PATCH 23/27] =?UTF-8?q?chore:=20=EC=9B=8C=ED=81=AC=ED=94=8C?= =?UTF-8?q?=EB=A1=9C=EC=9A=B0=20=EC=88=98=EB=8F=99=20=EC=8B=A4=ED=96=89=20?= =?UTF-8?q?=ED=97=88=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Dev_CD.yml | 1 + .github/workflows/Prod_CD.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/Dev_CD.yml b/.github/workflows/Dev_CD.yml index 8718cc23..3f9f229d 100644 --- a/.github/workflows/Dev_CD.yml +++ b/.github/workflows/Dev_CD.yml @@ -1,6 +1,7 @@ name: dev-cd on: + workflow_dispatch: push: branches: - "develop" diff --git a/.github/workflows/Prod_CD.yml b/.github/workflows/Prod_CD.yml index 20e0c517..2efb53f3 100644 --- a/.github/workflows/Prod_CD.yml +++ b/.github/workflows/Prod_CD.yml @@ -1,6 +1,7 @@ name: prod-cd on: + workflow_dispatch: push: branches: - "main" From 77ad2845b690047d394e4ff9d113ed2d7403cb14 Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Thu, 31 Jul 2025 11:09:46 +0900 Subject: [PATCH 24/27] =?UTF-8?q?[CHORE]=20=EC=A0=95=EC=83=81=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=20=ED=99=95=EC=9D=B8=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=20=EC=B2=B4=ED=81=AC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#211)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: coli --- .github/workflows/Dev_CD.yml | 17 +++++++++++++---- .github/workflows/Prod_CD.yml | 15 ++++++++++++--- scripts/dev/health-check.sh | 31 +++++++++++++++++++++++++++++++ scripts/prod/health-check.sh | 31 +++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 scripts/dev/health-check.sh create mode 100644 scripts/prod/health-check.sh diff --git a/.github/workflows/Dev_CD.yml b/.github/workflows/Dev_CD.yml index 3f9f229d..e9a4e830 100644 --- a/.github/workflows/Dev_CD.yml +++ b/.github/workflows/Dev_CD.yml @@ -11,10 +11,8 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout Develop Branch + - name: Checkout Branch uses: actions/checkout@v4 - with: - ref: "develop" - name: Setting dev-secret.yml run: | @@ -67,7 +65,18 @@ jobs: uses: actions/download-artifact@v4 with: name: deploy-scripts - path: ~/app/scripts + path: ~/app/scripts/ - name: Replace application to latest run: sudo sh ~/app/scripts/replace-new-version.sh + + - name: Health Check + run: sh ~/app/scripts/health-check.sh + + - name: Send Discord Alert on Failure + if: failure() + run: | + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\":x: [DEV] 배포 실패! 확인이 필요합니다.\n링크: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \ + ${{ secrets.DISCORD_WEB_HOOK }} diff --git a/.github/workflows/Prod_CD.yml b/.github/workflows/Prod_CD.yml index 2efb53f3..cb10ee58 100644 --- a/.github/workflows/Prod_CD.yml +++ b/.github/workflows/Prod_CD.yml @@ -13,8 +13,6 @@ jobs: steps: - name: Checkout Develop Branch uses: actions/checkout@v4 - with: - ref: "main" - name: Setting prod-secret.yml run: | @@ -67,7 +65,18 @@ jobs: uses: actions/download-artifact@v4 with: name: deploy-scripts - path: ~/app/scripts + path: ~/app/scripts/ - name: Replace application to latest run: sudo sh ~/app/scripts/replace-new-version.sh + + - name: Health Check + run: sh ~/app/scripts/health-check.sh + + - name: Send Discord Alert on Failure + if: failure() + run: | + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\":warning: :warning: [PROD] 배포 실패! :warning: :warning: \n링크: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \ + ${{ secrets.DISCORD_WEB_HOOK }} diff --git a/scripts/dev/health-check.sh b/scripts/dev/health-check.sh new file mode 100644 index 00000000..96fd66fc --- /dev/null +++ b/scripts/dev/health-check.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# 최대 반복 횟수 +MAX_RETRIES=60 + +# 성공 상태 코드와 요청 URL +SUCCESS_STATUS=200 +HEALTH_CHECK_URL="http://localhost:8083/monitoring/health" + +# 반복 시작 +i=1 +while [ "$i" -le "$MAX_RETRIES" ]; do + # HTTP 요청 보내기 + RESPONSE_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL") + echo "[TRY $i] StatusCode : $RESPONSE_STATUS " + # 상태 코드 확인 + if [ "$RESPONSE_STATUS" -eq "$SUCCESS_STATUS" ]; then + echo "Success: Received $SUCCESS_STATUS status code on attempt $i." + exit 0 + fi + + # 2초 대기 + sleep 2 + + # 반복 변수 증가 + i=$((i + 1)) +done + +# 실패 메시지 +echo "Failure: Did not receive $SUCCESS_STATUS status code within $MAX_RETRIES attempts." +exit 1 diff --git a/scripts/prod/health-check.sh b/scripts/prod/health-check.sh new file mode 100644 index 00000000..96fd66fc --- /dev/null +++ b/scripts/prod/health-check.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# 최대 반복 횟수 +MAX_RETRIES=60 + +# 성공 상태 코드와 요청 URL +SUCCESS_STATUS=200 +HEALTH_CHECK_URL="http://localhost:8083/monitoring/health" + +# 반복 시작 +i=1 +while [ "$i" -le "$MAX_RETRIES" ]; do + # HTTP 요청 보내기 + RESPONSE_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL") + echo "[TRY $i] StatusCode : $RESPONSE_STATUS " + # 상태 코드 확인 + if [ "$RESPONSE_STATUS" -eq "$SUCCESS_STATUS" ]; then + echo "Success: Received $SUCCESS_STATUS status code on attempt $i." + exit 0 + fi + + # 2초 대기 + sleep 2 + + # 반복 변수 증가 + i=$((i + 1)) +done + +# 실패 메시지 +echo "Failure: Did not receive $SUCCESS_STATUS status code within $MAX_RETRIES attempts." +exit 1 From 4812031f7852d7489ae474278c918e06bbef4514 Mon Sep 17 00:00:00 2001 From: SANGHUN OH <121424793+unifolio0@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:21:26 +0900 Subject: [PATCH 25/27] =?UTF-8?q?[FEAT]=20=EC=A2=85=EC=86=8C=EB=A6=AC=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20API=20V2=20=EC=A0=9C=EC=9E=91=20(#206)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../debatetimer/domain/customize/Bell.java | 12 +- .../domain/customize/BellType.java | 25 ++ .../CustomizeTableDomainRepository.java} | 77 +++--- .../table/CustomizeTableDomainRepository.java | 24 -- .../dto/customize/request/BellRequest.java | 6 +- .../request/CustomizeTableCreateRequest.java | 17 +- .../dto/customize/response/BellResponse.java | 4 +- .../entity/customize/BellEntity.java | 14 +- .../service/customize/CustomizeService.java | 55 ++-- .../debatetimer/service/poll/PollService.java | 2 +- .../V13__add_bell_type_into_bell.sql | 2 + .../controller/BaseControllerTest.java | 20 +- .../customize/CustomizeControllerTest.java | 34 +-- .../customize/CustomizeDocumentTest.java | 63 +++-- .../controller/poll/PollControllerTest.java | 16 +- .../controller/poll/VoteControllerTest.java | 44 ++-- .../domain/customize/BellTest.java | 18 +- .../domain/customize/BellTypeTest.java | 36 +++ .../BaseDomainRepositoryTest.java | 46 +++- .../CustomizeTableDomainRepositoryTest.java | 159 ++++++++++++ .../poll/PollDomainRepositoryTest.java | 10 +- .../poll/VoteDomainRepositoryTest.java | 28 +- .../entity/customize/BellEntityTest.java | 5 +- .../fixture/domain/BellGenerator.java | 23 ++ .../domain/CustomizeTableGenerator.java | 36 +++ .../domain/CustomizeTimeBoxGenerator.java | 32 +++ .../BellEntityGenerator.java} | 11 +- .../CustomizeTableEntityGenerator.java} | 6 +- .../CustomizeTimeBoxEntityGenerator.java} | 6 +- .../fixture/{ => entity}/MemberGenerator.java | 2 +- .../PollEntityGenerator.java} | 6 +- .../VoteEntityGenerator.java} | 6 +- .../repository/BaseRepositoryTest.java | 20 +- .../customize/BellRepositoryTest.java | 15 +- .../CustomizeTableEntityRepositoryTest.java | 10 +- .../CustomizeTimeBoxRepositoryTest.java | 32 +-- .../debatetimer/service/BaseServiceTest.java | 22 +- .../customize/CustomizeServiceTest.java | 73 ++++-- .../customize/CustomizeServiceV2Test.java | 243 ------------------ .../service/member/MemberServiceTest.java | 6 +- .../service/poll/PollServiceTest.java | 16 +- .../service/poll/VoteServiceTest.java | 30 +-- 42 files changed, 691 insertions(+), 621 deletions(-) create mode 100644 src/main/java/com/debatetimer/domain/customize/BellType.java rename src/main/java/com/debatetimer/{service/customize/CustomizeServiceV2.java => domainrepository/customize/CustomizeTableDomainRepository.java} (61%) delete mode 100644 src/main/java/com/debatetimer/domainrepository/table/CustomizeTableDomainRepository.java create mode 100644 src/main/resources/db/migration/V13__add_bell_type_into_bell.sql create mode 100644 src/test/java/com/debatetimer/domain/customize/BellTypeTest.java create mode 100644 src/test/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepositoryTest.java create mode 100644 src/test/java/com/debatetimer/fixture/domain/BellGenerator.java create mode 100644 src/test/java/com/debatetimer/fixture/domain/CustomizeTableGenerator.java create mode 100644 src/test/java/com/debatetimer/fixture/domain/CustomizeTimeBoxGenerator.java rename src/test/java/com/debatetimer/fixture/{BellGenerator.java => entity/BellEntityGenerator.java} (52%) rename src/test/java/com/debatetimer/fixture/{CustomizeTableGenerator.java => entity/CustomizeTableEntityGenerator.java} (84%) rename src/test/java/com/debatetimer/fixture/{CustomizeTimeBoxGenerator.java => entity/CustomizeTimeBoxEntityGenerator.java} (88%) rename src/test/java/com/debatetimer/fixture/{ => entity}/MemberGenerator.java (92%) rename src/test/java/com/debatetimer/fixture/{PollGenerator.java => entity/PollEntityGenerator.java} (85%) rename src/test/java/com/debatetimer/fixture/{VoteGenerator.java => entity/VoteEntityGenerator.java} (84%) delete mode 100644 src/test/java/com/debatetimer/service/customize/CustomizeServiceV2Test.java 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/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/table/CustomizeTableDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/table/CustomizeTableDomainRepository.java deleted file mode 100644 index f48ffc33..00000000 --- a/src/main/java/com/debatetimer/domainrepository/table/CustomizeTableDomainRepository.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.debatetimer.domainrepository.table; - -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/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/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/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 7d21bffb..cfa021b9 100644 --- a/src/main/java/com/debatetimer/service/poll/PollService.java +++ b/src/main/java/com/debatetimer/service/poll/PollService.java @@ -4,9 +4,9 @@ import com.debatetimer.domain.member.Member; import com.debatetimer.domain.poll.Poll; import com.debatetimer.domain.poll.VoteInfo; +import com.debatetimer.domainrepository.customize.CustomizeTableDomainRepository; import com.debatetimer.domainrepository.poll.PollDomainRepository; import com.debatetimer.domainrepository.poll.VoteDomainRepository; -import com.debatetimer.domainrepository.table.CustomizeTableDomainRepository; import com.debatetimer.dto.poll.response.PollCreateResponse; import com.debatetimer.dto.poll.response.PollInfoResponse; import lombok.RequiredArgsConstructor; 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 c63a61fb..504c5dcd 100644 --- a/src/test/java/com/debatetimer/controller/BaseControllerTest.java +++ b/src/test/java/com/debatetimer/controller/BaseControllerTest.java @@ -2,6 +2,7 @@ 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; @@ -10,13 +11,13 @@ import com.debatetimer.dto.customize.request.CustomizeTableInfoCreateRequest; import com.debatetimer.dto.customize.request.CustomizeTimeBoxCreateRequest; import com.debatetimer.dto.poll.request.VoteRequest; -import com.debatetimer.fixture.CustomizeTableGenerator; -import com.debatetimer.fixture.CustomizeTimeBoxGenerator; import com.debatetimer.fixture.HeaderGenerator; -import com.debatetimer.fixture.MemberGenerator; -import com.debatetimer.fixture.PollGenerator; import com.debatetimer.fixture.TokenGenerator; -import com.debatetimer.fixture.VoteGenerator; +import com.debatetimer.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; @@ -45,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; @@ -124,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/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 index 06276c5b..19fe3fdd 100644 --- a/src/test/java/com/debatetimer/controller/poll/VoteControllerTest.java +++ b/src/test/java/com/debatetimer/controller/poll/VoteControllerTest.java @@ -28,11 +28,11 @@ class GetVotersPollInfo { @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, "커찬"); VoterPollInfoResponse response = given() .contentType(ContentType.JSON) @@ -60,9 +60,9 @@ class VotePoll { @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, "콜리"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); VoteRequest voteRequest = getVoteRequestBuilder().sample(); VoteCreateResponse response = given() @@ -84,9 +84,9 @@ class VotePoll { @NullAndEmptyAndBlankSource void 투표_시_이름은_널이거나_빈_문자열일_수_없다(String invalidName) { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); - voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); VoteRequest voteRequest = getVoteRequestBuilder() .set("name", invalidName) .sample(); @@ -102,9 +102,9 @@ class VotePoll { @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, "콜리"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); VoteRequest voteRequest = getVoteRequestBuilder() .set("team", null) .sample(); @@ -121,9 +121,9 @@ class VotePoll { @NullAndEmptyAndBlankSource void 투표_시_참여코드는_널이거나_빈_문자열일_수_없다(String participateCode) { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - PollEntity pollEntity = pollGenerator.generate(table, PollStatus.PROGRESS); - voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity pollEntity = pollEntityGenerator.generate(table, PollStatus.PROGRESS); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리"); VoteRequest voteRequest = getVoteRequestBuilder() .set("participateCode", participateCode) .sample(); @@ -139,10 +139,10 @@ class VotePoll { @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); String participatecode = UUID.randomUUID().toString(); - voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리", participatecode); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리", participatecode); VoteRequest voteRequest = getVoteRequestBuilder() .set("participateCode", participatecode) .sample(); @@ -158,8 +158,8 @@ class VotePoll { @Test void 끝난_선거에_투표_할_수_없다() { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - PollEntity alreadyDonePoll = pollGenerator.generate(table, PollStatus.DONE); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity alreadyDonePoll = pollEntityGenerator.generate(table, PollStatus.DONE); VoteRequest voteRequest = getVoteRequestBuilder().sample(); given() 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 11ed5d33..1aea13bb 100644 --- a/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java +++ b/src/test/java/com/debatetimer/domainrepository/poll/VoteDomainRepositoryTest.java @@ -31,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()); @@ -54,11 +54,11 @@ class isExists { @Test void 이미_참여한_투표인지_알_수_있다() { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - PollEntity alreadyParticipatedPoll = pollGenerator.generate(table, PollStatus.PROGRESS); - PollEntity notYetParticipatedPoll = pollGenerator.generate(table, PollStatus.PROGRESS); + 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()); - voteGenerator.generate(alreadyParticipatedPoll, VoteTeam.PROS, "콜리", participateCode.getValue()); + voteEntityGenerator.generate(alreadyParticipatedPoll, VoteTeam.PROS, "콜리", participateCode.getValue()); boolean participated = voteDomainRepository.isExists(alreadyParticipatedPoll.getId(), participateCode); boolean notYetParticipated = voteDomainRepository.isExists(notYetParticipatedPoll.getId(), @@ -77,8 +77,8 @@ class Save { @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); Vote vote = new Vote(pollEntity.getId(), VoteTeam.PROS, "콜리", UUID.randomUUID().toString()); Vote savedVote = voteDomainRepository.save(vote); @@ -93,10 +93,10 @@ class Save { @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); String participateCode = UUID.randomUUID().toString(); - voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리", participateCode); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리", participateCode); Vote vote = new Vote(pollEntity.getId(), VoteTeam.PROS, "콜리", participateCode); assertThatThrownBy(() -> voteDomainRepository.save(vote)) 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/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 84% rename from src/test/java/com/debatetimer/fixture/VoteGenerator.java rename to src/test/java/com/debatetimer/fixture/entity/VoteEntityGenerator.java index ed97b1f1..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,11 +8,11 @@ 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; } 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 index 52626c73..e2f26ecb 100644 --- a/src/test/java/com/debatetimer/service/poll/VoteServiceTest.java +++ b/src/test/java/com/debatetimer/service/poll/VoteServiceTest.java @@ -35,9 +35,9 @@ class Vote { @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, "콜리"); + 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); @@ -53,10 +53,10 @@ class Vote { @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); String participateCode = UUID.randomUUID().toString(); - voteGenerator.generate(pollEntity, VoteTeam.PROS, "콜리", participateCode); + voteEntityGenerator.generate(pollEntity, VoteTeam.PROS, "콜리", participateCode); VoteRequest voteRequest = new VoteRequest("콜리", participateCode, VoteTeam.PROS); assertThatThrownBy(() -> voteService.vote(pollEntity.getId(), voteRequest)) @@ -67,8 +67,8 @@ class Vote { @Test void 투표_동시성_이슈에_단일_표만_유효하게_취급한다() throws InterruptedException { 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); String participateCode = UUID.randomUUID().toString(); VoteRequest voteRequest = new VoteRequest("콜리", participateCode, VoteTeam.PROS); @@ -81,8 +81,8 @@ class Vote { @Test void 끝난_선거에_투표_할_수_없다() { Member member = memberGenerator.generate("email@email.com"); - CustomizeTableEntity table = customizeTableGenerator.generate(member); - PollEntity alreadyDonePoll = pollGenerator.generate(table, PollStatus.DONE); + CustomizeTableEntity table = customizeTableEntityGenerator.generate(member); + PollEntity alreadyDonePoll = pollEntityGenerator.generate(table, PollStatus.DONE); String participateCode = UUID.randomUUID().toString(); VoteRequest voteRequest = new VoteRequest("콜리", participateCode, VoteTeam.PROS); @@ -98,11 +98,11 @@ class GetVoterPollInfo { @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, "커찬"); VoterPollInfoResponse response = voteService.getVoterPollInfo(pollEntity.getId()); From 659991c00d7e70a144ece5a37e9a7adef1f15afb Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:50:09 +0900 Subject: [PATCH 26/27] =?UTF-8?q?[HOTFIX]=20CustomizeTimeBoxEntity=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=20time=20=EC=9D=B4=20nullable=20=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#212)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../debatetimer/entity/customize/CustomizeTimeBoxEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java index 9480b05e..09dc6f4c 100644 --- a/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java +++ b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntity.java @@ -119,7 +119,7 @@ public CustomizeTimeBoxEntity(CustomizeTableEntity customizeTable, CustomizeTime this.customizeTable = customizeTable; this.sequence = sequence; this.stance = timeBox.getStance(); - this.time = timeBox.getTime(); + this.time = timeBox.getTime() == null ? timeBox.getTimePerTeam() * 2 : timeBox.getTime(); // TODO : Nullable 의논 this.speaker = timeBox.getSpeaker(); this.speechType = timeBox.getSpeechType(); this.boxType = timeBox.getBoxType(); From ad15ffb6d5cd092dbf53a19465c4128403dafc2a Mon Sep 17 00:00:00 2001 From: Chung-an Lee <44027393+leegwichan@users.noreply.github.com> Date: Mon, 11 Aug 2025 00:40:41 +0900 Subject: [PATCH 27/27] =?UTF-8?q?[REFACTOR]=20CustomizeTimeBoxEntities=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#214)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CustomizeTableDomainRepository.java | 28 +++++++++-- .../response/CustomizeTableResponse.java | 18 -------- .../customize/CustomizeTimeBoxEntities.java | 46 ------------------- .../customize/CustomizeTimeBoxRepository.java | 14 ------ .../CustomizeTableDomainRepositoryTest.java | 37 +++++++++++++-- .../CustomizeTimeBoxEntitiesTest.java | 38 --------------- .../CustomizeTimeBoxEntityGenerator.java | 16 +++++++ 7 files changed, 74 insertions(+), 123 deletions(-) delete mode 100644 src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntities.java delete mode 100644 src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntitiesTest.java diff --git a/src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java b/src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java index de3705b8..2c2e96e7 100644 --- a/src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java +++ b/src/main/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepository.java @@ -5,12 +5,15 @@ import com.debatetimer.domain.member.Member; import com.debatetimer.entity.customize.BellEntity; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import com.debatetimer.repository.customize.BellRepository; import com.debatetimer.repository.customize.CustomizeTableRepository; import com.debatetimer.repository.customize.CustomizeTimeBoxRepository; +import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -55,8 +58,27 @@ 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 timeBoxEntities.toDomain(); + return toCustomizeTimeBoxes(timeBoxEntityList, bellEntityList); + } + + private List toCustomizeTimeBoxes( + List timeBoxEntities, + List bellEntities + ) { + Map> timeBoxIdToBellEntities = bellEntities.stream() + .collect(Collectors.groupingBy(bellEntity -> bellEntity.getCustomizeTimeBox().getId())); + return timeBoxEntities.stream() + .sorted(Comparator.comparing(CustomizeTimeBoxEntity::getSequence)) + .map(timeBoxEntity -> toTimeBox( + timeBoxEntity, + timeBoxIdToBellEntities.getOrDefault(timeBoxEntity.getId(), Collections.emptyList())) + ).toList(); + } + + private CustomizeTimeBox toTimeBox(CustomizeTimeBoxEntity timeBoxEntity, List bellEntities) { + return bellEntities.stream() + .map(BellEntity::toDomain) + .collect(Collectors.collectingAndThen(Collectors.toList(), timeBoxEntity::toDomain)); } @Transactional diff --git a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java index 2309b490..366939d6 100644 --- a/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java +++ b/src/main/java/com/debatetimer/dto/customize/response/CustomizeTableResponse.java @@ -2,28 +2,10 @@ import com.debatetimer.domain.customize.CustomizeTable; import com.debatetimer.domain.customize.CustomizeTimeBox; -import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; -import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import java.util.List; public record CustomizeTableResponse(long id, CustomizeTableInfoResponse info, List table) { - public CustomizeTableResponse( - CustomizeTable customizeTable, - CustomizeTimeBoxEntities customizeTimeBoxes - ) { - this(customizeTable.getId(), new CustomizeTableInfoResponse(customizeTable), - toTimeBoxResponses(customizeTimeBoxes)); - } - - private static List toTimeBoxResponses(CustomizeTimeBoxEntities timeBoxes) { - List customizeTimeBoxes = timeBoxes.getTimeBoxes(); - return customizeTimeBoxes - .stream() - .map(CustomizeTimeBoxResponse::new) - .toList(); - } - public CustomizeTableResponse(CustomizeTable customizeTable, List customizeTimeBoxes) { this(customizeTable.getId(), diff --git a/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntities.java b/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntities.java deleted file mode 100644 index 32f5c383..00000000 --- a/src/main/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntities.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.debatetimer.entity.customize; - -import com.debatetimer.domain.customize.Bell; -import com.debatetimer.domain.customize.CustomizeTimeBox; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import lombok.Getter; - -public class CustomizeTimeBoxEntities { - - private static final Comparator TIME_BOX_COMPARATOR = Comparator - .comparing(CustomizeTimeBoxEntity::getSequence); - - @Getter - private final List timeBoxes; - - private final List bells; - - public CustomizeTimeBoxEntities(List timeBoxes) { - this.timeBoxes = timeBoxes.stream() - .sorted(TIME_BOX_COMPARATOR) - .toList(); - this.bells = Collections.emptyList(); - } - - public CustomizeTimeBoxEntities(List timeBoxes, List bells) { - this.timeBoxes = timeBoxes.stream() - .sorted(TIME_BOX_COMPARATOR) - .toList(); - this.bells = bells; - } - - public List toDomain() { - return timeBoxes.stream() - .map(timebox -> timebox.toDomain(getBells(timebox))) - .toList(); - } - - private List getBells(CustomizeTimeBoxEntity timeBox) { - return bells.stream() - .filter(bell -> bell.isContained(timeBox)) - .map(BellEntity::toDomain) - .toList(); - } -} diff --git a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java index 680391f6..1a0e524d 100644 --- a/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java +++ b/src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.java @@ -1,32 +1,18 @@ package com.debatetimer.repository.customize; import com.debatetimer.entity.customize.CustomizeTableEntity; -import com.debatetimer.entity.customize.CustomizeTimeBoxEntities; import com.debatetimer.entity.customize.CustomizeTimeBoxEntity; import java.util.List; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; -import org.springframework.transaction.annotation.Transactional; public interface CustomizeTimeBoxRepository extends Repository { CustomizeTimeBoxEntity save(CustomizeTimeBoxEntity timeBox); - @Transactional - default List saveAll(List timeBoxes) { - return timeBoxes.stream() - .map(this::save) - .toList(); - } - List findAllByCustomizeTable(CustomizeTableEntity table); - default CustomizeTimeBoxEntities findTableTimeBoxes(CustomizeTableEntity table) { - List timeBoxes = findAllByCustomizeTable(table); - return new CustomizeTimeBoxEntities(timeBoxes); - } - @Query("DELETE FROM CustomizeTimeBoxEntity ctb WHERE ctb.customizeTable.id = :tableId") @Modifying(clearAutomatically = true, flushAutomatically = true) void deleteAllByTable(long tableId); diff --git a/src/test/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepositoryTest.java b/src/test/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepositoryTest.java index e9108935..e1dbbf53 100644 --- a/src/test/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepositoryTest.java +++ b/src/test/java/com/debatetimer/domainrepository/customize/CustomizeTableDomainRepositoryTest.java @@ -68,16 +68,45 @@ class GetByIdAndMember { class GetCustomizeTimeBoxes { @Test - void 테이블의_시간박스를_가져온다() { + 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); + timeBoxEntityGenerator.generate(tableEntity, CustomizeBoxType.NORMAL, 1, 60); + timeBoxEntityGenerator.generate(tableEntity, CustomizeBoxType.NORMAL, 2, 180); + timeBoxEntityGenerator.generate(tableEntity, CustomizeBoxType.NORMAL, 3, 120); List timeBoxes = customizeTableDomainRepository.getCustomizeTimeBoxes( tableEntity.getId(), member); - assertThat(timeBoxes).hasSize(2); + assertThat(timeBoxes).hasSize(3) + .extracting(CustomizeTimeBox::getTime) + .containsExactly(60, 180, 120); + } + + @Test + void 테이블의_시간박스와_벨을_가져온다() { + Member member = memberGenerator.generate("email@email.com"); + CustomizeTableEntity tableEntity = tableEntityGenerator.generate(member); + CustomizeTimeBoxEntity timeBoxEntity1 = timeBoxEntityGenerator.generate(tableEntity, + CustomizeBoxType.NORMAL, 1); + CustomizeTimeBoxEntity timeBoxEntity2 = timeBoxEntityGenerator.generate(tableEntity, + CustomizeBoxType.NORMAL, 2); + bellEntityGenerator.generate(timeBoxEntity1, BellType.BEFORE_END, 20, 1); + bellEntityGenerator.generate(timeBoxEntity1, BellType.BEFORE_END, 30, 1); + bellEntityGenerator.generate(timeBoxEntity2, BellType.BEFORE_END, 10, 1); + + List timeBoxes = customizeTableDomainRepository.getCustomizeTimeBoxes( + tableEntity.getId(), member); + + assertAll( + () -> assertThat(timeBoxes).hasSize(2), + () -> assertThat(timeBoxes.get(0).getBells()).hasSize(2) + .extracting(Bell::getTime) + .containsExactly(20, 30), + () -> assertThat(timeBoxes.get(1).getBells()).hasSize(1) + .extracting(Bell::getTime) + .containsExactlyInAnyOrder(10) + ); } } diff --git a/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntitiesTest.java b/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntitiesTest.java deleted file mode 100644 index 48d0fb16..00000000 --- a/src/test/java/com/debatetimer/entity/customize/CustomizeTimeBoxEntitiesTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.debatetimer.entity.customize; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.debatetimer.domain.customize.CustomizeBoxType; -import com.debatetimer.domain.customize.CustomizeTable; -import com.debatetimer.domain.customize.Stance; -import com.debatetimer.domain.member.Member; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -class CustomizeTimeBoxEntitiesTest { - - @Nested - class SortedBySequence { - - @Test - void 타임박스의_순서에_따라_정렬된다() { - Member member = new Member("default@gmail.com"); - CustomizeTable table = new CustomizeTable(member, "토론 테이블", "주제", "찬성", - "반대", true, true, LocalDateTime.now()); - CustomizeTableEntity testTable = new CustomizeTableEntity(table); - CustomizeTimeBoxEntity firstBox = new CustomizeTimeBoxEntity(testTable, 1, Stance.PROS, "입론", - CustomizeBoxType.NORMAL, 300, "콜리"); - CustomizeTimeBoxEntity secondBox = new CustomizeTimeBoxEntity(testTable, 2, Stance.PROS, "입론", - CustomizeBoxType.NORMAL, 300, "콜리2"); - List timeBoxes = new ArrayList<>(Arrays.asList(secondBox, firstBox)); - - CustomizeTimeBoxEntities actual = new CustomizeTimeBoxEntities(timeBoxes); - - assertThat(actual.getTimeBoxes()).containsExactly(firstBox, secondBox); - } - } -} diff --git a/src/test/java/com/debatetimer/fixture/entity/CustomizeTimeBoxEntityGenerator.java b/src/test/java/com/debatetimer/fixture/entity/CustomizeTimeBoxEntityGenerator.java index 7b2ca578..fd0d8bf6 100644 --- a/src/test/java/com/debatetimer/fixture/entity/CustomizeTimeBoxEntityGenerator.java +++ b/src/test/java/com/debatetimer/fixture/entity/CustomizeTimeBoxEntityGenerator.java @@ -29,6 +29,22 @@ public CustomizeTimeBoxEntity generate(CustomizeTableEntity testTable, Customize return customizeTimeBoxRepository.save(timeBox); } + public CustomizeTimeBoxEntity generate(CustomizeTableEntity testTable, + CustomizeBoxType boxType, + int sequence, + int time) { + CustomizeTimeBoxEntity timeBox = new CustomizeTimeBoxEntity( + testTable, + sequence, + Stance.PROS, + "입론", + boxType, + time, + "콜리" + ); + return customizeTimeBoxRepository.save(timeBox); + } + public CustomizeTimeBoxEntity generateNotExistSpeaker(CustomizeTableEntity testTable, CustomizeBoxType boxType, int sequence) { CustomizeTimeBoxEntity timeBox = new CustomizeTimeBoxEntity(