Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat/20] 회원 차단 해제 API 구현 및 테스트 #21

Merged
merged 3 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
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;
Expand Down Expand Up @@ -44,4 +45,13 @@ public ApiResponse<BlockListResponse> getBlockList(@ValidPage @RequestParam(name
return ApiResponse.ok(blockFacadeService.getBlockList(member, page));
}

@Operation(summary = "회원 차단 해제 API", description = "해당 회원에 대한 차단을 해제하는 API 입니다.")
@Parameter(name = "memberId", description = "차단을 해제할 대상 회원의 id 입니다.")
@DeleteMapping("/{memberId}")
public ApiResponse<String> unblockMember(@PathVariable(name = "memberId") Long targetMemberId,
@AuthMember Member member) {
blockFacadeService.unBlockMember(member, targetMemberId);
return ApiResponse.ok("회원 차단 해제 성공");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,10 @@ public void setBlockerMember(Member member) {
member.getBlockList().add(this);
}

// Block 엔티티 매핑 해제를 위한 메소드
public void removeBlockerMember(Member blockerMember) {
blockerMember.getBlockList().remove(this);
this.blockerMember = null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

public interface BlockRepository extends JpaRepository<Block, Long> {

boolean existsByBlockerMemberAndBlockedMember(Member blockerMember, Member blockedMember);
Expand All @@ -16,4 +18,6 @@ public interface BlockRepository extends JpaRepository<Block, Long> {
":blockerId AND b.deleted = false ORDER BY b.createdAt DESC")
Page<Member> findBlockedMembersByBlockerIdAndNotDeleted(@Param("blockerId") Long blockerId, Pageable pageable);

Optional<Block> findByBlockerMemberAndBlockedMember(Member blockerMember, Member blockedMember);

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,16 @@ public BlockListResponse getBlockList(Member member, Integer pageIdx) {
return BlockListResponse.of(members);
}

/**
* 회원 차단 해제 Facade 메소드
*
* @param member
* @param targetMemberId
*/
@Transactional
public void unBlockMember(Member member, Long targetMemberId) {
Member targetMember = memberService.findMember(targetMemberId);
blockService.unBlockMember(member, targetMember);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,25 @@ public Page<Member> findBlockedMembersByBlockerId(Long blockerId, Pageable pagea
return blockRepository.findBlockedMembersByBlockerIdAndNotDeleted(blockerId, pageable);
}

/**
* menber가 targetMember를 차단 해제 처하는 메소드
*
* @param member
* @param targetMember
*/
public void unBlockMember(Member member, Member targetMember) {
// 대상 회원의 탈퇴 여부 검증
memberValidator.validateTargetMemberIsNotBlind(targetMember);

// targetMember가 차단 실제로 차단 목록에 존재하는지 검증 및 block 엔티티 조회
Block block = blockRepository.findByBlockerMemberAndBlockedMember(member, targetMember)
.orElseThrow(() -> new BlockException(ErrorCode.TARGET_MEMBER_NOT_BLOCKED));

// 양방향 연관관계 제거 및 block 엔티티 삭제
block.removeBlockerMember(member);
blockRepository.delete(block);
}

private void validateNotSelfBlock(Member member, Member targetMember) {
if (member.equals(targetMember)) {
throw new BlockException(ErrorCode.BLOCK_MEMBER_BAD_REQUEST);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willDoNothing;
import static org.mockito.BDDMockito.willThrow;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
Expand Down Expand Up @@ -138,5 +139,51 @@ void getBlockListFailedWhenPageIsNotValid() throws Exception {

}

@Nested
@DisplayName("회원 차단 해제")
class UnblockMemberTest {

@DisplayName("회원 차단 해제 성공: 차단 해제 성공 메시지가 반환된다.")
@Test
void unblockMemberSucceeds() throws Exception {
// given
willDoNothing().given(blockFacadeService).unBlockMember(any(), eq(TARGET_MEMBER_ID));

// when // then
mockMvc.perform(delete(API_URL_PREFIX + "/{memberId}", TARGET_MEMBER_ID))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message").value("OK"))
.andExpect(jsonPath("$.data").value("회원 차단 해제 성공"));
}

@DisplayName("회원 차단 해제 실패: 대상 회원을 차단한 상태가 아닌 경우 에러 응답을 반환한다.")
@Test
void unblockMemberFailedWhenTargetMemberIsNotBlocked() throws Exception {
// given
willThrow(new BlockException(ErrorCode.TARGET_MEMBER_NOT_BLOCKED))
.given(blockFacadeService).unBlockMember(any(), eq(TARGET_MEMBER_ID));

// when // then
mockMvc.perform(delete(API_URL_PREFIX + "/{memberId}", TARGET_MEMBER_ID))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.message").value("차단 목록에 존재하지 않는 회원입니다."));

}

@DisplayName("회원 차단 해제 실패: 대상 회원이 탈퇴한 경우 에러 응답을 반환한다.")
@Test
void unblockMemberFailedWhenTargetIsBlind() throws Exception {
// given
willThrow(new BlockException(ErrorCode.TARGET_MEMBER_DEACTIVATED))
.given(blockFacadeService).unBlockMember(any(), eq(TARGET_MEMBER_ID));

// when // then
mockMvc.perform(delete(API_URL_PREFIX + "/{memberId}", TARGET_MEMBER_ID))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$.message").value("대상 회원이 탈퇴했습니다."));
}

}

}

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

@ActiveProfiles("test")
@SpringBootTest
Expand Down Expand Up @@ -78,7 +80,7 @@ void blockMemberSucceeds(boolean chatroomExists, boolean friendshipExists, boole

// then
// 차단이 정상적으로 처리되었는지 검증
assertThat(blockRepository.existsByBlockerMemberAndBlockedMember(member, targetMember)).isTrue();
assertTrue(blockRepository.existsByBlockerMemberAndBlockedMember(member, targetMember));

// 채팅방에서 퇴장 처리 되었는지 검증

Expand Down Expand Up @@ -131,6 +133,18 @@ void blockMember_shouldThrowWhenTargetMemberIsBlind() {
.hasMessage(ErrorCode.TARGET_MEMBER_DEACTIVATED.getMessage());
}

@DisplayName("회원 차단 실패: 차단 대상 회원을 찾을 수 없는 경우 예외가 발생한다.")
@Test
void blockMember_shouldThrowWhenTargetMemberNotFound() {
// given
Member member = createMember(MEMBER_EMAIL, MEMBER_GAMENAME);

// when // then
assertThatThrownBy(() -> blockFacadeService.blockMember(member, 100L))
.isInstanceOf(MemberException.class)
.hasMessage(ErrorCode.MEMBER_NOT_FOUND.getMessage());
}

/* 차단한 회원 목록 조회 */
@DisplayName("차단한 회원 목록 조회 성공: 차단한 회원이 존재하는 경우")
@Test
Expand Down Expand Up @@ -168,6 +182,69 @@ void getBlockListSucceedsWhenBlockedMemberNotExists() {
assertThat(blockList.getBlockedMemberList()).isEmpty();
}

/* 회원 차단 해제 */
@DisplayName("회원 차단 해제 성공")
@Test
void unBlockMemberSucceeds() {
// given
Member member = createMember(MEMBER_EMAIL, MEMBER_GAMENAME);
Member targetMember = createMember("target@naver.com", "target");

// 회원 차단 처리
blockFacadeService.blockMember(member, targetMember.getId());

// when
blockFacadeService.unBlockMember(member, targetMember.getId());

// then
// 차단 기록이 정상적으로 삭제되었는지 검증
assertFalse(blockRepository.existsByBlockerMemberAndBlockedMember(member, targetMember));
}

@DisplayName("회원 차단 해제 실패: 대상 회원이 탈퇴한 경우 예외가 발생한다.")
@Test
void unBlockMember_shouldThrowWhenTargetMemberIsBlind() {
// given
Member member = createMember(MEMBER_EMAIL, MEMBER_GAMENAME);
Member targetMember = createMember("target@naver.com", "target");

// 회원 차단 처리
blockFacadeService.blockMember(member, targetMember.getId());

// 대상 회원을 탈퇴 처리
targetMember.updateBlind(true);

// when // then
assertThatThrownBy(() -> blockFacadeService.unBlockMember(member, targetMember.getId()))
.isInstanceOf(MemberException.class)
.hasMessage(ErrorCode.TARGET_MEMBER_DEACTIVATED.getMessage());
}

@DisplayName("회원 차단 해제 실패: 대상 회원을 차단하지 않은 상태인 경우 예외가 발생한다.")
@Test
void unBlockMember_shouldThrowWhenTargetMemberIsNotBlocked() {
// given
Member member = createMember(MEMBER_EMAIL, MEMBER_GAMENAME);
Member targetMember = createMember("target@naver.com", "target");

// when // then
assertThatThrownBy(() -> blockFacadeService.unBlockMember(member, targetMember.getId()))
.isInstanceOf(BlockException.class)
.hasMessage(ErrorCode.TARGET_MEMBER_NOT_BLOCKED.getMessage());
}

@DisplayName("회원 차단 해제 실패: 대상 회원을 찾을 수 없는 경우 예외가 발생한다.")
@Test
void unBlockMember_shouldThrowWhenTargetMemberNotFound() {
// given
Member member = createMember(MEMBER_EMAIL, MEMBER_GAMENAME);

// when // then
assertThatThrownBy(() -> blockFacadeService.unBlockMember(member, 100L))
.isInstanceOf(MemberException.class)
.hasMessage(ErrorCode.MEMBER_NOT_FOUND.getMessage());
}

private Member createMember(String email, String gameName) {
return memberRepository.save(Member.builder()
.email(email)
Expand Down
Loading