SNS 제작 (포스트 삭제, 포스트 리스트 조회)

개발연습생log·2023년 1월 3일
0

SNS 제작

목록 보기
6/15
post-thumbnail

12/27 SNS 제작 (포스트 삭제, 포스트 리스트 조회)

목표

포스트 삭제, 포스트 리스트 조회 기능을 구현하자.

TO-DO

  • 포스트 삭제 컨트롤러, 서비스 테스트 작성
  • 포스트 삭제 컨트롤러 구현
  • 포스트 삭제 서비스 구현
  • 포스트 리스트 조회 컨트롤러 테스트 작성
  • 포스트 리스트 조회 컨트롤러 구현
  • 포스트 리스트 조회 서비스 구현

포스트 삭제 컨트롤러, 서비스 테스트 작성

컨트롤러 테스트 메서드 추가

@Test
    @DisplayName("포스트 삭제 성공")
    @WithMockUser
    void post_delete_SUCCESS() throws Exception {
        String title = "테스트";
        String body = "테스트입니다.";

        when(postService.delete(any(),any()))
                .thenReturn(new PostResponse("포스트 삭제 완료", 1l));

        mockMvc.perform(delete("/api/v1/posts/1")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsBytes(new PostRequest(title, body))))
                .andDo(print())
                .andExpect(status().isOk());
    }
    @Test
    @DisplayName("포스트 삭제 실패1_인증")
    @WithAnonymousUser
    void post_delete_FAILED_authentication() throws Exception {
        String title = "테스트";
        String body = "테스트입니다.";

        when(postService.delete(any(),any()))
                .thenThrow(new AppException(ErrorCode.INVALID_PERMISSION,ErrorCode.INVALID_PERMISSION.getMessage()));

        mockMvc.perform(delete("/api/v1/posts/1")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsBytes(new PostRequest(title, body))))
                .andDo(print())
                .andExpect(status().isUnauthorized());
    }

    @Test
    @DisplayName("포스트 삭제 실패2_작성자 불일치")
    @WithMockUser
    void post_delete_FAILED_different() throws Exception {
        String title = "테스트";
        String body = "테스트입니다.";

        when(postService.delete(any(),any()))
                .thenThrow(new AppException(ErrorCode.INVALID_PERMISSION,ErrorCode.INVALID_PERMISSION.getMessage()));

        mockMvc.perform(delete("/api/v1/posts/1")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsBytes(new PostRequest(title, body))))
                .andDo(print())
                .andExpect(status().isUnauthorized());
    }

    @Test
    @DisplayName("포스트 삭제 실패3_데이터베이스 에러")
    @WithMockUser
    void post_delete_FAILED_db() throws Exception {
        String title = "테스트";
        String body = "테스트입니다.";

        when(postService.delete(any(),any()))
                .thenThrow(new AppException(ErrorCode.DATABASE_ERROR,ErrorCode.DATABASE_ERROR.getMessage()));

        mockMvc.perform(delete("/api/v1/posts/1")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsBytes(new PostRequest(title, body))))
                .andDo(print())
                .andExpect(status().isInternalServerError());
    }

서비스 테스트 메서드 추가

@Test
    @DisplayName("포스트 삭제 성공")
    void post_delete_SUCCESS() {
        when(postRepository.findById(any()))
                .thenReturn(Optional.of(post));
        when(userRepository.findByUserName(any()))
                .thenReturn(Optional.of(user));

        assertDoesNotThrow(() -> postService.delete(user.getUserName(), post.getId()));
    }

    @Test
    @DisplayName("포스트 삭제 실패1_포스트가 존재하지 않는 경우")
    void post_delete_FAILED_not_found_post() {
        when(postRepository.findById(any()))
                .thenReturn(Optional.empty());
        when(userRepository.findByUserName(any()))
                .thenReturn(Optional.of(user));

        AppException exception = assertThrows(AppException.class, () -> postService.delete(user.getUserName(), post.getId()));
        assertEquals(ErrorCode.POST_NOT_FOUND, exception.getErrorCode());
    }

    @Test
    @DisplayName("포스트 수정 실패2_포스트 작성자와 유저가 다른 경우")
    void post_delete_FAILED_different() {
        when(postRepository.findById(any()))
                .thenReturn(Optional.of(post));
        when(userRepository.findByUserName(any()))
                .thenReturn(Optional.of(user2));

        AppException exception = assertThrows(AppException.class, () -> postService.delete(user2.getUserName(), post.getId()));
        assertEquals(ErrorCode.INVALID_PERMISSION, exception.getErrorCode());
    }

    @Test
    @DisplayName("포스트 수정 실패3_유저가 존재하지 않는 경우")
    void post_delete_FAILED_not_found_userName() {
        when(postRepository.findById(any()))
                .thenReturn(Optional.of(post));
        when(userRepository.findByUserName(any()))
                .thenReturn(Optional.empty());

        AppException exception = assertThrows(AppException.class, () -> postService.delete(user.getUserName(), post.getId()));
        assertEquals(ErrorCode.INVALID_PERMISSION, exception.getErrorCode());
    }

포스트 삭제 컨트롤러 구현

@DeleteMapping("/{id}")
    public ResponseEntity<Response> deletePost(@PathVariable Long id, Authentication authentication) {
        String userName = null;
        try {
            userName = authentication.getName();
        } catch (Exception e) {
            throw new AppException(ErrorCode.INVALID_TOKEN, ErrorCode.INVALID_TOKEN.getMessage());
        }
        PostResponse postResponse = postService.delete(userName, id);
        return ResponseEntity.ok().body(Response.of("SUCCESS", postResponse));
    }

포스트 삭제 서비스 구현

public PostResponse delete(String userName, Long id) {
        //포스트 체크
        Post deletePost = postRepository.findById(id).orElseThrow(() -> {
            throw new AppException(ErrorCode.POST_NOT_FOUND, ErrorCode.POST_NOT_FOUND.getMessage());
        });
        //유저 체크
        User user = userRepository.findByUserName(userName).orElseThrow(() -> {
            throw new AppException(ErrorCode.INVALID_PERMISSION, ErrorCode.INVALID_PERMISSION.getMessage());
        });
        //포스트 유저와 유처 비교
        if (!deletePost.getUser().getUserName().equals(user.getUserName())) {
            throw new AppException(ErrorCode.INVALID_PERMISSION, ErrorCode.INVALID_PERMISSION.getMessage());
        }
        //포스트 삭제 후 DTO 리턴
        postRepository.deleteById(deletePost.getId());
        PostResponse postResponse = PostResponse.of("포스트 삭제 완료", deletePost.getId());
        return postResponse;
    }

포스트 리스트 조회 컨트롤러 테스트 작성

@Test
    @DisplayName("pageable 파라미터 검증")
    @WithMockUser
    void post_get_posts_SUCCESS() throws Exception {
        mockMvc.perform(get("/api/v1/posts")
                        .with(csrf())
                        .param("page", "0")
                        .param("size", "3")
                        .param("sort", "createdAt,desc"))
                .andExpect(status().isOk());

        ArgumentCaptor<Pageable> pageableCaptor = ArgumentCaptor.forClass(Pageable.class);

        verify(postService).getPosts(pageableCaptor.capture());
        PageRequest pageRequest = (PageRequest) pageableCaptor.getValue();

        assertEquals(0, pageRequest.getPageNumber());
        assertEquals(3, pageRequest.getPageSize());
        assertEquals(Sort.by("createdAt","desc"), pageRequest.withSort(Sort.by("createdAt","desc")).getSort());
    }

포스트 리스트 조회 컨트롤러 작성

@GetMapping
    public ResponseEntity<Response<Page<PostGetResponse>>> getPosts(@PageableDefault(size = 20, sort = "createdAt",direction = Sort.Direction.DESC) Pageable pageable){
        Page<PostGetResponse> postGetResponses = postService.getPosts(pageable);
        return ResponseEntity.ok().body(Response.of("SUCCESS",postGetResponses));
    }

포스트 리스트 조회 서비스 구현

PostService 메서드 추가

public Page<PostGetResponse> getPosts(Pageable pageable) {
        Page<Post> posts = postRepository.findAll(pageable);
        Page<PostGetResponse> postGetResponses = PostGetResponse.toResponses(posts);
        return postGetResponses;
    }

PostGetResponse 메서드 추가

public static Page<PostGetResponse> toResponses(Page<Post> posts) {
        Page<PostGetResponse> postGetResponses = posts.map(m -> PostGetResponse.builder()
                .id(m.getId())
                .title(m.getTitle())
                .body(m.getBody())
                .userName(m.getUser().getUserName())
                .createdAt(m.getCreatedAt())
                .lastModifiedAt(m.getModifiedAt())
                .build());
        return postGetResponses;
    }

오늘의 회고록

  • 오늘 활동을 끝으로 첫번째 미션인 CRUD API 구현을 마쳤다. 프로젝트를 진행하면서 수업에서 이해하지 못한 부분을 이해하면서 공부한 순간도 있었지만 아직 내가 부족하다는 느낌 또한 들었다.
  • 스프링 기본기에 대한 공부 필요성을 느꼈다.
profile
주니어 개발자를 향해서..

0개의 댓글