-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Comment API에서 도메인명 안바뀐 부분 수정 #609 #610
Merged
BurningFalls
merged 4 commits into
develop
from
feature/#609-fix-domain-name-in-comment-api
Feb 1, 2025
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
backend/src/main/java/com/staccato/comment/service/dto/request/CommentRequestV2.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.staccato.comment.service.dto.request; | ||
|
||
import jakarta.validation.constraints.Min; | ||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.NotNull; | ||
import jakarta.validation.constraints.Size; | ||
|
||
import com.staccato.comment.domain.Comment; | ||
import com.staccato.member.domain.Member; | ||
import com.staccato.moment.domain.Moment; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
|
||
@Schema(description = "댓글 생성 시 요청 형식입니다.") | ||
public record CommentRequestV2( | ||
@Schema(example = "1") | ||
@NotNull(message = "스타카토를 선택해주세요.") | ||
@Min(value = 1L, message = "스타카토 식별자는 양수로 이루어져야 합니다.") | ||
Long staccatoId, | ||
@Schema(example = "예시 댓글 내용") | ||
@NotBlank(message = "댓글 내용을 입력해주세요.") | ||
@Size(max = 500, message = "댓글은 공백 포함 500자 이하로 입력해주세요.") | ||
String content | ||
) { | ||
public Comment toComment(Moment moment, Member member) { | ||
return Comment.builder() | ||
.content(content) | ||
.moment(moment) | ||
.member(member) | ||
.build(); | ||
} | ||
} |
237 changes: 237 additions & 0 deletions
237
backend/src/test/java/com/staccato/comment/controller/CommentControllerV2Test.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
package com.staccato.comment.controller; | ||
|
||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.Mockito.when; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
|
||
import java.util.List; | ||
import java.util.stream.Stream; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
import org.junit.jupiter.params.provider.NullSource; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
|
||
import com.staccato.ControllerTest; | ||
import com.staccato.comment.service.dto.request.CommentRequestV2; | ||
import com.staccato.comment.service.dto.request.CommentUpdateRequest; | ||
import com.staccato.comment.service.dto.response.CommentResponse; | ||
import com.staccato.comment.service.dto.response.CommentResponses; | ||
import com.staccato.exception.ExceptionResponse; | ||
import com.staccato.fixture.Member.MemberFixture; | ||
import com.staccato.fixture.comment.CommentUpdateRequestFixture; | ||
|
||
public class CommentControllerV2Test extends ControllerTest { | ||
private static final int MAX_CONTENT_LENGTH = 500; | ||
private static final long MIN_STACCATO_ID = 1L; | ||
|
||
static Stream<Arguments> invalidCommentRequestProvider() { | ||
return Stream.of( | ||
Arguments.of( | ||
new CommentRequestV2(null, "예시 댓글 내용"), | ||
"스타카토를 선택해주세요." | ||
), | ||
Arguments.of( | ||
new CommentRequestV2(MIN_STACCATO_ID - 1, "예시 댓글 내용"), | ||
"스타카토 식별자는 양수로 이루어져야 합니다." | ||
), | ||
Arguments.of( | ||
new CommentRequestV2(MIN_STACCATO_ID, null), | ||
"댓글 내용을 입력해주세요." | ||
), | ||
Arguments.of( | ||
new CommentRequestV2(MIN_STACCATO_ID, ""), | ||
"댓글 내용을 입력해주세요." | ||
), | ||
Arguments.of( | ||
new CommentRequestV2(MIN_STACCATO_ID, " "), | ||
"댓글 내용을 입력해주세요." | ||
), | ||
Arguments.of( | ||
new CommentRequestV2(MIN_STACCATO_ID, "1".repeat(MAX_CONTENT_LENGTH + 1)), | ||
"댓글은 공백 포함 500자 이하로 입력해주세요." | ||
) | ||
); | ||
} | ||
|
||
@DisplayName("댓글 생성 요청/응답에 대한 직렬화/역직렬화에 성공한다.") | ||
@Test | ||
void createComment() throws Exception { | ||
// given | ||
when(authService.extractFromToken(any())).thenReturn(MemberFixture.create()); | ||
String commentRequest = """ | ||
{ | ||
"staccatoId": 1, | ||
"content": "content" | ||
} | ||
"""; | ||
when(commentService.createComment(any(), any())).thenReturn(1L); | ||
|
||
// when & then | ||
mockMvc.perform(post("/comments/v2") | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.content(commentRequest) | ||
.header(HttpHeaders.AUTHORIZATION, "token")) | ||
.andExpect(status().isCreated()) | ||
.andExpect(header().string(HttpHeaders.LOCATION, "/comments/1")); | ||
} | ||
|
||
@DisplayName("올바르지 않은 형식으로 정보를 입력하면, 댓글을 생성할 수 없다.") | ||
@ParameterizedTest | ||
@MethodSource("invalidCommentRequestProvider") | ||
void createCommentFail(CommentRequestV2 commentRequestV2, String expectedMessage) throws Exception { | ||
// given | ||
when(authService.extractFromToken(any())).thenReturn(MemberFixture.create()); | ||
ExceptionResponse exceptionResponse = new ExceptionResponse(HttpStatus.BAD_REQUEST.toString(), expectedMessage); | ||
|
||
// when & then | ||
mockMvc.perform(post("/comments/v2") | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.content(objectMapper.writeValueAsString(commentRequestV2)) | ||
.header(HttpHeaders.AUTHORIZATION, "token")) | ||
.andExpect(status().isBadRequest()) | ||
.andExpect(content().json(objectMapper.writeValueAsString(exceptionResponse))); | ||
} | ||
|
||
@DisplayName("댓글을 조회했을 때 응답 직렬화에 성공한다.") | ||
@Test | ||
void readCommentsByStaccatoId() throws Exception { | ||
// given | ||
when(authService.extractFromToken(any())).thenReturn(MemberFixture.create()); | ||
CommentResponse commentResponse = new CommentResponse(1L, 1L, "member", "image.jpg", "내용"); | ||
CommentResponses commentResponses = new CommentResponses(List.of(commentResponse)); | ||
when(commentService.readAllCommentsByMomentId(any(), any())).thenReturn(commentResponses); | ||
String expectedResponse = """ | ||
{ | ||
"comments": [ | ||
{ | ||
"commentId": 1, | ||
"memberId": 1, | ||
"nickname": "member", | ||
"memberImageUrl": "image.jpg", | ||
"content": "내용" | ||
} | ||
] | ||
} | ||
"""; | ||
|
||
// when & then | ||
mockMvc.perform(get("/comments/v2") | ||
.param("staccatoId", "1") | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.header(HttpHeaders.AUTHORIZATION, "token")) | ||
.andExpect(status().isOk()) | ||
.andExpect(content().json(expectedResponse)); | ||
} | ||
|
||
@DisplayName("스타카토 식별자가 양수가 아닐 경우 댓글 읽기에 실패한다.") | ||
@Test | ||
void readCommentsByStaccatoIdFail() throws Exception { | ||
// given | ||
when(authService.extractFromToken(any())).thenReturn(MemberFixture.create()); | ||
ExceptionResponse exceptionResponse = new ExceptionResponse(HttpStatus.BAD_REQUEST.toString(), "스타카토 식별자는 양수로 이루어져야 합니다."); | ||
|
||
// when & then | ||
mockMvc.perform(get("/comments/v2") | ||
.param("staccatoId", "0") | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.header(HttpHeaders.AUTHORIZATION, "token")) | ||
.andExpect(status().isBadRequest()) | ||
.andExpect(content().json(objectMapper.writeValueAsString(exceptionResponse))); | ||
} | ||
|
||
@DisplayName("댓글 수정 요청 역직렬화에 성공한다.") | ||
@Test | ||
void updateComment() throws Exception { | ||
// given | ||
when(authService.extractFromToken(any())).thenReturn(MemberFixture.create()); | ||
String commentUpdateRequest = """ | ||
{ | ||
"content": "content" | ||
} | ||
"""; | ||
|
||
// when & then | ||
mockMvc.perform(put("/comments/{commentId}", 1) | ||
.content(commentUpdateRequest) | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.header(HttpHeaders.AUTHORIZATION, "token")) | ||
.andExpect(status().isOk()); | ||
} | ||
|
||
@DisplayName("댓글 식별자가 양수가 아닐 경우 댓글 수정에 실패한다.") | ||
@Test | ||
void updateCommentFail() throws Exception { | ||
// given | ||
when(authService.extractFromToken(any())).thenReturn(MemberFixture.create()); | ||
CommentUpdateRequest commentUpdateRequest = CommentUpdateRequestFixture.create(); | ||
ExceptionResponse exceptionResponse = new ExceptionResponse(HttpStatus.BAD_REQUEST.toString(), "댓글 식별자는 양수로 이루어져야 합니다."); | ||
|
||
// when & then | ||
mockMvc.perform(put("/comments/{commentId}", 0) | ||
.content(objectMapper.writeValueAsString(commentUpdateRequest)) | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.header(HttpHeaders.AUTHORIZATION, "token")) | ||
.andExpect(status().isBadRequest()) | ||
.andExpect(content().json(objectMapper.writeValueAsString(exceptionResponse))); | ||
} | ||
|
||
@DisplayName("댓글 내용을 입력하지 않을 경우 댓글 수정에 실패한다.") | ||
@ParameterizedTest | ||
@NullSource | ||
@ValueSource(strings = {"", " "}) | ||
void updateCommentFailByBlank(String updatedContent) throws Exception { | ||
// given | ||
when(authService.extractFromToken(any())).thenReturn(MemberFixture.create()); | ||
CommentUpdateRequest commentUpdateRequest = new CommentUpdateRequest(updatedContent); | ||
ExceptionResponse exceptionResponse = new ExceptionResponse(HttpStatus.BAD_REQUEST.toString(), "댓글 내용을 입력해주세요."); | ||
|
||
// when & then | ||
mockMvc.perform(put("/comments/{commentId}", 1) | ||
.content(objectMapper.writeValueAsString(commentUpdateRequest)) | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.header(HttpHeaders.AUTHORIZATION, "token")) | ||
.andExpect(status().isBadRequest()) | ||
.andExpect(content().json(objectMapper.writeValueAsString(exceptionResponse))); | ||
} | ||
|
||
@DisplayName("올바른 형식으로 댓글 삭제를 시도하면 성공한다.") | ||
@Test | ||
void deleteComment() throws Exception { | ||
// given | ||
when(authService.extractFromToken(any())).thenReturn(MemberFixture.create()); | ||
|
||
// when & then | ||
mockMvc.perform(delete("/comments/{commentId}", 1) | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.header(HttpHeaders.AUTHORIZATION, "token")) | ||
.andExpect(status().isOk()); | ||
} | ||
|
||
@DisplayName("댓글 식별자가 양수가 아닐 경우 댓글 삭제에 실패한다.") | ||
@Test | ||
void deleteCommentFail() throws Exception { | ||
// given | ||
when(authService.extractFromToken(any())).thenReturn(MemberFixture.create()); | ||
ExceptionResponse exceptionResponse = new ExceptionResponse(HttpStatus.BAD_REQUEST.toString(), "댓글 식별자는 양수로 이루어져야 합니다."); | ||
|
||
// when & then | ||
mockMvc.perform(delete("/comments/{commentId}", 0) | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.header(HttpHeaders.AUTHORIZATION, "token")) | ||
.andExpect(status().isBadRequest()) | ||
.andExpect(content().json(objectMapper.writeValueAsString(exceptionResponse))); | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 테스트 파일에 기존 컨트롤로 테스트가 전부 있는게 아닌 변경에 대한 테스트만 일부 있군요! 처음에 얼핏 봤을 때는 모든 엔드포인트 테스트가 있을거라고 생각이 들었던 것 같아요. 또한, v2를 제거할땐 해당 테스트파일과 기존 컨트롤러 테스트 파일 총 2 군데 변경 지점이 생겨서 다른 개발자가 추후에 수정하게 된다면 헷갈릴 여지가 있다고 생각이 들었어요!
그래서 한 가지 제안 드리자면, 기존에 있는 관련 테스트에 필요한 요청 데이터와 검증부만 추가하는건 어떻게 생각하시나요?
제가 전에 썼던 방식이기도 한데요, 변경 지점이 하나로 분명하니까 편리하지 않을까 싶어서 제안드립니당
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
결국에 나중에 수정할 때, (새로운 파일을 살리는 경우) 새로운 파일에 원래 테스트를 넣어야 하므로, 지금 추가해놓는 게 낫겠네요! 수정했습니다.
이 방법을 시도해보려 했으나,
createComment
에서는 괜찮은데,createCommentFail
에는MethodSource
를 사용하기 때문에 하나의 함수에서 처리하지 못하고,readCommentsByMomentId
와readCommentsByMomentIdFail
는 메서드 이름의MomentId
가StaccatoId
로 바뀌어야 하기 때문에, 해당 방법을 사용하지 않았습니다.그리고 이 새로 생겨난 메서드들까지 원래 파일에서 관리하면 오히려 나중에 제거할때 훨씬 복잡해질 것 같아, 새로운 파일로 분리해서 만들었습니다. 혹시 제가 생각하지 못한 더 좋은 방법이 있다면, 공유 부탁드립니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그렇겠네요! 두 메서드는 어쩔 수 없을 것 같아요! 반영하시느라 수고하셨슴다~!~!