diff --git a/src/main/java/com/somemore/community/controller/CommunityBoardCommandApiController.java b/src/main/java/com/somemore/community/controller/CommunityBoardCommandApiController.java new file mode 100644 index 000000000..44ff7957d --- /dev/null +++ b/src/main/java/com/somemore/community/controller/CommunityBoardCommandApiController.java @@ -0,0 +1,78 @@ +package com.somemore.community.controller; + +import com.somemore.auth.annotation.CurrentUser; +import com.somemore.community.dto.request.CommunityBoardCreateRequestDto; +import com.somemore.community.dto.request.CommunityBoardUpdateRequestDto; +import com.somemore.community.usecase.board.CreateCommunityBoardUseCase; +import com.somemore.community.usecase.board.DeleteCommunityBoardUseCase; +import com.somemore.community.usecase.board.UpdateCommunityBoardUseCase; +import com.somemore.global.common.response.ApiResponse; +import com.somemore.imageupload.dto.ImageUploadRequestDto; +import com.somemore.imageupload.usecase.ImageUploadUseCase; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.UUID; + +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; + +@Tag(name = "Community Board Command API", description = "커뮤니티 게시글 생성 수정 삭제 API") +@RequiredArgsConstructor +@RequestMapping("/api/community-board") +@RestController +public class CommunityBoardCommandApiController { + + private final CreateCommunityBoardUseCase createCommunityBoardUseCase; + private final UpdateCommunityBoardUseCase updateCommunityBoardUseCase; + private final DeleteCommunityBoardUseCase deleteCommunityBoardUseCase; + private final ImageUploadUseCase imageUploadUseCase; + + @Secured("ROLE_VOLUNTEER") + @Operation(summary = "커뮤니티 게시글 등록", description = "커뮤니티 게시글을 등록합니다.") + @PostMapping(consumes = MULTIPART_FORM_DATA_VALUE) + public ApiResponse createCommunityBoard( + @CurrentUser UUID userId, + @Valid @RequestPart("data") CommunityBoardCreateRequestDto requestDto, + @RequestPart(value = "img_file", required = false) MultipartFile image + ) { + String imgUrl = imageUploadUseCase.uploadImage(new ImageUploadRequestDto(image)); + + return ApiResponse.ok( + 201, + createCommunityBoardUseCase.createCommunityBoard(requestDto, userId, imgUrl), + "커뮤니티 게시글 등록 성공" + ); + } + + @Secured("ROLE_VOLUNTEER") + @Operation(summary = "커뮤니티 게시글 수정", description = "커뮤니티 게시글을 수정합니다.") + @PutMapping(value = "/{id}", consumes = MULTIPART_FORM_DATA_VALUE) + public ApiResponse updateCommunityBoard( + @CurrentUser UUID userId, + @PathVariable Long id, + @Valid @RequestPart("data") CommunityBoardUpdateRequestDto requestDto, + @RequestPart(value = "img_file", required = false) MultipartFile image + ) { + String imgUrl = imageUploadUseCase.uploadImage(new ImageUploadRequestDto(image)); + updateCommunityBoardUseCase.updateCommunityBoard(requestDto, id, userId, imgUrl); + + return ApiResponse.ok("커뮤니티 게시글 수정 성공"); + } + + @Secured("ROLE_VOLUNTEER") + @Operation(summary = "커뮤니티 게시글 삭제", description = "커뮤니티 게시글을 삭제합니다.") + @DeleteMapping(value = "/{id}") + public ApiResponse deleteCommunityBoard( + @CurrentUser UUID userId, + @PathVariable Long id + ) { + deleteCommunityBoardUseCase.deleteCommunityBoard(userId, id); + + return ApiResponse.ok("커뮤니티 게시글 삭제 성공"); + } +} diff --git a/src/main/java/com/somemore/community/controller/CommunityBoardQueryApiController.java b/src/main/java/com/somemore/community/controller/CommunityBoardQueryApiController.java new file mode 100644 index 000000000..18f112412 --- /dev/null +++ b/src/main/java/com/somemore/community/controller/CommunityBoardQueryApiController.java @@ -0,0 +1,60 @@ +package com.somemore.community.controller; + +import com.somemore.community.dto.response.CommunityBoardDetailResponseDto; +import com.somemore.community.dto.response.CommunityBoardResponseDto; +import com.somemore.community.usecase.board.CommunityBoardQueryUseCase; +import com.somemore.global.common.response.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.web.bind.annotation.*; + +import java.util.UUID; + +@Tag(name = "Community Board Query API", description = "커뮤니티 게시글 조회 관련 API") +@RequiredArgsConstructor +@RequestMapping("/api") +@RestController +public class CommunityBoardQueryApiController { + + private final CommunityBoardQueryUseCase communityBoardQueryUseCase; + + @GetMapping("/community-boards") + @Operation(summary = "전체 커뮤니티 게시글 조회", description = "전체 커뮤니티 게시글 목록을 조회합니다.") + public ApiResponse> getAll( + Pageable pageable + ) { + return ApiResponse.ok( + 200, + communityBoardQueryUseCase.getCommunityBoards(pageable.getPageNumber()), + "전체 커뮤니티 게시글 리스트 조회 성공" + ); + } + + @GetMapping("/community-boards/{writerId}") + @Operation(summary = "작성자별 커뮤니티 게시글 조회", description = "작성자별 커뮤니티 게시글 목록을 조회합니다.") + public ApiResponse> getByWriterId( + @PathVariable UUID writerId, + Pageable pageable + ) { + return ApiResponse.ok( + 200, + communityBoardQueryUseCase.getCommunityBoardsByWriterId(writerId, pageable.getPageNumber()), + "작성자별 커뮤니티 게시글 리스트 조회 성공" + ); + } + + @GetMapping("/community-board/{id}") + @Operation(summary = "커뮤니티 게시글 상세 조회", description = "커뮤니티 게시글의 상세 정보를 조회합니다.") + public ApiResponse getById( + @PathVariable Long id + ) { + return ApiResponse.ok( + 200, + communityBoardQueryUseCase.getCommunityBoardDetail(id), + "커뮤니티 게시글 상세 조회 성공" + ); + } +} diff --git a/src/main/java/com/somemore/community/controller/CommunityCommentCommandApiController.java b/src/main/java/com/somemore/community/controller/CommunityCommentCommandApiController.java new file mode 100644 index 000000000..de3cdab0e --- /dev/null +++ b/src/main/java/com/somemore/community/controller/CommunityCommentCommandApiController.java @@ -0,0 +1,69 @@ +package com.somemore.community.controller; + +import com.somemore.auth.annotation.CurrentUser; +import com.somemore.community.dto.request.CommunityCommentCreateRequestDto; +import com.somemore.community.dto.request.CommunityCommentUpdateRequestDto; +import com.somemore.community.usecase.comment.CreateCommunityCommentUseCase; +import com.somemore.community.usecase.comment.DeleteCommunityCommentUseCase; +import com.somemore.community.usecase.comment.UpdateCommunityCommentUseCase; +import com.somemore.global.common.response.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.*; + +import java.util.UUID; + +@Tag(name = "Community Comment Command API", description = "커뮤니티 댓글 생성 수정 삭제 API") +@RequiredArgsConstructor +@RequestMapping("/api/community-board/{boardId}") +@RestController +public class CommunityCommentCommandApiController { + + private final CreateCommunityCommentUseCase createCommunityCommentUseCase; + private final UpdateCommunityCommentUseCase updateCommunityCommentUseCase; + private final DeleteCommunityCommentUseCase deleteCommunityCommentUseCase; + + @Secured("ROLE_VOLUNTEER") + @Operation(summary = "커뮤니티 댓글 등록", description = "커뮤니티 게시글에 댓글을 등록합니다.") + @PostMapping(value = "/comment") + public ApiResponse createCommunityComment( + @CurrentUser UUID userId, + @PathVariable Long boardId, + @Valid @RequestBody CommunityCommentCreateRequestDto requestDto) { + + return ApiResponse.ok( + 201, + createCommunityCommentUseCase.createCommunityComment(requestDto, userId, boardId), + "커뮤니티 댓글 등록 성공"); + } + + @Secured("ROLE_VOLUNTEER") + @Operation(summary = "커뮤니티 댓글 수정", description = "커뮤니티 댓글을 수정합니다.") + @PutMapping(value = "/comment/{id}") + public ApiResponse updateCommunityComment( + @CurrentUser UUID userId, + @PathVariable Long boardId, + @PathVariable Long id, + @Valid @RequestBody CommunityCommentUpdateRequestDto requestDto + ) { + updateCommunityCommentUseCase.updateCommunityComment(requestDto, id, userId, boardId); + + return ApiResponse.ok("커뮤니티 댓글 수정 성공"); + } + + @Secured("ROLE_VOLUNTEER") + @Operation(summary = "커뮤니티 댓글 삭제", description = "커뮤니티 댓글을 삭제합니다.") + @DeleteMapping(value = "/comment/{id}") + public ApiResponse deleteCommunityComment( + @CurrentUser UUID userId, + @PathVariable Long boardId, + @PathVariable Long id + ) { + deleteCommunityCommentUseCase.deleteCommunityComment(userId, id, boardId); + + return ApiResponse.ok("커뮤니티 댓글 삭제 성공"); + } +} diff --git a/src/main/java/com/somemore/community/controller/CommunityCommentQueryApiController.java b/src/main/java/com/somemore/community/controller/CommunityCommentQueryApiController.java new file mode 100644 index 000000000..25cec5980 --- /dev/null +++ b/src/main/java/com/somemore/community/controller/CommunityCommentQueryApiController.java @@ -0,0 +1,36 @@ +package com.somemore.community.controller; + +import com.somemore.community.dto.response.CommunityCommentResponseDto; +import com.somemore.community.usecase.comment.CommunityCommentQueryUseCase; +import com.somemore.global.common.response.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "Community Comment Query API", description = "커뮤니티 댓글 조회 API") +@RequiredArgsConstructor +@RequestMapping("/api/community-board") +@RestController +public class CommunityCommentQueryApiController { + + private final CommunityCommentQueryUseCase communityCommentQueryUseCase; + + @GetMapping("/{boardId}/comments") + @Operation(summary = "커뮤니티 댓글 조회", description = "커뮤니티 게시글의 댓글 목록을 조회합니다.") + public ApiResponse> getByBoardId( + @PathVariable Long boardId, + Pageable pageable + ) { + return ApiResponse.ok( + 200, + communityCommentQueryUseCase.getCommunityCommentsByBoardId(boardId, pageable.getPageNumber()), + "커뮤니티 게시글의 댓글 리스트 조회 성공" + ); + } +} diff --git a/src/main/java/com/somemore/community/dto/request/CommunityCommentCreateRequestDto.java b/src/main/java/com/somemore/community/dto/request/CommunityCommentCreateRequestDto.java index 2c6a5b104..6185bfa44 100644 --- a/src/main/java/com/somemore/community/dto/request/CommunityCommentCreateRequestDto.java +++ b/src/main/java/com/somemore/community/dto/request/CommunityCommentCreateRequestDto.java @@ -6,7 +6,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.annotation.Nullable; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import lombok.Builder; import java.util.UUID; @@ -14,9 +13,6 @@ @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) @Builder public record CommunityCommentCreateRequestDto( - @Schema(description = "커뮤니티 게시글 ID", example = "33") - @NotNull(message = "게시글 ID는 필수 값입니다.") - Long communityBoardId, @Schema(description = "커뮤니티 댓글 내용", example = "저도 함께 하고 싶습니다.") @NotBlank(message = "댓글 내용은 필수 값입니다.") String content, @@ -24,7 +20,7 @@ public record CommunityCommentCreateRequestDto( @Nullable Long parentCommentId ) { - public CommunityComment toEntity(UUID writerId) { + public CommunityComment toEntity(UUID writerId, Long communityBoardId) { return CommunityComment.builder() .communityBoardId(communityBoardId) .writerId(writerId) diff --git a/src/main/java/com/somemore/community/dto/response/CommunityBoardDetailResponseDto.java b/src/main/java/com/somemore/community/dto/response/CommunityBoardDetailResponseDto.java index 22a9a28cd..6bab4ff0f 100644 --- a/src/main/java/com/somemore/community/dto/response/CommunityBoardDetailResponseDto.java +++ b/src/main/java/com/somemore/community/dto/response/CommunityBoardDetailResponseDto.java @@ -4,10 +4,12 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.somemore.community.domain.CommunityBoard; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; import java.time.LocalDateTime; import java.util.UUID; +@Builder @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) @Schema(description = "커뮤니티 게시글 상세 조회 응답 DTO") public record CommunityBoardDetailResponseDto( diff --git a/src/main/java/com/somemore/community/service/comment/CreateCommunityCommentService.java b/src/main/java/com/somemore/community/service/comment/CreateCommunityCommentService.java index ba7a116db..5d3a34d1a 100644 --- a/src/main/java/com/somemore/community/service/comment/CreateCommunityCommentService.java +++ b/src/main/java/com/somemore/community/service/comment/CreateCommunityCommentService.java @@ -24,10 +24,10 @@ public class CreateCommunityCommentService implements CreateCommunityCommentUseC private final CommunityCommentRepository communityCommentRepository; @Override - public Long createCommunityComment(CommunityCommentCreateRequestDto requestDto, UUID writerId) { - CommunityComment communityComment = requestDto.toEntity(writerId); + public Long createCommunityComment(CommunityCommentCreateRequestDto requestDto, UUID writerId, Long communityBoardId) { + CommunityComment communityComment = requestDto.toEntity(writerId, communityBoardId); - validateCommunityBoardExists(communityComment.getCommunityBoardId()); + validateCommunityBoardExists(communityBoardId); if (requestDto.parentCommentId() != null) { validateParentCommentExists(communityComment.getParentCommentId()); diff --git a/src/main/java/com/somemore/community/service/comment/DeleteCommunityCommentService.java b/src/main/java/com/somemore/community/service/comment/DeleteCommunityCommentService.java index 6072c4e4f..03e00e359 100644 --- a/src/main/java/com/somemore/community/service/comment/DeleteCommunityCommentService.java +++ b/src/main/java/com/somemore/community/service/comment/DeleteCommunityCommentService.java @@ -20,7 +20,7 @@ public class DeleteCommunityCommentService implements DeleteCommunityCommentUseC private final CommunityCommentRepository communityCommentRepository; @Override - public void deleteCommunityComment(UUID writerId, Long id) { + public void deleteCommunityComment(UUID writerId, Long id, Long communityBoardId) { CommunityComment communityComment = getCommunityCommentById(id); diff --git a/src/main/java/com/somemore/community/service/comment/UpdateCommunityCommentService.java b/src/main/java/com/somemore/community/service/comment/UpdateCommunityCommentService.java index ec688a9e6..a613c9bcc 100644 --- a/src/main/java/com/somemore/community/service/comment/UpdateCommunityCommentService.java +++ b/src/main/java/com/somemore/community/service/comment/UpdateCommunityCommentService.java @@ -23,11 +23,11 @@ public class UpdateCommunityCommentService implements UpdateCommunityCommentUseC private final CommunityBoardRepository communityBoardRepository; @Override - public void updateCommunityComment(CommunityCommentUpdateRequestDto requestDto, Long communityCommentId, UUID writerId) { + public void updateCommunityComment(CommunityCommentUpdateRequestDto requestDto, Long communityCommentId, UUID writerId, Long communityBoardId) { CommunityComment communityComment = getCommunityCommentById(communityCommentId); - validateCommunityBoardExists(communityComment.getCommunityBoardId()); + validateCommunityBoardExists(communityBoardId); validateWriter(communityComment, writerId); diff --git a/src/main/java/com/somemore/community/usecase/comment/CreateCommunityCommentUseCase.java b/src/main/java/com/somemore/community/usecase/comment/CreateCommunityCommentUseCase.java index 3def6667f..5a2a5ed98 100644 --- a/src/main/java/com/somemore/community/usecase/comment/CreateCommunityCommentUseCase.java +++ b/src/main/java/com/somemore/community/usecase/comment/CreateCommunityCommentUseCase.java @@ -7,5 +7,6 @@ public interface CreateCommunityCommentUseCase { Long createCommunityComment( CommunityCommentCreateRequestDto requestDto, - UUID writerId); + UUID writerId, + Long communityBoardId); } diff --git a/src/main/java/com/somemore/community/usecase/comment/DeleteCommunityCommentUseCase.java b/src/main/java/com/somemore/community/usecase/comment/DeleteCommunityCommentUseCase.java index c5e1f5d38..7226cf348 100644 --- a/src/main/java/com/somemore/community/usecase/comment/DeleteCommunityCommentUseCase.java +++ b/src/main/java/com/somemore/community/usecase/comment/DeleteCommunityCommentUseCase.java @@ -3,5 +3,5 @@ import java.util.UUID; public interface DeleteCommunityCommentUseCase { - void deleteCommunityComment(UUID writerId, Long id); + void deleteCommunityComment(UUID writerId, Long id, Long communityBoardId); } diff --git a/src/main/java/com/somemore/community/usecase/comment/UpdateCommunityCommentUseCase.java b/src/main/java/com/somemore/community/usecase/comment/UpdateCommunityCommentUseCase.java index ef4c028e9..acc15bf41 100644 --- a/src/main/java/com/somemore/community/usecase/comment/UpdateCommunityCommentUseCase.java +++ b/src/main/java/com/somemore/community/usecase/comment/UpdateCommunityCommentUseCase.java @@ -8,5 +8,6 @@ public interface UpdateCommunityCommentUseCase { void updateCommunityComment( CommunityCommentUpdateRequestDto requestDto, Long communityCommentId, - UUID writerId); + UUID writerId, + Long communityBoardId); } diff --git a/src/test/java/com/somemore/community/controller/CommunityBoardCommandApiControllerTest.java b/src/test/java/com/somemore/community/controller/CommunityBoardCommandApiControllerTest.java new file mode 100644 index 000000000..75c02276f --- /dev/null +++ b/src/test/java/com/somemore/community/controller/CommunityBoardCommandApiControllerTest.java @@ -0,0 +1,171 @@ +package com.somemore.community.controller; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willDoNothing; +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.somemore.ControllerTestSupport; +import com.somemore.WithMockCustomUser; +import com.somemore.community.dto.request.CommunityBoardCreateRequestDto; +import com.somemore.community.dto.request.CommunityBoardUpdateRequestDto; +import com.somemore.community.usecase.board.CreateCommunityBoardUseCase; +import com.somemore.community.usecase.board.DeleteCommunityBoardUseCase; +import com.somemore.community.usecase.board.UpdateCommunityBoardUseCase; +import com.somemore.imageupload.usecase.ImageUploadUseCase; +import java.util.UUID; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.RequestPostProcessor; + +public class CommunityBoardCommandApiControllerTest extends ControllerTestSupport { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private CreateCommunityBoardUseCase createCommunityBoardUseCase; + + @MockBean + private UpdateCommunityBoardUseCase updateCommunityBoardUseCase; + + @MockBean + private DeleteCommunityBoardUseCase deleteCommunityBoardUseCase; + + @MockBean + private ImageUploadUseCase imageUploadUseCase; + + @Test + @DisplayName("커뮤니티 게시글 등록 성공 테스트") + @WithMockCustomUser + void createCommunityBoard_success() throws Exception { + //given + CommunityBoardCreateRequestDto dto = CommunityBoardCreateRequestDto.builder() + .title("11/29 OO도서관 봉사 같이 갈 사람 모집합니다.") + .content("저 포함 5명이 같이 가면 좋을 거 같아요.") + .build(); + + MockMultipartFile imageFile = new MockMultipartFile( + "img_file", + "test-image.jpg", + MediaType.IMAGE_JPEG_VALUE, + "test image content".getBytes() + ); + + MockMultipartFile requestData = new MockMultipartFile( + "data", + "", + MediaType.APPLICATION_JSON_VALUE, + objectMapper.writeValueAsBytes(dto) + ); + + String mockImageUrl = "http://example.com/image/test-image.jpg"; + long communityBoardId = 1L; + + given(imageUploadUseCase.uploadImage(any())).willReturn(mockImageUrl); + given(createCommunityBoardUseCase.createCommunityBoard(any(), any(UUID.class), + anyString())).willReturn(communityBoardId); + + //when + mockMvc.perform(multipart("/api/community-board") + .file(requestData) + .file(imageFile) + .contentType(MULTIPART_FORM_DATA) + .header("Authorization", "Bearer access-token")) + //then + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(201)) + .andExpect(jsonPath("$.data").value(communityBoardId)) + .andExpect(jsonPath("$.message").value("커뮤니티 게시글 등록 성공")); + } + + @Test + @DisplayName("커뮤니티 게시글 수정 성공 테스트") + @WithMockCustomUser + void updateCommunityBoard_success() throws Exception { + //given + CommunityBoardUpdateRequestDto requestDto = CommunityBoardUpdateRequestDto.builder() + .title("XX아동센터 추천합니다.") + .content("지난 주 토요일에 방문했는데 강추드려요.") + .build(); + + MockMultipartFile imageFile = new MockMultipartFile( + "img_file", + "test-image.jpg", + MediaType.IMAGE_JPEG_VALUE, + "test image content".getBytes() + ); + + MockMultipartFile requestData = new MockMultipartFile( + "data", + "", + MediaType.APPLICATION_JSON_VALUE, + objectMapper.writeValueAsBytes(requestDto) + ); + + String imageUrl = "http://example.com/image/test-image.jpg"; + + given(imageUploadUseCase.uploadImage(any())).willReturn(imageUrl); + willDoNothing().given(updateCommunityBoardUseCase) + .updateCommunityBoard(any(), any(), any(UUID.class), anyString()); + + MockMultipartHttpServletRequestBuilder builder = multipart("/api/community-board/{id}", 1); + builder.with(new RequestPostProcessor() { + @Override + public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { + request.setMethod("PUT"); + return request; + } + }); + + //when + mockMvc.perform(builder + .file(requestData) + .file(imageFile) + .contentType(MULTIPART_FORM_DATA) + .header("Authorization", "Bearer access-token")) + + //then + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isEmpty()) + .andExpect(jsonPath("$.message").value("커뮤니티 게시글 수정 성공")); + } + + @Test + @DisplayName("커뮤니티 게시글 삭제 성공 테스트") + @WithMockCustomUser + void deleteCommunityBoard_success() throws Exception { + //given + Long communityBoardId = 1L; + willDoNothing().given(deleteCommunityBoardUseCase).deleteCommunityBoard(any(UUID.class), any()); + + //when + mockMvc.perform(delete("/api/community-board/{id}", communityBoardId) + .header("Authorization", "Bearer access-token")) + //then + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.message").value("커뮤니티 게시글 삭제 성공")) + .andExpect(jsonPath("$.data").isEmpty()); + } +} diff --git a/src/test/java/com/somemore/community/controller/CommunityBoardQueryApiControllerTest.java b/src/test/java/com/somemore/community/controller/CommunityBoardQueryApiControllerTest.java new file mode 100644 index 000000000..74e05b775 --- /dev/null +++ b/src/test/java/com/somemore/community/controller/CommunityBoardQueryApiControllerTest.java @@ -0,0 +1,104 @@ +package com.somemore.community.controller; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.somemore.ControllerTestSupport; +import com.somemore.community.dto.response.CommunityBoardDetailResponseDto; +import com.somemore.community.dto.response.CommunityBoardResponseDto; +import com.somemore.community.usecase.board.CommunityBoardQueryUseCase; +import java.util.Collections; +import java.util.UUID; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +public class CommunityBoardQueryApiControllerTest extends ControllerTestSupport { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private CommunityBoardQueryUseCase communityBoardQueryUseCase; + + @Test + @DisplayName("커뮤니티 게시글 전체 조회 성공") + void getAll() throws Exception { + //given + Page page = new PageImpl<>(Collections.emptyList()); + + given(communityBoardQueryUseCase.getCommunityBoards(anyInt())) + .willReturn(page); + + //when + //then + mockMvc.perform(get("/api/community-boards") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").exists()) + .andExpect(jsonPath("$.message") + .value("전체 커뮤니티 게시글 리스트 조회 성공")); + + verify(communityBoardQueryUseCase, times(1)) + .getCommunityBoards(anyInt()); + } + + @Test + @DisplayName("작성자별 커뮤니티 게시글 조회 성공") + void getByWriterId() throws Exception { + //given + UUID writerId = UUID.randomUUID(); + Page page = new PageImpl<>(Collections.emptyList()); + + given(communityBoardQueryUseCase.getCommunityBoardsByWriterId(any(), anyInt())) + .willReturn(page); + + //when + //then + mockMvc.perform(get("/api/community-boards/{writerId}", writerId) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").exists()) + .andExpect(jsonPath("$.message") + .value("작성자별 커뮤니티 게시글 리스트 조회 성공")); + + verify(communityBoardQueryUseCase, times(1)) + .getCommunityBoardsByWriterId(any(), anyInt()); + } + + @Test + @DisplayName("커뮤니티 게시글 상세 조회 성공") + void getById() throws Exception { + //given + Long communityBoardId = 1L; + CommunityBoardDetailResponseDto responseDto = CommunityBoardDetailResponseDto.builder().build(); + given(communityBoardQueryUseCase.getCommunityBoardDetail(any())) + .willReturn(responseDto); + + //when + //then + mockMvc.perform(get("/api/community-board/{id}", communityBoardId) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").exists()) + .andExpect(jsonPath("$.message") + .value("커뮤니티 게시글 상세 조회 성공")); + + verify(communityBoardQueryUseCase, times(1)) + .getCommunityBoardDetail(any()); + } +} diff --git a/src/test/java/com/somemore/community/controller/CommunityCommentCommandApiControllerTest.java b/src/test/java/com/somemore/community/controller/CommunityCommentCommandApiControllerTest.java new file mode 100644 index 000000000..1573ba6cc --- /dev/null +++ b/src/test/java/com/somemore/community/controller/CommunityCommentCommandApiControllerTest.java @@ -0,0 +1,115 @@ +package com.somemore.community.controller; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willDoNothing; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.somemore.ControllerTestSupport; +import com.somemore.WithMockCustomUser; +import com.somemore.community.dto.request.CommunityCommentCreateRequestDto; +import com.somemore.community.dto.request.CommunityCommentUpdateRequestDto; +import com.somemore.community.usecase.comment.CreateCommunityCommentUseCase; +import com.somemore.community.usecase.comment.DeleteCommunityCommentUseCase; +import com.somemore.community.usecase.comment.UpdateCommunityCommentUseCase; +import java.util.UUID; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +public class CommunityCommentCommandApiControllerTest extends ControllerTestSupport { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private CreateCommunityCommentUseCase createCommunityCommentUseCase; + + @MockBean + private UpdateCommunityCommentUseCase updateCommunityCommentUseCase; + + @MockBean + private DeleteCommunityCommentUseCase deleteCommunityCommentUseCase; + + private final long communityBoardId = 1L; + private final long communityCommentId = 1L; + + @Test + @DisplayName("커뮤니티 댓글 등록 성공 테스트") + @WithMockCustomUser + void createCommunityComment() throws Exception { + //given + CommunityCommentCreateRequestDto requestDto = CommunityCommentCreateRequestDto.builder() + .content("몇시에 하는지 알 수 있을까요?") + .parentCommentId(null) + .build(); + + given(createCommunityCommentUseCase.createCommunityComment(any(), any(UUID.class), + eq(communityBoardId))).willReturn(communityCommentId); + + //when + mockMvc.perform(post("/api/community-board/{boardId}/comment", communityBoardId) + .content(objectMapper.writeValueAsString(requestDto)) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer access-token")) + + //then + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(201)) + .andExpect(jsonPath("$.data").value(communityCommentId)) + .andExpect(jsonPath("$.message").value("커뮤니티 댓글 등록 성공")); + } + + @Test + @DisplayName("커뮤니티 댓글 수정 성공 테스트") + @WithMockCustomUser + void updateCommunityComment_success() throws Exception { + //given + CommunityCommentUpdateRequestDto requestDto = CommunityCommentUpdateRequestDto.builder() + .content("감사합니다.") + .build(); + + willDoNothing().given(updateCommunityCommentUseCase) + .updateCommunityComment(any(), any(), any(UUID.class), any()); + + //when + mockMvc.perform(put("/api/community-board/{boardId}/comment/{id}", communityBoardId, communityCommentId) + .content(objectMapper.writeValueAsString(requestDto)) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer access-token")) + + //then + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isEmpty()) + .andExpect(jsonPath("$.message").value("커뮤니티 댓글 수정 성공")); + } + + @Test + @DisplayName("커뮤니티 댓글 삭제 성공 테스트") + @WithMockCustomUser + void deleteCommunityComment_success() throws Exception { + //given + willDoNothing().given(deleteCommunityCommentUseCase).deleteCommunityComment(any(UUID.class), any(), any()); + + //when + mockMvc.perform(delete("/api/community-board/{boardId}/comment/{id}", communityBoardId, communityCommentId) + .header("Authorization", "Bearer access-token")) + //then + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.message").value("커뮤니티 댓글 삭제 성공")) + .andExpect(jsonPath("$.data").isEmpty()); + } +} diff --git a/src/test/java/com/somemore/community/controller/CommunityCommentQueryApiControllerTest.java b/src/test/java/com/somemore/community/controller/CommunityCommentQueryApiControllerTest.java new file mode 100644 index 000000000..46ea7a9fd --- /dev/null +++ b/src/test/java/com/somemore/community/controller/CommunityCommentQueryApiControllerTest.java @@ -0,0 +1,57 @@ +package com.somemore.community.controller; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.somemore.ControllerTestSupport; +import com.somemore.community.dto.response.CommunityCommentResponseDto; +import java.util.Collections; + +import com.somemore.community.usecase.comment.CommunityCommentQueryUseCase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +public class CommunityCommentQueryApiControllerTest extends ControllerTestSupport { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private CommunityCommentQueryUseCase communityCommentQueryUseCase; + + @Test + @DisplayName("커뮤니티 댓글 조회 성공") + void getByBoardId() throws Exception { + //given + long communityBoardId = 1L; + Page page = new PageImpl<>(Collections.emptyList()); + + given(communityCommentQueryUseCase.getCommunityCommentsByBoardId(any(), anyInt())) + .willReturn(page); + + //when + //then + mockMvc.perform(get("/api/community-board/{boardId}/comments", communityBoardId) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").exists()) + .andExpect(jsonPath("$.message") + .value("커뮤니티 게시글의 댓글 리스트 조회 성공")); + + verify(communityCommentQueryUseCase, times(1)) + .getCommunityCommentsByBoardId(any(), anyInt()); + } + +} diff --git a/src/test/java/com/somemore/community/service/comment/CommunityCommentQueryServiceTest.java b/src/test/java/com/somemore/community/service/comment/CommunityCommentQueryServiceTest.java index 184fc665b..3a1d6034a 100644 --- a/src/test/java/com/somemore/community/service/comment/CommunityCommentQueryServiceTest.java +++ b/src/test/java/com/somemore/community/service/comment/CommunityCommentQueryServiceTest.java @@ -88,7 +88,7 @@ void getCommentsByCommunityBoardId() { @Test void doesNotFind() { //given - deleteCommunityCommentUseCase.deleteCommunityComment(writerId, replyId); + deleteCommunityCommentUseCase.deleteCommunityComment(writerId, replyId, boardId); //when Page comments = communityCommentQueryService.getCommunityCommentsByBoardId(boardId, 0); @@ -105,7 +105,7 @@ void doesNotFind() { void getCommentsByCommunityBoardIdWithDeletedComment() { //given - deleteCommunityCommentUseCase.deleteCommunityComment(writerId, commentId); + deleteCommunityCommentUseCase.deleteCommunityComment(writerId, commentId, boardId); //when Page comments = communityCommentQueryService.getCommunityCommentsByBoardId(boardId, 0); diff --git a/src/test/java/com/somemore/community/service/comment/CreateCommunityCommentServiceTest.java b/src/test/java/com/somemore/community/service/comment/CreateCommunityCommentServiceTest.java index b65a2c2c5..b811cab06 100644 --- a/src/test/java/com/somemore/community/service/comment/CreateCommunityCommentServiceTest.java +++ b/src/test/java/com/somemore/community/service/comment/CreateCommunityCommentServiceTest.java @@ -59,13 +59,12 @@ void createCommunityCommentWithDto() { //given CommunityCommentCreateRequestDto dto = CommunityCommentCreateRequestDto.builder() - .communityBoardId(boardId) .content("커뮤니티 댓글 테스트 내용") .parentCommentId(null) .build(); //when - Long commentId = createCommunityCommentService.createCommunityComment(dto, writerId); + Long commentId = createCommunityCommentService.createCommunityComment(dto, writerId, boardId); //then Optional communityComment = communityCommentRepository.findById(commentId); @@ -83,21 +82,19 @@ void createCommunityCommentRelyWithDto() { //given CommunityCommentCreateRequestDto commentDto = CommunityCommentCreateRequestDto.builder() - .communityBoardId(boardId) .content("커뮤니티 댓글 테스트 내용") .parentCommentId(null) .build(); - Long commentId = createCommunityCommentService.createCommunityComment(commentDto, writerId); + Long commentId = createCommunityCommentService.createCommunityComment(commentDto, writerId, boardId); CommunityCommentCreateRequestDto replyDto = CommunityCommentCreateRequestDto.builder() - .communityBoardId(boardId) .content("커뮤니티 대댓글 테스트 내용") .parentCommentId(commentId) .build(); //when - Long replyCommentId = createCommunityCommentService.createCommunityComment(replyDto, writerId); + Long replyCommentId = createCommunityCommentService.createCommunityComment(replyDto, writerId, boardId); //then Optional communityCommentReply = communityCommentRepository.findById(replyCommentId); @@ -115,13 +112,12 @@ void createCommunityCommentReplyWithDeletedParentId() { //given CommunityCommentCreateRequestDto replyDto = CommunityCommentCreateRequestDto.builder() - .communityBoardId(boardId) .content("커뮤니티 대댓글 테스트 내용") .parentCommentId(2L) .build(); //when - ThrowableAssert.ThrowingCallable callable = () -> createCommunityCommentService.createCommunityComment(replyDto, UUID.randomUUID()); + ThrowableAssert.ThrowingCallable callable = () -> createCommunityCommentService.createCommunityComment(replyDto, UUID.randomUUID(), boardId); //then assertThatExceptionOfType(BadRequestException.class) @@ -135,7 +131,6 @@ void createCommunityCommentWithDeletedBoardId() { //given CommunityCommentCreateRequestDto commentDto = CommunityCommentCreateRequestDto.builder() - .communityBoardId(boardId) .content("커뮤니티 댓글 테스트 내용") .parentCommentId(null) .build(); @@ -143,7 +138,7 @@ void createCommunityCommentWithDeletedBoardId() { communityBoardRepository.deleteAllInBatch(); //when - ThrowableAssert.ThrowingCallable callable = () -> createCommunityCommentService.createCommunityComment(commentDto, UUID.randomUUID()); + ThrowableAssert.ThrowingCallable callable = () -> createCommunityCommentService.createCommunityComment(commentDto, UUID.randomUUID(), boardId); //then assertThatExceptionOfType(BadRequestException.class) diff --git a/src/test/java/com/somemore/community/service/comment/DeleteCommunityCommentServiceTest.java b/src/test/java/com/somemore/community/service/comment/DeleteCommunityCommentServiceTest.java index 2e6db5020..bf159279a 100644 --- a/src/test/java/com/somemore/community/service/comment/DeleteCommunityCommentServiceTest.java +++ b/src/test/java/com/somemore/community/service/comment/DeleteCommunityCommentServiceTest.java @@ -32,7 +32,7 @@ class DeleteCommunityCommentServiceTest extends IntegrationTestSupport { private CommunityBoardRepository communityBoardRepository; private UUID writerId; - private Long commentId; + private Long commentId, boardId; @BeforeEach void setUp() { @@ -44,14 +44,14 @@ void setUp() { writerId = UUID.randomUUID(); CommunityBoard communityBoard = communityBoardRepository.save(boardDto.toEntity(writerId, "https://test.image/123")); + boardId = communityBoard.getId(); CommunityCommentCreateRequestDto dto = CommunityCommentCreateRequestDto.builder() - .communityBoardId(communityBoard.getId()) .content("커뮤니티 댓글 테스트 내용") .parentCommentId(null) .build(); - CommunityComment communityComment = communityCommentRepository.save(dto.toEntity(writerId)); + CommunityComment communityComment = communityCommentRepository.save(dto.toEntity(writerId, communityBoard.getId())); commentId = communityComment.getId();} @@ -66,7 +66,7 @@ void deleteCommunityCommentWithId() { //given //when - deleteCommunityCommentService.deleteCommunityComment(writerId, commentId); + deleteCommunityCommentService.deleteCommunityComment(writerId, commentId, boardId); //then assertThat(communityCommentRepository.existsById(commentId)).isFalse(); @@ -77,10 +77,10 @@ void deleteCommunityCommentWithId() { void deleteCommunityCommentWithDeletedId() { //given - deleteCommunityCommentService.deleteCommunityComment(writerId, commentId); + deleteCommunityCommentService.deleteCommunityComment(writerId, commentId, boardId); //when - ThrowableAssert.ThrowingCallable callable = () -> deleteCommunityCommentService.deleteCommunityComment(writerId, commentId); + ThrowableAssert.ThrowingCallable callable = () -> deleteCommunityCommentService.deleteCommunityComment(writerId, commentId, boardId); //then assertThatExceptionOfType(BadRequestException.class) @@ -94,7 +94,7 @@ void deleteCommunityCommentWithNotWriterId() { //given //when - ThrowableAssert.ThrowingCallable callable = () -> deleteCommunityCommentService.deleteCommunityComment(UUID.randomUUID(), commentId); + ThrowableAssert.ThrowingCallable callable = () -> deleteCommunityCommentService.deleteCommunityComment(UUID.randomUUID(), commentId, boardId); //then assertThatExceptionOfType(BadRequestException.class) diff --git a/src/test/java/com/somemore/community/service/comment/UpdateCommunityCommentServiceTest.java b/src/test/java/com/somemore/community/service/comment/UpdateCommunityCommentServiceTest.java index dfbd16fe3..c86c81177 100644 --- a/src/test/java/com/somemore/community/service/comment/UpdateCommunityCommentServiceTest.java +++ b/src/test/java/com/somemore/community/service/comment/UpdateCommunityCommentServiceTest.java @@ -34,7 +34,7 @@ class UpdateCommunityCommentServiceTest extends IntegrationTestSupport { private CommunityBoardRepository communityBoardRepository; private UUID writerId; - private Long commentId; + private Long commentId, boardId; private CommunityCommentUpdateRequestDto updateRequestDto; @BeforeEach @@ -47,14 +47,14 @@ void setUp() { writerId = UUID.randomUUID(); CommunityBoard communityBoard = communityBoardRepository.save(boardDto.toEntity(writerId, "https://test.image/123")); + boardId = communityBoard.getId(); CommunityCommentCreateRequestDto commentDto = CommunityCommentCreateRequestDto.builder() - .communityBoardId(communityBoard.getId()) .content("커뮤니티 댓글 테스트 내용") .parentCommentId(null) .build(); - CommunityComment communityComment = communityCommentRepository.save(commentDto.toEntity(writerId)); + CommunityComment communityComment = communityCommentRepository.save(commentDto.toEntity(writerId, boardId)); commentId = communityComment.getId(); @@ -74,7 +74,7 @@ void updateCommunityComment() { //given //when - updateCommunityCommentService.updateCommunityComment(updateRequestDto, commentId, writerId); + updateCommunityCommentService.updateCommunityComment(updateRequestDto, commentId, writerId, boardId); //then Optional communityComment = communityCommentRepository.findById(commentId); @@ -89,7 +89,9 @@ void updateCommunityCommentWithNotWriterId() { //given //when - ThrowableAssert.ThrowingCallable callable = () -> updateCommunityCommentService.updateCommunityComment(updateRequestDto, commentId, UUID.randomUUID()); + ThrowableAssert.ThrowingCallable callable = () -> + updateCommunityCommentService.updateCommunityComment(updateRequestDto, commentId, + UUID.randomUUID(), boardId); //then assertThatExceptionOfType(BadRequestException.class) @@ -104,7 +106,9 @@ void updateCommunityCommentWithDeletedBoardId() { //given communityBoardRepository.deleteAllInBatch(); //when - ThrowableAssert.ThrowingCallable callable = () -> updateCommunityCommentService.updateCommunityComment(updateRequestDto, commentId, writerId); + ThrowableAssert.ThrowingCallable callable = () -> + updateCommunityCommentService.updateCommunityComment(updateRequestDto, commentId, + writerId, boardId); //then assertThatExceptionOfType(BadRequestException.class)