Skip to content

Commit

Permalink
Feat: 도서 좋아요 취소 api 구현 (#98)
Browse files Browse the repository at this point in the history
* #97 - feat: 도서 좋아요 취소 api 구현

* #97 - fix: 도서 좋아요시 이미 좋아요 정보가 존재하는지 확인 로직 추가

* #97 - text: 좋아요 취소 기능 테스트 코드 작성
  • Loading branch information
GGHDMS authored Feb 9, 2024
1 parent 3d0dfdf commit f3c6a30
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.net.URI;
Expand Down Expand Up @@ -38,4 +35,14 @@ public ResponseEntity<Void> registerLike(
return ResponseEntity.created(location).build();
}

@DeleteMapping
public ResponseEntity<Void> deleteLike(
@Valid @RequestBody BookIsbn13Request request,
@AuthenticationPrincipal AuthDetails details
) {
bookLikeService.deleteLike(request.isbn13(), details.getId());

return ResponseEntity.noContent().build();
}

}
4 changes: 4 additions & 0 deletions src/main/java/cotato/bookitlist/book/domain/entity/Book.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,8 @@ public void increaseLikeCount() {
this.likeCount++;
}

public void decreaseLikeCount() {
this.likeCount--;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ public static BookLike of(Book book, Member member ) {
public void increaseBookLikeCount() {
book.increaseLikeCount();
}

public void decreaseBookLikeCount() {
book.decreaseLikeCount();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,11 @@
import cotato.bookitlist.book.domain.entity.BookLike;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface BookLikeRepository extends JpaRepository<BookLike, Long> {

boolean existsByBook_Isbn13AndMemberId(String isbn13, Long memberId);

Optional<BookLike> findByBook_Isbn13AndMemberId(String isbn13, Long memberId);
}
15 changes: 15 additions & 0 deletions src/main/java/cotato/bookitlist/book/service/BookLikeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import cotato.bookitlist.book.repository.BookRepository;
import cotato.bookitlist.member.domain.Member;
import cotato.bookitlist.member.repository.MemberRepository;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -21,6 +23,10 @@ public class BookLikeService {
private final MemberRepository memberRepository;

public Long registerLike(String isbn13, Long memberId) {
if (bookLikeRepository.existsByBook_Isbn13AndMemberId(isbn13, memberId)) {
throw new DuplicateKeyException("도서 좋아요가 이미 존재합니다.");
}

Book book = bookRepository.findByIsbn13(isbn13)
.orElseGet(() -> bookRepository.getReferenceById(
bookService.registerBook(isbn13)
Expand All @@ -33,4 +39,13 @@ public Long registerLike(String isbn13, Long memberId) {

return bookLikeRepository.save(bookLike).getId();
}

public void deleteLike(String isbn13, Long memberId) {
BookLike bookLike = bookLikeRepository.findByBook_Isbn13AndMemberId(isbn13, memberId)
.orElseThrow(() -> new EntityNotFoundException("도서 좋아요 정보를 찾을 수 없습니다."));

bookLike.decreaseBookLikeCount();

bookLikeRepository.delete(bookLike);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@Transactional
@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("도서 좋아요 컨트롤러 테스트")
Expand All @@ -32,7 +35,7 @@ class BookLikeControllerTest {
@Test
@WithCustomMockUser
@DisplayName("도서 좋아요를 등록한다.")
void givenIsbn13_whenRegisteringLike_thenRegisterLike() throws Exception {
void givenIsbn13_whenRegisteringBookLike_thenRegisterBookLike() throws Exception {
//given
BookIsbn13Request request = new BookIsbn13Request("9791127278199");

Expand All @@ -50,7 +53,7 @@ void givenIsbn13_whenRegisteringLike_thenRegisterLike() throws Exception {
@Test
@WithCustomMockUser
@DisplayName("DB에 등록되지 않은 도서 좋아요를 등록한다.")
void givenIsbn13NonExistedInDB_whenRegisteringLike_thenRegisterLike() throws Exception {
void givenIsbn13NonExistedInDB_whenRegisteringBookLike_thenRegisterBookLike() throws Exception {
//given
BookIsbn13Request request = new BookIsbn13Request("9788966262281");

Expand All @@ -61,6 +64,56 @@ void givenIsbn13NonExistedInDB_whenRegisteringLike_thenRegisterLike() throws Exc
)
.andExpect(status().isCreated())
.andExpect(header().exists("Location"))
;
}

@Test
@WithCustomMockUser
@DisplayName("이미 도서 좋아요 정보가 존재하면 에러를 반환한다.")
void givenExistedBookLike_whenRegisteringBookLike_thenErrorResponse() throws Exception {
//given
BookIsbn13Request request = new BookIsbn13Request("9788931514810");

//when & then
mockMvc.perform(post("/books/likes")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsBytes(request))
)
.andExpect(status().isConflict())
.andDo(print())
;
}

@Test
@WithCustomMockUser
@DisplayName("좋아요 정보를 삭제한다.")
void givenIsbn13_whenDeletingBookLike_thenDeleteBookLike() throws Exception {
//given
BookIsbn13Request request = new BookIsbn13Request("9788931514810");

//when & then
mockMvc.perform(delete("/books/likes")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsBytes(request))
)
.andExpect(status().isNoContent())
.andDo(print())
;
}

@Test
@WithCustomMockUser
@DisplayName("존재하지 않는 좋아요 삭제시 에러를 반환한다.")
void givenNonExistedBookLike_whenDeletingBookLike_thenReturnErrorResponse() throws Exception {
//given
BookIsbn13Request request = new BookIsbn13Request("9791187824824");

//when & then
mockMvc.perform(delete("/books/likes")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsBytes(request))
)
.andExpect(status().isNotFound())
.andDo(print())
;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.BDDMockito.*;

@DisplayName("도서 좋아요 서비스 테스트")
@ExtendWith(MockitoExtension.class)
Expand All @@ -46,6 +45,7 @@ void givenIsbn13_whenRegisteringBookLike_thenRegisterBookLike() throws Exception
Member member = createMember();
Book book = createBook(isbn13);

given(bookLikeRepository.existsByBook_Isbn13AndMemberId(isbn13, member.getId())).willReturn(false);
given(bookRepository.findByIsbn13(isbn13)).willReturn(Optional.of(book));
given(memberRepository.getReferenceById(member.getId())).willReturn(member);
given(bookLikeRepository.save(any(BookLike.class))).willReturn(createBookLike(book, member));
Expand All @@ -54,10 +54,11 @@ void givenIsbn13_whenRegisteringBookLike_thenRegisterBookLike() throws Exception
sut.registerLike(isbn13, member.getId());

//then
then(bookLikeRepository).should().existsByBook_Isbn13AndMemberId(isbn13, member.getId());
then(bookRepository).should().findByIsbn13(isbn13);
then(memberRepository).should().getReferenceById(member.getId());
then(bookLikeRepository).should().save(any(BookLike.class));
assertThat(book.getLikeCount()).isEqualTo(1);
assertThat(book.getLikeCount()).isOne();
}

@Test
Expand All @@ -68,6 +69,7 @@ void givenIsbn13NonExistedInDB_whenRegisteringBookLike_thenRegisterBookLike() th
Member member = createMember();
Book book = createBook(isbn13);

given(bookLikeRepository.existsByBook_Isbn13AndMemberId(isbn13, member.getId())).willReturn(false);
given(bookRepository.findByIsbn13(isbn13)).willReturn(Optional.empty());
given(bookService.registerBook(isbn13)).willReturn(book.getId());
given(bookRepository.getReferenceById(book.getId())).willReturn(book);
Expand All @@ -78,14 +80,38 @@ void givenIsbn13NonExistedInDB_whenRegisteringBookLike_thenRegisterBookLike() th
sut.registerLike(isbn13, member.getId());

//then
then(bookLikeRepository).should().existsByBook_Isbn13AndMemberId(isbn13, member.getId());
then(bookRepository).should().findByIsbn13(isbn13);
then(bookService).should().registerBook(isbn13);
then(bookRepository).should().getReferenceById(book.getId());
then(memberRepository).should().getReferenceById(member.getId());
then(bookLikeRepository).should().save(any(BookLike.class));
assertThat(book.getLikeCount()).isEqualTo(1);
assertThat(book.getLikeCount()).isOne();
}

@Test
@DisplayName("도서 좋아요 삭제시 LikeCount가 감소한다.")
void givenIsbn13_whenDeletingBookLike_thenDeleteBookLike() throws Exception {
//given
String isbn13 = "9788931514810";
Member member = createMember();
Book book = createBook(isbn13);
BookLike bookLike = createBookLike(book, member);
book.increaseLikeCount();

given(bookLikeRepository.findByBook_Isbn13AndMemberId(isbn13, member.getId())).willReturn(Optional.of(bookLike));
willDoNothing().given(bookLikeRepository).delete(bookLike);

//when
sut.deleteLike(isbn13, member.getId());

//then
then(bookLikeRepository).should().findByBook_Isbn13AndMemberId(isbn13, member.getId());
then(bookLikeRepository).should().delete(bookLike);
assertThat(book.getLikeCount()).isZero();
}


Book createBook(String isbn13) {
return Book.of("title", "author", "pubisher", LocalDate.now(), "description", "link", isbn13, 10000, "cover");
}
Expand Down
4 changes: 4 additions & 0 deletions src/test/resources/data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ VALUES (1, 1, 'reviewContent', 0, 0, false),
INSERT INTO post_like (member_id, post_id)
VALUES (1, 2),
(2, 2);

INSERT INTO book_like (book_id, member_id)
VALUES (1, 1),
(2, 1);

0 comments on commit f3c6a30

Please sign in to comment.