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 ca5cb7c2..224e18db 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,22 +14,22 @@ @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() { } @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 +41,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/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/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/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/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/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/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/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 51cbde50..4abe1d47 100644 --- a/src/main/java/com/debatetimer/dto/member/TableResponses.java +++ b/src/main/java/com/debatetimer/dto/member/TableResponses.java @@ -1,33 +1,20 @@ package com.debatetimer.dto.member; -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) { - private static final Comparator DEBATE_TABLE_COMPARATOR = Comparator - .comparing(DebateTable::getUsedAt) + private static final Comparator DEBATE_TABLE_COMPARATOR = Comparator + .comparing(CustomizeTable::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/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/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/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..cd341416 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, @@ -50,6 +48,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, "입력한 값의 타입이 잘못되었습니다."), @@ -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/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..295c6ef0 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; @@ -22,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 @@ -100,6 +102,18 @@ 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); + 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/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/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/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/customize/CustomizeService.java b/src/main/java/com/debatetimer/service/customize/CustomizeService.java index fc6afe60..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; @@ -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 @@ -28,37 +27,40 @@ 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 = getOwnerTable(tableId, member.getId()); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); + CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + CustomizeTimeBoxes 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); + 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 = getOwnerTable(tableId, member.getId()); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); + CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + CustomizeTimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); table.updateUsedAt(); return new CustomizeTableResponse(table, timeBoxes); @@ -66,30 +68,19 @@ public CustomizeTableResponse updateUsedAt(long tableId, Member member) { @Transactional public void deleteTable(long tableId, Member member) { - CustomizeTable table = getOwnerTable(tableId, member.getId()); - TimeBoxes timeBoxes = timeBoxRepository.findTableTimeBoxes(table); + CustomizeTable table = tableRepository.findByIdAndMember(tableId, member) + .orElseThrow(() -> new DTClientErrorException(ClientErrorCode.TABLE_NOT_FOUND)); + 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); - } - - 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); - } + CustomizeTimeBoxes customizeTimeBoxes = tableCreateRequest.toTimeBoxes(table); + 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 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/main/resources/logging/log4j2-dev.yml b/src/main/resources/logging/log4j2-dev.yml index 5225f07e..71ab44f3 100644 --- a/src/main/resources/logging/log4j2-dev.yml +++ b/src/main/resources/logging/log4j2-dev.yml @@ -5,15 +5,20 @@ 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" - PatternLayout: - pattern: "%d{yyyy-MM-dd HH:mm:ss} [%X{requestId}] [%thread] [%-5level] %logger{35} - %msg%n" + filePattern: "${log-dir}/logfile-%d{yyyy-MM-dd}.%i.log.gz" + JsonLayout: + complete: false + compact: true + eventEol: true + properties: true + stacktraceAsString: true + objectMessageAsJsonObject: true immediateFlush: false #false로 설정되어야 Async로 buffer에 저장됨 Policies: @@ -29,7 +34,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..a2e5f0f6 100644 --- a/src/main/resources/logging/log4j2-prod.yml +++ b/src/main/resources/logging/log4j2-prod.yml @@ -5,15 +5,20 @@ 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" - PatternLayout: - pattern: "%d{yyyy-MM-dd HH:mm:ss} [%X{requestId}] [%thread] [%-5level] %logger{35} - %msg%n" + filePattern: "${log-dir}/logfile-%d{yyyy-MM-dd}.%i.log.gz" + JsonLayout: + complete: false + compact: true + eventEol: true + properties: true + stacktraceAsString: true + objectMessageAsJsonObject: true immediateFlush: false #false로 설정되어야 Async로 buffer에 저장됨 Policies: 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); + } + } +} diff --git a/src/test/java/com/debatetimer/controller/BaseControllerTest.java b/src/test/java/com/debatetimer/controller/BaseControllerTest.java index b0f6de9e..0ccdab51 100644 --- a/src/test/java/com/debatetimer/controller/BaseControllerTest.java +++ b/src/test/java/com/debatetimer/controller/BaseControllerTest.java @@ -2,15 +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.ParliamentaryTableGenerator; -import com.debatetimer.fixture.ParliamentaryTimeBoxGenerator; import com.debatetimer.fixture.TokenGenerator; import com.debatetimer.repository.customize.CustomizeTableRepository; -import com.debatetimer.repository.parliamentary.ParliamentaryTableRepository; +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; @@ -27,21 +32,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; @@ -62,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; @@ -74,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/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/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/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/controller/member/MemberControllerTest.java b/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java index 0028d5aa..b0a59bca 100644 --- a/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java +++ b/src/test/java/com/debatetimer/controller/member/MemberControllerTest.java @@ -6,15 +6,20 @@ 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; 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 { @@ -24,7 +29,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 +41,7 @@ class GetTables { .then().statusCode(200) .extract().as(TableResponses.class); - assertThat(response.tables()).hasSize(2); + assertThat(response.tables()).hasSize(1); } } @@ -47,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/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/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/DebateTableTest.java b/src/test/java/com/debatetimer/domain/DebateTableTest.java deleted file mode 100644 index 3035f0c8..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()).isEqualTo(false), - () -> assertThat(table.isFinishBell()).isEqualTo(false) - ); - } - - @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/TimeBoxesTest.java b/src/test/java/com/debatetimer/domain/TimeBoxesTest.java deleted file mode 100644 index c3a80805..00000000 --- a/src/test/java/com/debatetimer/domain/TimeBoxesTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.debatetimer.domain; - -import static org.assertj.core.api.Assertions.assertThat; - -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; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -class TimeBoxesTest { - - @Nested - 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)); - - TimeBoxes actual = new TimeBoxes<>(timeBoxes); - - assertThat(actual.getTimeBoxes()).containsExactly(firstBox, secondBox); - } - } -} 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/customize/CustomizeTimeBoxesTest.java b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java new file mode 100644 index 00000000..f37f835f --- /dev/null +++ b/src/test/java/com/debatetimer/domain/customize/CustomizeTimeBoxesTest.java @@ -0,0 +1,34 @@ +package com.debatetimer.domain.customize; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.debatetimer.domain.Stance; +import com.debatetimer.domain.member.Member; +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 CustomizeTimeBoxesTest { + + @Nested + class SortedBySequence { + + @Test + void 타임박스의_순서에_따라_정렬된다() { + Member member = new Member("default@gmail.com"); + 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)); + + CustomizeTimeBoxes actual = new CustomizeTimeBoxes(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/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/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/customize/CustomizeTableRepositoryTest.java b/src/test/java/com/debatetimer/repository/customize/CustomizeTableRepositoryTest.java index 049387ff..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; @@ -27,32 +24,11 @@ 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); 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/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); 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/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()); } } } 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(); - } - } -}