diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesApi.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesApi.java index deb8e5ea..32a81189 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesApi.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesApi.java @@ -33,45 +33,6 @@ ResponseEntity registerMemes( GenerateMemesRequest generateMemesRequest ); - @Operation( - summary = "meme`s 인기차트 조회", - description = "좋아요 수 많은 순으로 10개 조회", - security = {@SecurityRequirement(name = "access_token")} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "인기차트 조회" - ) - }) - ResponseEntity findTopMemesByLike(); - - @Operation( - summary = "meme`s 이달의 인기차트 조회", - description = "최근 한 달 동안 좋아요 수 많은 순으로 10개 조회", - security = {@SecurityRequirement(name = "access_token")} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "이달의 인기차트 조회" - ) - }) - ResponseEntity findTopMemesByLikeForMonth(); - - @Operation( - summary = "meme`s 이주의 인기차트 조회", - description = "최근 한 주 동안 좋아요 수 많은 순으로 10개 조회", - security = {@SecurityRequirement(name = "access_token")} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "이주의 인기차트 조회" - ) - }) - ResponseEntity findTopMemesByLikeForWeek(); - @Operation( summary = "meme`s 삭제", description = "meme`s 삭제", @@ -115,38 +76,4 @@ ResponseEntity findMemesResponse( ) }) ResponseEntity findMemesInfoSliceResponse(Pageable pageable); - - @Operation( - summary = "meme`s 좋아요 등록", - description = "공유된 meme`s 게시물에 좋아요를 등록한다.", - security = {@SecurityRequirement(name = "access_token")} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "201", - description = "좋아요 등록!" - ) - }) - ResponseEntity registerLike( - @Parameter(hidden = true) String email, - @Parameter(in = ParameterIn.PATH, description = "밈스 아이디", required = true) - Long memesId - ); - - @Operation( - summary = "meme`s 좋아요 취소", - description = "공유된 meme`s 게시물에 등록된 좋아요를 취소한다.", - security = {@SecurityRequirement(name = "access_token")} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "좋아요 취소!" - ) - }) - ResponseEntity cancelLike( - @Parameter(hidden = true) String email, - @Parameter(in = ParameterIn.PATH, description = "밈스 아이디", required = true) - Long memesId - ); } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesController.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesController.java index 8110eacb..0e0b245a 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesController.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesController.java @@ -2,8 +2,6 @@ import static com.example.memetory.global.response.ResultCode.*; -import java.util.List; - import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -15,11 +13,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.example.memetory.domain.like.dto.LikeServiceDto; -import com.example.memetory.domain.like.service.LikeService; import com.example.memetory.domain.memes.dto.MemesServiceDto; import com.example.memetory.domain.memes.dto.request.GenerateMemesRequest; -import com.example.memetory.domain.memes.dto.response.MemesInfoResponse; import com.example.memetory.domain.memes.dto.response.MemesInfoSliceResponse; import com.example.memetory.domain.memes.dto.response.MemesResponse; import com.example.memetory.domain.memes.service.MemesService; @@ -33,7 +28,6 @@ @RequestMapping("/memes") public class MemesController implements MemesApi { private final MemesService memesService; - private final LikeService likeService; @GetMapping @Override @@ -68,43 +62,4 @@ public ResponseEntity deleteMemes(@LoginMemberEmail String email memesService.deleteMemes(memesServiceDto); return ResponseEntity.ok(ResultResponse.of(DELETE_MEMES_SUCCESS)); } - - @GetMapping("/like/all") - @Override - public ResponseEntity findTopMemesByLike() { - List response = memesService.findTopMemesByLike(); - return ResponseEntity.ok(ResultResponse.of(GET_TOP_TEN_MEMES_SUCCESS, response)); - } - - @GetMapping("/like/month") - @Override - public ResponseEntity findTopMemesByLikeForMonth() { - List response = memesService.findTopMemesByLikeForMonth(); - return ResponseEntity.ok(ResultResponse.of(GET_MONTH_TOP_TEN_MEMES_SUCCESS, response)); - } - - @GetMapping("/like/week") - @Override - public ResponseEntity findTopMemesByLikeForWeek() { - List response = memesService.findTopMemesByLikeForWeek(); - return ResponseEntity.ok(ResultResponse.of(GET_WEEK_TOP_TEN_MEMES_SUCCESS, response)); - } - - @PostMapping("/{memesId}/like") - @Override - public ResponseEntity registerLike(@LoginMemberEmail String email, @PathVariable Long memesId) { - LikeServiceDto likeServiceDto = LikeServiceDto.fromEmailAndMemesId(email, memesId); - likeService.registerLike(likeServiceDto); - - return ResponseEntity.status(HttpStatus.CREATED).body(ResultResponse.of(CREATE_LIKE_SUCCESS)); - } - - @DeleteMapping("/{memesId}/like") - @Override - public ResponseEntity cancelLike(@LoginMemberEmail String email, @PathVariable Long memesId) { - LikeServiceDto likeServiceDto = LikeServiceDto.fromEmailAndMemesId(email, memesId); - likeService.cancelLike(likeServiceDto); - - return ResponseEntity.ok(ResultResponse.of(DELETE_LIKE_SUCCESS)); - } } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesLikeApi.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesLikeApi.java new file mode 100644 index 00000000..cf15945a --- /dev/null +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesLikeApi.java @@ -0,0 +1,87 @@ +package com.example.memetory.domain.memes.controller; + +import org.springframework.http.ResponseEntity; + +import com.example.memetory.global.response.ResultResponse; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; + +public interface MemesLikeApi { + @Operation( + summary = "meme`s 인기차트 조회", + description = "좋아요 수 많은 순으로 10개 조회", + security = {@SecurityRequirement(name = "access_token")} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "인기차트 조회" + ) + }) + ResponseEntity findTopMemesByLike(); + + @Operation( + summary = "meme`s 이달의 인기차트 조회", + description = "최근 한 달 동안 좋아요 수 많은 순으로 10개 조회", + security = {@SecurityRequirement(name = "access_token")} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "이달의 인기차트 조회" + ) + }) + ResponseEntity findTopMemesByLikeForMonth(); + + @Operation( + summary = "meme`s 이주의 인기차트 조회", + description = "최근 한 주 동안 좋아요 수 많은 순으로 10개 조회", + security = {@SecurityRequirement(name = "access_token")} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "이주의 인기차트 조회" + ) + }) + ResponseEntity findTopMemesByLikeForWeek(); + + @Operation( + summary = "meme`s 좋아요 등록", + description = "공유된 meme`s 게시물에 좋아요를 등록한다.", + security = {@SecurityRequirement(name = "access_token")} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "201", + description = "좋아요 등록!" + ) + }) + ResponseEntity registerLike( + @Parameter(hidden = true) String email, + @Parameter(in = ParameterIn.PATH, description = "밈스 아이디", required = true) + Long memesId + ); + + @Operation( + summary = "meme`s 좋아요 취소", + description = "공유된 meme`s 게시물에 등록된 좋아요를 취소한다.", + security = {@SecurityRequirement(name = "access_token")} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "좋아요 취소!" + ) + }) + ResponseEntity cancelLike( + @Parameter(hidden = true) String email, + @Parameter(in = ParameterIn.PATH, description = "밈스 아이디", required = true) + Long memesId + ); +} diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesLikeController.java b/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesLikeController.java new file mode 100644 index 00000000..0ba82224 --- /dev/null +++ b/backend/memetory/src/main/java/com/example/memetory/domain/memes/controller/MemesLikeController.java @@ -0,0 +1,70 @@ +package com.example.memetory.domain.memes.controller; + +import static com.example.memetory.global.response.ResultCode.*; + +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.memetory.domain.like.dto.LikeServiceDto; +import com.example.memetory.domain.like.service.LikeService; +import com.example.memetory.domain.memes.dto.response.MemesInfoResponse; +import com.example.memetory.domain.memes.service.MemesService; +import com.example.memetory.global.annotation.LoginMemberEmail; +import com.example.memetory.global.response.ResultResponse; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/memes") +public class MemesLikeController implements MemesLikeApi { + private final MemesService memesService; + private final LikeService likeService; + + @GetMapping("/like/all") + @Override + public ResponseEntity findTopMemesByLike() { + List response = memesService.findTopMemesByLike(); + return ResponseEntity.ok(ResultResponse.of(GET_TOP_TEN_MEMES_SUCCESS, response)); + } + + @GetMapping("/like/month") + @Override + public ResponseEntity findTopMemesByLikeForMonth() { + List response = memesService.findTopMemesByLikeForMonth(); + return ResponseEntity.ok(ResultResponse.of(GET_MONTH_TOP_TEN_MEMES_SUCCESS, response)); + } + + @GetMapping("/like/week") + @Override + public ResponseEntity findTopMemesByLikeForWeek() { + List response = memesService.findTopMemesByLikeForWeek(); + return ResponseEntity.ok(ResultResponse.of(GET_WEEK_TOP_TEN_MEMES_SUCCESS, response)); + } + + @PostMapping("/{memesId}/like") + @Override + public ResponseEntity registerLike(@LoginMemberEmail String email, @PathVariable Long memesId) { + LikeServiceDto likeServiceDto = LikeServiceDto.fromEmailAndMemesId(email, memesId); + likeService.registerLike(likeServiceDto); + + return ResponseEntity.status(HttpStatus.CREATED).body(ResultResponse.of(CREATE_LIKE_SUCCESS)); + } + + @DeleteMapping("/{memesId}/like") + @Override + public ResponseEntity cancelLike(@LoginMemberEmail String email, @PathVariable Long memesId) { + LikeServiceDto likeServiceDto = LikeServiceDto.fromEmailAndMemesId(email, memesId); + likeService.cancelLike(likeServiceDto); + + return ResponseEntity.ok(ResultResponse.of(DELETE_LIKE_SUCCESS)); + } +} diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java deleted file mode 100644 index 1d05d351..00000000 --- a/backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.example.memetory.domain.member.controller; - -import static com.example.memetory.domain.member.MemberFixture.*; -import static com.example.memetory.global.response.ErrorCode.*; -import static com.example.memetory.global.response.ResultCode.*; -import static org.mockito.BDDMockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.ResultActions; - -import com.example.memetory.domain.member.dto.MemberServiceDto; -import com.example.memetory.domain.member.dto.request.MemberUpdateRequest; -import com.example.memetory.domain.member.dto.response.MemberResponse; -import com.example.memetory.domain.member.exception.DuplicatedMemberException; -import com.example.memetory.domain.member.service.MemberService; -import com.example.memetory.global.BaseControllerTest; - -@DisplayName("Member 컨트롤러 테스트의 ") -@WebMvcTest(MemberController.class) -public class MemberControllerTest extends BaseControllerTest { - @MockBean - private MemberService memberService; - - @Test - @DisplayName("MemberUpdateRequest를 통한 멤버 업데이트 성공") - public void Given_MemberUpdateRequest_When_updateMember_Then_UPDATRE_MEMBER_SUCCESS() throws Exception { - // given - MemberUpdateRequest request = new MemberUpdateRequest("junrain2", "imageUrl2"); - MemberResponse expectedResult = MemberResponse.of(MEMBER()); - String expectedNickname = expectedResult.getNickName(); - - given(memberService.updateMember(any())).willReturn(expectedResult); - - // when - final ResultActions perform = mockMvc.perform(post("/member") - .contentType(MediaType.APPLICATION_JSON) - .content(toRequestBody(request)) - .header("Authorization", "Bearer " + accessToken)) - .andDo(print()); - - // then - perform.andExpect(status().isOk()) - .andExpect(jsonPath(MESSAGE).value(UPDATE_MEMBER_SUCCESS.getMessage())) - .andExpect(jsonPath("$.data.nickName").value(expectedNickname)); - } - - @Test - @DisplayName("중복된 닉네임으로 인한 NICKNAME_IS_DUPLICATED 반환") - public void Given_MemberUpdateRequest_When_updateMember_Then_NICKNAME_IS_DUPLICATED() throws Exception { - // given - MemberUpdateRequest request = new MemberUpdateRequest("junrain2", "imageUrl2"); - - doThrow(new DuplicatedMemberException()).when(memberService).updateMember(any()); - - // when - final ResultActions perform = mockMvc.perform(post("/member") - .contentType(MediaType.APPLICATION_JSON) - .content(toRequestBody(request)) - .header("Authorization", "Bearer " + accessToken)) - .andDo(print()); - - // then - perform.andExpect(status().isConflict()) - .andExpect(jsonPath(ERROR_MESSAGE).value(NICKNAME_IS_DUPLICATED.getMessage())); - } - - @Test - @DisplayName("email을 통한 MemberResponse 반환 성공") - public void Given_email_When_findMember_Then_MemberResponse() throws Exception { - // given - MemberResponse expectedResult = MemberResponse.of(MEMBER()); - String expectedNickname = expectedResult.getNickName(); - - given(memberService.findMemberResponse(any(MemberServiceDto.class))).willReturn(expectedResult); - - // when - final ResultActions perform = mockMvc.perform(get("/member") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken)) - .andDo(print()); - - // then - perform.andExpect(status().isOk()) - .andExpect(jsonPath(MESSAGE).value(GET_MEMBER_SUCCESS.getMessage())) - .andExpect(jsonPath("$.data.nickName").value(expectedNickname)); - } -} diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/meme/controller/MemeControllerTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/meme/controller/MemeControllerTest.java deleted file mode 100644 index 36b7411b..00000000 --- a/backend/memetory/src/test/java/com/example/memetory/domain/meme/controller/MemeControllerTest.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.example.memetory.domain.meme.controller; - -import static com.example.memetory.domain.meme.MemeFixture.*; -import static com.example.memetory.global.response.ErrorCode.*; -import static com.example.memetory.global.response.ResultCode.*; -import static org.mockito.BDDMockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import java.util.List; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.ResultActions; - -import com.example.memetory.domain.meme.dto.MemePageResponse; -import com.example.memetory.domain.meme.dto.MemeResponse; -import com.example.memetory.domain.meme.dto.MemeServiceDto; -import com.example.memetory.domain.meme.exception.AccessDeniedMemeException; -import com.example.memetory.domain.meme.exception.NotFoundMemeException; -import com.example.memetory.domain.meme.service.MemeService; -import com.example.memetory.global.BaseControllerTest; - -@DisplayName("Meme 컨트롤러 테스트의 ") -@WebMvcTest(MemeController.class) -public class MemeControllerTest extends BaseControllerTest { - @MockBean - private MemeService memeService; - - /** - *Todo - * register와 callback은 외부 API를 활용하는 메서드이기 떄문에 테스트 법 공부가 필요 - */ - - @Test - @DisplayName("이메일과 밈id를 통한 멤버의 MemeResponse 반환 성공") - void Given_emailAndMemeId_When_findMemberMemeResponse_Then_Member_MemeResponse() throws Exception { - // given - MemeResponse returnedMemeResponse = MemeResponse.of(MEME(loginMember)); - given(memeService.findMemberMemeResponse(any())).willReturn(returnedMemeResponse); - - // when - final ResultActions perform = mockMvc.perform( - get("/meme/-1") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken)) - .andDo(print()); - - // then - perform.andExpect(status().isOk()) - .andExpect(jsonPath(MESSAGE, GET_ONE_MEME_SUCCESS.getMessage()).exists()) - .andExpect(jsonPath("$.data.s3Url").exists()); - } - - @Test - @DisplayName("권한 없는 멤버 이메일로 인한 AccessDeniedMemeException 반환") - void Given_NotPermissionMemberId_When_findMemberMemeResponse_Throw_AccessDeniedMemberMemeException() throws Exception { - // given - given(memeService.findMemberMemeResponse(any(MemeServiceDto.class))).willThrow(new AccessDeniedMemeException()); - - // when - final ResultActions perform = mockMvc.perform( - get("/meme/-1") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken)) - .andDo(print()); - - // then - perform.andExpect(status().isForbidden()) - .andExpect(jsonPath(ERROR_MESSAGE, MEME_ACCESS_DENY.getMessage()).exists()); - } - - @Test - @DisplayName("존재하지 않는 memeId로 인한 NotFoundMemeException 반환") - void Given_NotExistMemeId_Then_findMemberMemeResponse_Throw_NotFoundMemberMemeException() throws Exception { - // given - Long notExistMemeId = -1L; - - given(memeService.findMemberMemeResponse(any(MemeServiceDto.class))).willThrow(new NotFoundMemeException()); - - // when - final ResultActions perform = mockMvc.perform( - get("/meme/" + notExistMemeId) - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken)) - .andDo(print()); - - // then - perform.andExpect(status().isNotFound()) - .andExpect(jsonPath(ERROR_MESSAGE, MEME_NOT_FOUND.getMessage()).exists()); - } - - @Test - @DisplayName("이메일과 Pageable을 통한 멤버의 MemePageResponse 반환 성공") - void Given_emailAndPageable_When_findMemberMemePageResponse_Then_Member_MemePageResponse() throws Exception { - // given - List memeList = List.of(MEME(loginMember), OTHER_MEME(loginMember)) - .stream() - .map(MemeResponse::of) - .toList(); - MemePageResponse memePageResponse = MemePageResponse.builder().memeList(memeList).build(); - - given(memeService.findMemberMemePageResponse(any(), any())).willReturn(memePageResponse); - - // when - final ResultActions perform = mockMvc.perform( - get("/meme?page=0&size=10").contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken)).andDo(print()); - - // then - perform.andExpect(status().isOk()) - .andExpect(jsonPath(MESSAGE).value(GET_MEMBER_MEME_SUCCESS.getMessage())) - .andExpect(jsonPath("$.data.memeList").isArray()); - } -} diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/memes/MemesIntegrationTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/memes/MemesIntegrationTest.java new file mode 100644 index 00000000..6f40120b --- /dev/null +++ b/backend/memetory/src/test/java/com/example/memetory/domain/memes/MemesIntegrationTest.java @@ -0,0 +1,202 @@ +package com.example.memetory.domain.memes; + +import static com.example.memetory.domain.member.MemberFixture.*; +import static com.example.memetory.domain.meme.MemeFixture.*; +import static com.example.memetory.domain.memes.MemesFixture.*; +import static com.example.memetory.global.response.ErrorCode.*; +import static com.example.memetory.global.response.ResultCode.*; +import static io.restassured.RestAssured.*; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; + +import com.example.memetory.domain.member.entity.Member; +import com.example.memetory.domain.meme.entity.Meme; +import com.example.memetory.domain.meme.repository.MemeRepository; +import com.example.memetory.domain.memes.dto.request.GenerateMemesRequest; +import com.example.memetory.domain.memes.dto.response.MemesInfoSliceResponse; +import com.example.memetory.domain.memes.dto.response.MemesResponse; +import com.example.memetory.domain.memes.entity.Memes; +import com.example.memetory.domain.memes.repository.MemesRepository; +import com.example.memetory.global.integration.BaseIntegrationTest; + +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; + +@DisplayName("Memes 통합 테스트의 ") +public class MemesIntegrationTest extends BaseIntegrationTest { + @Autowired + private MemeRepository memeRepository; + @Autowired + private MemesRepository memesRepository; + + @Test + @DisplayName("GenerateMemesRequest을 통한 Memes 생성 성공") + public void Given_GenerateMemesRequest_When_registerMemes_Then_MemesResponse() { + // given + Meme meme = memeRepository.save(MEME(member)); + + final String title = "new Memes"; + GenerateMemesRequest request = new GenerateMemesRequest(meme.getId(), title); + + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .when() + .post("/memes") + .then() + .log() + .all() + .extract(); + + MemesResponse result = response.jsonPath().getObject("data", MemesResponse.class); + + // then + assertThat(result.getTitle()).isEqualTo(title); + } + + @Test + @DisplayName("존재하지 않는 MemeId로 인한 MEME_NOT_FOUND 반환") + public void Given_NotExistedMemeId_When_findMemberMemePageResponse_Throw_NotFoundMemberMemeException() { + // given + final String title = "new Memes"; + GenerateMemesRequest request = new GenerateMemesRequest(-1L, title); + + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .when() + .post("/memes") + .then() + .log() + .all() + .extract(); + + String result = response.jsonPath().get(ERROR_MESSAGE); + + // then + assertThat(result).isEqualTo(MEME_NOT_FOUND.getMessage()); + } + + @Test + @DisplayName("memesId를 통한 밈스 삭제 성공") + public void Given_memesId_When_deleteMemes_Then_DELETE_MEMES_SUCCESS() { + // given + Meme meme = memeRepository.save(MEME(member)); + Memes memes = memesRepository.save(MEMES(member, meme)); + + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .when() + .delete("/memes/{memesId}", memes.getId()) + .then() + .log() + .all() + .extract(); + + String result = response.jsonPath().get(MESSAGE); + + // then + assertThat(result).isEqualTo(DELETE_MEMES_SUCCESS.getMessage()); + } + + @DisplayName("권한 없는 멤버로 인한 MEMES_ACCESS_DENY 반환") + @Test + public void Given_unAuthorizedMember_When_deleteMemes_Then_MEMES_ACCESS_DENY() { + // given + Member unAuthorizationMember = memberRepository.save(OTHER_MEMBER()); + Meme meme = memeRepository.save(MEME(unAuthorizationMember)); + Memes memes = memesRepository.save(MEMES(unAuthorizationMember, meme)); + + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .when() + .delete("/memes/{memesId}", memes.getId()) + .then() + .log() + .all() + .extract(); + + String result = response.jsonPath().get(ERROR_MESSAGE); + + // then + assertThat(result).isEqualTo(MEMES_ACCESS_DENY.getMessage()); + } + + @Test + @DisplayName("memesId를 통한 MemesResponse 반환 성공") + public void Given_memesId_When_findMemesResponse_Thee_MemeResponse() { + Meme meme = memeRepository.save(MEME(member)); + Memes memes = memesRepository.save(MEMES(member, meme)); + + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .when() + .get("/memes/{memesId}", memes.getId()) + .then() + .log() + .all() + .extract(); + + MemesResponse result = response.jsonPath().getObject("data", MemesResponse.class); + + // then + assertThat(result.getMemesId()).isEqualTo(memes.getId()); + } + + @Test + @DisplayName("Pageable을 통한 MemesInfoSliceResponse 반환 성공") + public void Given_Pageable_When_findMemesInfoSliceResponse_Then_MemesInfoPageResponse() throws Exception { + final int SIZE = 10; + Meme meme = memeRepository.save(MEME(member)); + + for (int i = 0; i < SIZE + 1; i++) { + memesRepository.save(MEMES(member, meme)); + } + + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .when() + .get("/memes?page={page}&size={size}", 0, SIZE) + .then() + .log() + .all() + .extract(); + + MemesInfoSliceResponse result = response.jsonPath().getObject("data", MemesInfoSliceResponse.class); + + // then + assertTrue(result.isHasNext()); + assertThat(result.getMemesInfoResponseList()).hasSize(SIZE); + } +} diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/memes/MemesLikeIntegrationTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/memes/MemesLikeIntegrationTest.java new file mode 100644 index 00000000..bb93efb8 --- /dev/null +++ b/backend/memetory/src/test/java/com/example/memetory/domain/memes/MemesLikeIntegrationTest.java @@ -0,0 +1,219 @@ +package com.example.memetory.domain.memes; + +import static com.example.memetory.domain.like.LikeFixture.*; +import static com.example.memetory.domain.meme.MemeFixture.*; +import static com.example.memetory.domain.memes.MemesFixture.*; +import static com.example.memetory.global.response.ErrorCode.*; +import static com.example.memetory.global.response.ResultCode.*; +import static io.restassured.RestAssured.*; +import static org.assertj.core.api.Assertions.*; + +import java.time.LocalDate; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.ZSetOperations; + +import com.example.memetory.domain.like.entity.Like; +import com.example.memetory.domain.like.repository.LikeRepository; +import com.example.memetory.domain.meme.entity.Meme; +import com.example.memetory.domain.meme.repository.MemeRepository; +import com.example.memetory.domain.memes.dto.response.MemesInfoResponse; +import com.example.memetory.domain.memes.entity.Memes; +import com.example.memetory.domain.memes.repository.MemesRepository; +import com.example.memetory.global.integration.BaseIntegrationTest; + +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; + +@DisplayName("MemesLike 통합 테스트의 ") +public class MemesLikeIntegrationTest extends BaseIntegrationTest { + @Autowired + MemeRepository memeRepository; + @Autowired + MemesRepository memesRepository; + @Autowired + LikeRepository likeRepository; + + @Autowired + private ZSetOperations rankingZSet; + @Autowired + private RedisConnectionFactory redisConnectionFactory; + + private Meme meme; + private Memes memes; + + @Override + @BeforeEach + public void setUp() { + super.setUp(); + redisConnectionFactory.getConnection().flushAll(); + + meme = memeRepository.save(MEME(member)); + memes = memesRepository.save(MEMES(member, meme)); + } + + @Test + @DisplayName("memesId를 통한 좋아요 등록 성공") + void Given_memesId_When_registerLike_Then_CREATE_LIKE_SUCCESS() { + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .when() + .post("/memes/{memesId}/like", memes.getId()) + .then() + .log() + .all() + .extract(); + + String result = response.jsonPath().get(MESSAGE); + + // then + assertThat(result).isEqualTo(CREATE_LIKE_SUCCESS.getMessage()); + memesRepository.findByMemesId(memes.getId()) + .ifPresent(m -> assertThat(m.getLikeCount()).isEqualTo(2L)); + } + + @Test + @DisplayName("DB에 존재하는 좋아요로 인한 LIKE_NOT_CREATE 반환") + void Given_ExistLike_When_registerLike_Then_LIKE_NOT_CREATE() throws Exception { + // given + likeRepository.save(LIKE(member, memes)); + + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .when() + .post("/memes/{memesId}/like", memes.getId()) + .then() + .log() + .all() + .extract(); + + String result = response.jsonPath().get(ERROR_MESSAGE); + + // then + assertThat(result).isEqualTo(LIKE_NOT_CREATE.getMessage()); + } + + @Test + @DisplayName("memesId를 통한 좋아요 삭제 성공") + void Given_memesId_When_cancelLike_Then_DELETE_LIKE_SUCCESS() { + // given + Like like = likeRepository.save(LIKE(member, memes)); + + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .when() + .delete("/memes/{memesId}/like", memes.getId()) + .then() + .log() + .all() + .extract(); + + String result = response.jsonPath().get(MESSAGE); + + // then + assertThat(result).isEqualTo(DELETE_LIKE_SUCCESS.getMessage()); + } + + @Test + @DisplayName("DB에 없는 좋아요로 인한 LIKE_NOT_FOUND 반환") + void Given_NotExistLike_When_cancelLike_Then_LIKE_NOT_FOUND() { + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .when() + .delete("/memes/{memesId}/like", memes.getId()) + .then() + .log() + .all() + .extract(); + + String result = response.jsonPath().get(ERROR_MESSAGE); + + // then + assertThat(result).isEqualTo(LIKE_NOT_FOUND.getMessage()); + } + + @Test + @DisplayName("올타임 좋아요 Top 10 밈스 반환 성공") + public void When_findTopMemesByLike_Then_GET_TOP_TEN_MEMES_SUCCESS() { + // given + final Long SIZE = 15L; + + for (long i = 0; i < SIZE; i++) { + memesRepository.save(MEMES_SET_LIKE(member, meme, i)); + } + + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .when() + .get("/memes/like/all") + .then() + .log() + .all() + .extract(); + + List responses = response.jsonPath().getList("data", MemesInfoResponse.class); + + // then + assertThat(responses).hasSize(10); + assertThat(responses.get(0).getLikeCount()).isEqualTo(SIZE - 1); + } + + @Test + @DisplayName("주간 좋아요 Top 10 밈스 반환 성공") + public void When_findTopMemesByLikeForWeek_Then_GET_WEEK_TOP_TEN_MEMES_SUCCESS() { + // given + final Long SIZE = 15L; + String dateKey = "LIKE_RANKING_DATE::" + LocalDate.now().minusDays(1); + + for (Long i = 0L; i < SIZE; i++) { + // 총 좋아요 수와 오늘 좋아요 수의 차이를 주기 위해 + 3 + Memes rankMemes = memesRepository.save(MEMES_SET_LIKE(member, meme, i + 3)); + + rankingZSet.add(dateKey, rankMemes.getId(), i); + } + + // when + ExtractableResponse response = + given() + .log() + .all() + .auth().oauth2(accessToken) + .when() + .get("/memes/like/week") + .then() + .log() + .all() + .extract(); + + List responses = response.jsonPath().getList("data", MemesInfoResponse.class); + + // then + assertThat(responses).hasSize(10); + assertThat(responses.get(0).getLikeCount()).isEqualTo(SIZE - 1); + } +} diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/memes/controller/MemesControllerTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/memes/controller/MemesControllerTest.java deleted file mode 100644 index bca2c480..00000000 --- a/backend/memetory/src/test/java/com/example/memetory/domain/memes/controller/MemesControllerTest.java +++ /dev/null @@ -1,246 +0,0 @@ -package com.example.memetory.domain.memes.controller; - -import static com.example.memetory.domain.meme.MemeFixture.*; -import static com.example.memetory.domain.memes.MemesFixture.*; -import static com.example.memetory.global.response.ErrorCode.*; -import static com.example.memetory.global.response.ResultCode.*; -import static org.mockito.BDDMockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import java.util.List; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.ResultActions; - -import com.example.memetory.domain.like.dto.LikeServiceDto; -import com.example.memetory.domain.like.exception.NotCreateLikeException; -import com.example.memetory.domain.like.exception.NotFoundLikeException; -import com.example.memetory.domain.like.service.LikeService; -import com.example.memetory.domain.meme.entity.Meme; -import com.example.memetory.domain.memes.dto.MemesServiceDto; -import com.example.memetory.domain.memes.dto.request.GenerateMemesRequest; -import com.example.memetory.domain.memes.dto.response.MemesInfoResponse; -import com.example.memetory.domain.memes.dto.response.MemesInfoSliceResponse; -import com.example.memetory.domain.memes.dto.response.MemesResponse; -import com.example.memetory.domain.memes.exception.AccessDinedMemesException; -import com.example.memetory.domain.memes.service.MemesService; -import com.example.memetory.global.BaseControllerTest; - -@DisplayName("Memes 컨트롤러 테스트의 ") -@WebMvcTest(MemesController.class) -public class MemesControllerTest extends BaseControllerTest { - @MockBean - private MemesService memesService; - @MockBean - private LikeService likeService; - - @Test - @DisplayName("GenerateMemesRequest을 통한 Memes 생성 성공") - public void Given_GenerateMemesRequest_When_registerMemes_Then_MemesResponse() throws Exception { - // given - final String title = "new Memes"; - - Meme meme = MEME(loginMember); - - GenerateMemesRequest request = new GenerateMemesRequest(meme.getId(), title); - MemesResponse response = MemesResponse.of(MEMES(loginMember, meme)); - - given(memesService.registerMemes(any(MemesServiceDto.class))).willReturn(response); - - // when - final ResultActions perform = mockMvc.perform( - post("/memes") - .contentType(MediaType.APPLICATION_JSON) - .content(toRequestBody(request)) - .header("Authorization", "Bearer " + accessToken) - ).andDo(print()); - - // then - perform.andExpect(status().isCreated()) - .andExpect(jsonPath(MESSAGE).value(CREATE_MEMES_SUCCESS.getMessage())) - .andExpect(jsonPath("$.data.title").value(title)); - } - - @Test - @DisplayName("memesId를 통한 밈스 삭제 성공") - public void Given_memesId_When_deleteMemes_Then_DELETE_MEMES_SUCCESS() throws Exception { - // when - final ResultActions perform = mockMvc.perform( - delete("/memes/-1") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken) - ).andDo(print()); - - // then - perform.andExpect(status().isOk()) - .andExpect(jsonPath(MESSAGE).value(DELETE_MEMES_SUCCESS.getMessage())); - } - - @Test - @DisplayName("권한 없는 멤버로 인한 MEMES_ACCESS_DENY 반환") - public void Given_unAuthorizedMember_When_deleteMemes_Then_MEMES_ACCESS_DENY() throws Exception { - // given - doThrow(new AccessDinedMemesException()).when(memesService).deleteMemes(any()); - - // when - final ResultActions perform = mockMvc.perform( - delete("/memes/-1") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken) - ).andDo(print()); - - // then - perform.andExpect(status().isForbidden()) - .andExpect(jsonPath(ERROR_MESSAGE).value(MEMES_ACCESS_DENY.getMessage())); - } - - @Test - @DisplayName("올타임 좋아요 Top 10 밈스 반환 성공") - public void When_findTopMemesByLike_Then_GET_TOP_TEN_MEMES_SUCCESS() throws Exception { - // when - final ResultActions perform = mockMvc.perform( - get("/memes/like/all") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken) - ).andDo(print()); - - // then - perform.andExpect(status().isOk()) - .andExpect(jsonPath(MESSAGE).value(GET_TOP_TEN_MEMES_SUCCESS.getMessage())); - } - - @Test - @DisplayName("주간 좋아요 Top 10 밈스 반환 성공") - public void When_findTopMemesByLikeForWeek_Then_GET_WEEK_TOP_TEN_MEMES_SUCCESS() throws Exception { - // when - final ResultActions perform = mockMvc.perform( - get("/memes/like/week") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken) - ).andDo(print()); - - // then - perform.andExpect(status().isOk()) - .andExpect(jsonPath(MESSAGE).value(GET_WEEK_TOP_TEN_MEMES_SUCCESS.getMessage())); - } - - @Test - @DisplayName("밈스 id를 통한 MemesResponse 반환 성공") - public void Given_memesId_When_findMemesResponse_Thee_MemeResponse() throws Exception { - Meme meme = MEME(loginMember); - MemesResponse response = MemesResponse.of(MEMES(loginMember, meme)); - given(memesService.findMemesResponse(any())).willReturn(response); - - // when - final ResultActions perform = mockMvc.perform( - get("/memes/-1") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken) - ).andDo(print()); - - // then - perform.andExpect(status().isOk()) - .andExpectAll( - jsonPath(MESSAGE).value(GET_ONE_MEMES_SUCCESS.getMessage()), - jsonPath("$.data.likeCount").value("1")); - } - - @Test - @DisplayName("Pageable을 통한 MemesInfoSliceResponse 반환 성공") - public void Given_Pageable_When_findMemesInfoSliceResponse_Then_MemesInfoPageResponse() throws Exception { - final String page = "page=0&size=10"; - Meme meme = MEME(loginMember); - - MemesInfoSliceResponse response = MemesInfoSliceResponse.builder() - .memesInfoResponseList(List.of(MemesInfoResponse.of(MEMES(loginMember, meme)))) - .currentPage(0) - .hasNext(false) - .build(); - given(memesService.findMemesInfoSliceResponse(any())).willReturn(response); - - String expectedTitle = response.getMemesInfoResponseList().get(0).getTitle(); - - // when - final ResultActions perform = mockMvc.perform( - get("/memes?" + page) - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken) - ).andDo(print()); - - // then - perform.andExpect(status().isOk()).andExpectAll( - jsonPath(MESSAGE).value(GET_ALL_MEMES_SUCCESS.getMessage()), - jsonPath("$.data.memesInfoResponseList[0].title").value(expectedTitle)); - } - - @Test - @DisplayName("memesId를 통한 좋아요 등록 성공") - void Given_memesId_When_registerLike_Then_CREATE_LIKE_SUCCESS() throws Exception { - // when - final ResultActions perform = mockMvc.perform(post("/memes/-1/like") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken)) - .andDo(print()); - - // then - verify(likeService).registerLike(any(LikeServiceDto.class)); - perform.andExpect(status().isCreated()) - .andExpect(jsonPath(MESSAGE).value(CREATE_LIKE_SUCCESS.getMessage())); - } - - @Test - @DisplayName("DB에 존재하는 좋아요로 인한 LIKE_NOT_CREATE 반환") - void Given_ExistLike_When_registerLike_Then_LIKE_NOT_CREATE() throws Exception { - // given - doThrow(new NotCreateLikeException()).when(likeService).registerLike(any(LikeServiceDto.class)); - - // when - final ResultActions perform = mockMvc.perform(post("/memes/-1/like") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken)) - .andDo(print()); - - // then - verify(likeService).registerLike(any(LikeServiceDto.class)); - perform.andExpect(status().isConflict()) - .andExpect(jsonPath(ERROR_MESSAGE).value(LIKE_NOT_CREATE.getMessage())); - } - - @Test - @DisplayName("memesId를 통한 좋아요 삭제 성공") - void Given_memesId_When_cancelLike_Then_DELETE_LIKE_SUCCESS() throws Exception { - // when - final ResultActions perform = mockMvc.perform(delete("/memes/-1/like") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken)) - .andDo(print()); - - // then - verify(likeService).cancelLike(any(LikeServiceDto.class)); - perform.andExpect(status().isOk()) - .andExpect(jsonPath(MESSAGE).value(DELETE_LIKE_SUCCESS.getMessage())); - } - - @Test - @DisplayName("DB에 없는 좋아요로 인한 LIKE_NOT_FOUND 반환") - void Given_NotExistLike_When_cancelLike_Then_LIKE_NOT_FOUND() throws Exception { - // given - doThrow(new NotFoundLikeException()).when(likeService).cancelLike(any(LikeServiceDto.class)); - - // when - final ResultActions perform = mockMvc.perform(delete("/memes/-1/like") - .contentType(MediaType.APPLICATION_JSON) - .header("Authorization", "Bearer " + accessToken)) - .andDo(print()); - - // then - perform.andExpect(status().isNotFound()) - .andExpect(jsonPath(ERROR_MESSAGE).value(LIKE_NOT_FOUND.getMessage())); - } -}