From ab449307c1a76e78c9dbfac4ece1a7d1b083e993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=ED=98=B8?= <66300965+kdkdhoho@users.noreply.github.com> Date: Mon, 20 Jan 2025 19:58:05 +0900 Subject: [PATCH] =?UTF-8?q?test:=20=EB=8C=93=EA=B8=80=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=20=EC=8B=9C,=20=EB=A9=98=EC=85=98=20ID=EC=97=90=20Null?= =?UTF-8?q?=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20(#338)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: spring-validation 의존성 추가 (#337) * test: 댓글 작성 시, 멘션 ID에 Null일 경우 테스트 코드 작성 (#337) --- build.gradle | 1 + .../listywave/common/exception/ErrorCode.java | 1 + .../exception/GlobalExceptionHandler.java | 35 +++++++++++++------ .../controller/CommentController.java | 3 +- .../request/comment/CommentCreateRequest.java | 3 +- .../comment/CommentAcceptanceTest.java | 19 ++++++++++ 6 files changed, 49 insertions(+), 13 deletions(-) diff --git a/build.gradle b/build.gradle index 4df9bf86..08b189b7 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,7 @@ dependencies { annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-validation' // jwt implementation 'io.jsonwebtoken:jjwt-api:0.12.4' diff --git a/src/main/java/com/listywave/common/exception/ErrorCode.java b/src/main/java/com/listywave/common/exception/ErrorCode.java index 385b7daa..31031edc 100644 --- a/src/main/java/com/listywave/common/exception/ErrorCode.java +++ b/src/main/java/com/listywave/common/exception/ErrorCode.java @@ -24,6 +24,7 @@ public enum ErrorCode { // Http Request METHOD_ARGUMENT_TYPE_MISMATCH(BAD_REQUEST, "요청 한 값 타입이 잘못되어 binding에 실패하였습니다."), + METHOD_ARGUMENT_NOT_VALID_EXCEPTION(BAD_REQUEST, "요청에 담긴 값에 문제가 있습니다."), RESOURCE_NOT_FOUND(NOT_FOUND, "대상이 존재하지 않습니다."), RESOURCES_EMPTY(NOT_FOUND, "해당 대상들이 존재하지 않습니다."), ELASTICSEARCH_REQUEST_FAILED(BAD_REQUEST, "Elasticsearch 검색 요청에 실패했습니다."), diff --git a/src/main/java/com/listywave/common/exception/GlobalExceptionHandler.java b/src/main/java/com/listywave/common/exception/GlobalExceptionHandler.java index bb6ca061..8a733c4c 100644 --- a/src/main/java/com/listywave/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/listywave/common/exception/GlobalExceptionHandler.java @@ -1,20 +1,23 @@ package com.listywave.common.exception; import static com.listywave.common.exception.ErrorCode.INVALID_ACCESS_TOKEN; -import static com.listywave.common.exception.ErrorCode.METHOD_ARGUMENT_TYPE_MISMATCH; -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; +import static com.listywave.common.exception.ErrorCode.METHOD_ARGUMENT_NOT_VALID_EXCEPTION; import static org.springframework.http.HttpStatus.UNAUTHORIZED; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.security.SignatureException; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @Slf4j @@ -32,20 +35,30 @@ ResponseEntity handleCustomException(CustomException e) { @ExceptionHandler(Exception.class) protected ResponseEntity handleException(Exception e) { log.error("[InternalServerError] : {}", e.getMessage(), e); - return ResponseEntity.status(INTERNAL_SERVER_ERROR).body(e.getMessage()); + return ResponseEntity.internalServerError().body(e.getMessage()); } @ExceptionHandler(IllegalArgumentException.class) ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) { log.error("[IllegalArgumentException] : {}", e.getMessage(), e); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); + return ResponseEntity.badRequest().body(e.getMessage()); } - @ExceptionHandler(MethodArgumentTypeMismatchException.class) - ResponseEntity handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { - log.error("[MethodArgumentTypeMismatchException] : {}", e.getMessage(), e); - CustomException customException = new CustomException(METHOD_ARGUMENT_TYPE_MISMATCH); - return ErrorResponse.toResponseEntity(customException); + @Override + protected ResponseEntity handleMethodArgumentNotValid( + MethodArgumentNotValidException e, + HttpHeaders headers, + HttpStatusCode status, + WebRequest request + ) { + log.error("[MethodArgumentNotValidException] : {}", e.getMessage(), e); + String errorMessage = e.getFieldErrors() + .stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.joining(", ")); + ErrorCode errorCode = METHOD_ARGUMENT_NOT_VALID_EXCEPTION; + ErrorResponse errorResponse = new ErrorResponse(status.value(), errorMessage, errorCode.name(), errorCode.getDetail(), errorMessage); + return ResponseEntity.badRequest().body(errorResponse); } @ExceptionHandler(SignatureException.class) diff --git a/src/main/java/com/listywave/list/presentation/controller/CommentController.java b/src/main/java/com/listywave/list/presentation/controller/CommentController.java index b45a3bef..a02530c6 100644 --- a/src/main/java/com/listywave/list/presentation/controller/CommentController.java +++ b/src/main/java/com/listywave/list/presentation/controller/CommentController.java @@ -8,6 +8,7 @@ import com.listywave.list.application.service.CommentService; import com.listywave.list.presentation.dto.request.comment.CommentCreateRequest; import com.listywave.list.presentation.dto.request.comment.CommentUpdateRequest; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; @@ -29,7 +30,7 @@ public class CommentController { ResponseEntity create( @PathVariable("listId") Long listId, @Auth Long writerId, - @RequestBody CommentCreateRequest request + @RequestBody @Valid CommentCreateRequest request ) { CommentCreateResponse response = commentService.create(listId, writerId, request.content(), request.mentionIds()); return ResponseEntity.status(CREATED).body(response); diff --git a/src/main/java/com/listywave/list/presentation/dto/request/comment/CommentCreateRequest.java b/src/main/java/com/listywave/list/presentation/dto/request/comment/CommentCreateRequest.java index a2510725..c644f075 100644 --- a/src/main/java/com/listywave/list/presentation/dto/request/comment/CommentCreateRequest.java +++ b/src/main/java/com/listywave/list/presentation/dto/request/comment/CommentCreateRequest.java @@ -1,9 +1,10 @@ package com.listywave.list.presentation.dto.request.comment; +import jakarta.validation.constraints.NotNull; import java.util.List; public record CommentCreateRequest( String content, - List mentionIds + @NotNull(message = "댓글 작성 시 멘션 ID에 Null이 될 수 없습니다.") List mentionIds ) { } diff --git a/src/test/java/com/listywave/acceptance/comment/CommentAcceptanceTest.java b/src/test/java/com/listywave/acceptance/comment/CommentAcceptanceTest.java index 5ba5e1d6..f776d048 100644 --- a/src/test/java/com/listywave/acceptance/comment/CommentAcceptanceTest.java +++ b/src/test/java/com/listywave/acceptance/comment/CommentAcceptanceTest.java @@ -13,6 +13,7 @@ import static com.listywave.user.fixture.UserFixture.정수; import static java.util.Collections.EMPTY_LIST; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.NOT_FOUND; @@ -20,6 +21,7 @@ import com.listywave.list.application.dto.response.CommentFindResponse; import com.listywave.list.application.dto.response.ListCreateResponse; import com.listywave.list.presentation.dto.request.ReplyCreateRequest; +import com.listywave.list.presentation.dto.request.comment.CommentCreateRequest; import com.listywave.list.presentation.dto.request.comment.CommentUpdateRequest; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -49,6 +51,23 @@ public class CommentAcceptanceTest extends AcceptanceTest { assertThat(결과.totalCount()).isEqualTo(11); } + @Test + void 멘션ID를_포함하지_않고_요청을_보내면_예외가_발생한다() { + // given + var 동호 = 회원을_저장한다(동호()); + var 동호_액세스_토큰 = 액세스_토큰을_발급한다(동호); + var 동호_리스트_ID = 리스트_저장_API_호출(가장_좋아하는_견종_TOP3_생성_요청_데이터(List.of()), 동호_액세스_토큰) + .as(ListCreateResponse.class) + .listId(); + + // when + var 멘션ID를_포함하지_않은_요청 = new CommentCreateRequest("댓글", null); + var response = 댓글_저장_API_호출(동호_액세스_토큰, 동호_리스트_ID, 멘션ID를_포함하지_않은_요청); + + // then + assertThat(response.statusCode()).isEqualTo(BAD_REQUEST.value()); + } + @Nested class 댓글_수정 {