From 62c5e8e972908a190f2160adf1e6a92a4cfb77bb Mon Sep 17 00:00:00 2001 From: Shinjongyun Date: Wed, 1 Oct 2025 23:49:08 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[Fix]=20#10=20=EB=8F=99=EC=95=84=EB=A6=AC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + build.gradle | 14 + .../domain/user/service/UserService.java | 50 ++-- .../domain/user/service/UserServiceTest.java | 248 +++++++++++------- 4 files changed, 191 insertions(+), 123 deletions(-) diff --git a/.gitignore b/.gitignore index ce9b259..d9960a3 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ out/ ### .env file ### *.env +### test file ### +/src/test/resources/* diff --git a/build.gradle b/build.gradle index 3ac0063..36f996a 100644 --- a/build.gradle +++ b/build.gradle @@ -65,4 +65,18 @@ dependencies { tasks.named('test') { useJUnitPlatform() + // 테스트 JVM도 확실히 UTF-8 + jvmArgs '-Dfile.encoding=UTF-8' + testLogging { + events "FAILED", "SKIPPED", "STANDARD_ERROR" + exceptionFormat "FULL" + showCauses true; showExceptions true; showStackTraces true + } +} +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' +} + +tasks.withType(Javadoc).configureEach { + options.encoding = 'UTF-8' } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserService.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserService.java index b238c68..1efe088 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserService.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserService.java @@ -21,10 +21,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; @Slf4j @@ -117,39 +114,40 @@ private void updateUserNickName(User user, String newNickName) { private void updateUserClubs(User user, List newClubIdList) { - // null이면 빈 리스트로 간주 => 모두 탈퇴 처리 - Set requested = newClubIdList == null ? Set.of() + Long userId = user.getId(); + + // 1) 요청 정규화 (null => 빈 집합, 중복 제거) + Set targetClubIds = newClubIdList == null ? Set.of() : newClubIdList.stream() .filter(Objects::nonNull) - .collect(Collectors.toCollection(LinkedHashSet::new)); // 순서 유지 필요시 + .collect(Collectors.toCollection(LinkedHashSet::new)); - Long userId = user.getId(); + // === 전체 요청 clubId 기준 존재 여부 검증 === + validateClubExistence(targetClubIds); - // 현재 가입된 clubId 목록 - Set current = new LinkedHashSet<>(memberRepository.findClubIdsByUserId(userId)); + // 2) 현재 가입된 clubId 목록 조회 + Set currentClubIds = new HashSet<>(memberRepository.findClubIdsByUserId(userId)); - // 계산: 추가/삭제 집합 - Set toAdd = new LinkedHashSet<>(requested); - toAdd.removeAll(current); + // 3) 추가할 것과 제거할 것 계산 + Set toAdd = new HashSet<>(targetClubIds); + toAdd.removeAll(currentClubIds); // 새로 추가할 것만 남김 - Set toRemove = new LinkedHashSet<>(current); - toRemove.removeAll(requested); + Set toRemove = new HashSet<>(currentClubIds); + toRemove.removeAll(targetClubIds); // 요청에 없는 건 제거 - // 삭제 먼저 (없으면 no-op) + // 4) 제거 실행 if (!toRemove.isEmpty()) { memberRepository.deleteByUserIdAndClubIdIn(userId, toRemove); } - // 추가할 Club의 존재성 검증 + // 5) 추가할 Club 존재 여부 검증 if (!toAdd.isEmpty()) { List clubs = clubRepository.findAllById(toAdd); - if (clubs.size() != toAdd.size()) { - // 어떤 ID는 존재X throw new BusinessException(ErrorCode.CLUB_NOT_FOUND); } - // Member 엔티티 생성 + // 6) 추가 실행 List newMembers = clubs.stream() .map(club -> Member.builder() .user(user) @@ -157,8 +155,18 @@ private void updateUserClubs(User user, List newClubIdList) { .build()) .toList(); - // 저장 (유니크 제약 (user_id, club_id) 있어도 toAdd는 중복이 아님) memberRepository.saveAll(newMembers); } } + + private void validateClubExistence(Set clubIds) { + if (clubIds == null || clubIds.isEmpty()) { + return; // 요청이 없으면 패스 + } + + List clubs = clubRepository.findAllById(clubIds); + if (clubs.size() != clubIds.size()) { + throw new BusinessException(ErrorCode.CLUB_NOT_FOUND); + } + } } diff --git a/src/test/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserServiceTest.java b/src/test/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserServiceTest.java index dd3aed4..bfb45a8 100644 --- a/src/test/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserServiceTest.java +++ b/src/test/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserServiceTest.java @@ -6,136 +6,180 @@ import com.WhoIsRoom.WhoIs_Server.domain.member.repository.MemberRepository; import com.WhoIsRoom.WhoIs_Server.domain.user.dto.request.MyPageUpdateRequest; import com.WhoIsRoom.WhoIs_Server.domain.user.dto.response.MyPageResponse; +import com.WhoIsRoom.WhoIs_Server.domain.user.model.Role; import com.WhoIsRoom.WhoIs_Server.domain.user.model.User; import com.WhoIsRoom.WhoIs_Server.domain.user.repository.UserRepository; +import com.WhoIsRoom.WhoIs_Server.domain.user.dto.response.ClubResponse; +import com.WhoIsRoom.WhoIs_Server.global.common.exception.BusinessException; +import com.WhoIsRoom.WhoIs_Server.global.common.response.ErrorCode; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentMatchers; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; +import java.util.*; + +import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyCollection; -import static org.mockito.Mockito.when; @Slf4j -@ExtendWith(MockitoExtension.class) +@SpringBootTest +@ActiveProfiles("test") +@Transactional +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // H2로 대체 금지 class UserServiceTest { - @Mock private UserRepository userRepository; - @Mock private ClubRepository clubRepository; - @Mock private MemberRepository memberRepository; + @Autowired private UserService userService; + @Autowired private UserRepository userRepository; + @Autowired private ClubRepository clubRepository; + @Autowired + private MemberRepository memberRepository; - @InjectMocks - private UserService userService; + @PersistenceContext + EntityManager em; private User user; - private List clubs; + private Club c1, c2, c3, c4; @BeforeEach void setUp() { - System.out.println("\n[TEST] ========== setUp =========="); - user = User.builder() - .nickName("조익성") - .email("konkuk@gmail.com") - .password("1234") + // 깨끗한 상태로 시작(FK 제약 있으면 순서 중요) + memberRepository.deleteAllInBatch(); + clubRepository.deleteAllInBatch(); + userRepository.deleteAllInBatch(); + + // ⚠️ User, Club 엔티티의 @NotNull 필드가 있다면 실제 필드 모두 채워줘야 함 + user = userRepository.save(User.builder() + .nickName("oldNick") + .role(Role.MEMBER) + .email("oldEmail") + .password("oldPassword") + .build()); + + c1 = clubRepository.save(Club.builder().name("C1").clubNumber("1").build()); + c2 = clubRepository.save(Club.builder().name("C2").clubNumber("2").build()); + c3 = clubRepository.save(Club.builder().name("C3").clubNumber("3").build()); + c4 = clubRepository.save(Club.builder().name("C4").clubNumber("4").build()); + + em.flush(); + em.clear(); + } + + @Test + @DisplayName("성공: 추가만 (현재 {c1,c2} → 요청 {c1,c2,c3})") + void addOnly() { + memberRepository.save(Member.builder().user(user).club(c1).build()); + memberRepository.save(Member.builder().user(user).club(c2).build()); + em.flush(); em.clear(); + + MyPageUpdateRequest req = MyPageUpdateRequest.builder() + .nickName("oldNick") + .clubList(List.of(c1.getId(), c2.getId(), c3.getId())) .build(); - user.setId(1L); - Club club1 = Club.builder().name("메이커스팜").build(); club1.setId(1L); - Club club2 = Club.builder().name("목방").build(); club2.setId(2L); - Club club3 = Club.builder().name("건대교지편집위원회").build(); club3.setId(3L); - Club club4 = Club.builder().name("국어국문학과").build(); club4.setId(4L); + MyPageResponse resp = userService.updateMyPage(user.getId(), req); - clubs = List.of(club1, club2, club3, club4); + assertThat(resp.getClubList()) + .extracting(ClubResponse::getId) + .containsExactlyInAnyOrder(c1.getId(), c2.getId(), c3.getId()); - System.out.println("[TEST] userId=" + user.getId() + ", nick=" + user.getNickName()); - System.out.println("[TEST] clubs=" + clubs.stream() - .map(c -> c.getId() + ":" + c.getName()).toList()); - System.out.println("[TEST] =============================\n"); + // DB 상태도 확인 + var after = memberRepository.findByUserId(user.getId()); + assertThat(after).extracting(m -> m.getClub().getId()) + .containsExactlyInAnyOrder(c1.getId(), c2.getId(), c3.getId()); } @Test - @DisplayName("닉네임과 클럽 목록을 업데이트하고 응답 DTO를 반환한다") - void updateMyPage_success() { - Long userId = user.getId(); + @DisplayName("성공: 삭제만 (현재 {c1,c2,c3} → 요청 {c2,c3})") + void removeOnly() { + memberRepository.save(Member.builder().user(user).club(c1).build()); + memberRepository.save(Member.builder().user(user).club(c2).build()); + memberRepository.save(Member.builder().user(user).club(c3).build()); + em.flush(); em.clear(); + + MyPageUpdateRequest req = MyPageUpdateRequest.builder() + .nickName("oldNick") + .clubList(List.of(c2.getId(), c3.getId())) + .build(); + + MyPageResponse resp = userService.updateMyPage(user.getId(), req); + + assertThat(resp.getClubList()) + .extracting(ClubResponse::getId) + .containsExactlyInAnyOrder(c2.getId(), c3.getId()); + + var after = memberRepository.findByUserId(user.getId()); + assertThat(after).extracting(m -> m.getClub().getId()) + .containsExactlyInAnyOrder(c2.getId(), c3.getId()); + } + + @Test + @DisplayName("성공: 추가+삭제 (현재 {c1,c2,c3} → 요청 {c2,c4})") + void addAndRemove() { + memberRepository.save(Member.builder().user(user).club(c1).build()); + memberRepository.save(Member.builder().user(user).club(c2).build()); + memberRepository.save(Member.builder().user(user).club(c3).build()); + em.flush(); em.clear(); + + MyPageUpdateRequest req = MyPageUpdateRequest.builder() + .nickName("oldNick") + .clubList(List.of(c2.getId(), c4.getId())) + .build(); + + MyPageResponse resp = userService.updateMyPage(user.getId(), req); + + assertThat(resp.getClubList()) + .extracting(ClubResponse::getId) + .containsExactlyInAnyOrder(c2.getId(), c4.getId()); + + var after = memberRepository.findByUserId(user.getId()); + assertThat(after).extracting(m -> m.getClub().getId()) + .containsExactlyInAnyOrder(c2.getId(), c4.getId()); + } + + @Test + @DisplayName("성공: 전체 탈퇴 (요청 null)") + void leaveAll() { + memberRepository.save(Member.builder().user(user).club(c1).build()); + memberRepository.save(Member.builder().user(user).club(c2).build()); + em.flush(); em.clear(); + + MyPageUpdateRequest req = MyPageUpdateRequest.builder() + .nickName("oldNick") + .clubList(null) // 전체 탈퇴로 간주 + .build(); + + MyPageResponse resp = userService.updateMyPage(user.getId(), req); + + assertThat(resp.getClubList()).isEmpty(); + assertThat(memberRepository.findByUserId(user.getId())).isEmpty(); + } + + @Test + @DisplayName("실패: 존재하지 않는 Club ID 포함 → CLUB_NOT_FOUND") + void clubNotFound() { + Long notExistId = 9999L; + + memberRepository.save(Member.builder().user(user).club(c1).build()); + em.flush(); em.clear(); - MyPageUpdateRequest request = MyPageUpdateRequest.builder() - .nickName("조익성") - .clubList(List.of(1L, 2L, 3L, 4L)) + MyPageUpdateRequest req = MyPageUpdateRequest.builder() + .nickName("oldNick") + .clubList(List.of(c1.getId(), notExistId)) .build(); - // --- 스텁 + 로그 --- - when(userRepository.findById(userId)) - .thenAnswer(inv -> { - System.out.println("[TEST] userRepository.findById(" + userId + ")"); - return Optional.of(user); - }); - - when(memberRepository.findClubIdsByUserId(userId)) - .thenAnswer(inv -> { - System.out.println("[TEST] memberRepository.findClubIdsByUserId(" + userId + ") -> [2]"); - return List.of(2L); - }); - - when(clubRepository.findAllById(ArgumentMatchers.anyIterable())) - .thenAnswer(invocation -> { - Iterable ids = invocation.getArgument(0); - List idList = new ArrayList<>(); - ids.forEach(idList::add); - System.out.println("[TEST] clubRepository.findAllById called with ids=" + idList); - var result = clubs.stream() - .filter(c -> idList.contains(c.getId())) - .collect(Collectors.toList()); - System.out.println("[TEST] clubRepository.findAllById returns ids=" + - result.stream().map(Club::getId).toList()); - return result; - }); - - when(memberRepository.saveAll(anyCollection())) - .thenAnswer(invocation -> { - @SuppressWarnings("unchecked") - var c = (java.util.Collection) invocation.getArgument(0); - System.out.println("[TEST] memberRepository.saveAll called size=" + c.size() + - ", clubIds=" + c.stream().map(m -> m.getClub().getId()).toList()); - return new ArrayList<>(c); - }); - - when(memberRepository.findByUserId(userId)) - .thenAnswer(inv -> { - System.out.println("[TEST] memberRepository.findByUserId(" + userId + ")"); - var list = clubs.stream() - .map(c -> Member.builder().user(user).club(c).build()) - .collect(Collectors.toList()); - System.out.println("[TEST] memberRepository.findByUserId returns clubIds=" + - list.stream().map(m -> m.getClub().getId()).toList()); - return list; - }); - - // --- 실행 --- - System.out.println("\n[TEST] ===== call userService.updateMyPage ====="); - MyPageResponse response = userService.updateMyPage(userId, request); - System.out.println("[TEST] ===== returned MyPageResponse ====="); - System.out.println("[TEST] resp.nick=" + response.getNickName()); - System.out.println("[TEST] resp.clubs=" + response.getClubList().stream() - .map(c -> c.getId() + ":" + c.getName()).toList()); - System.out.println("[TEST] ===================================\n"); - - // --- 검증 --- - assertThat(response.getNickName()).isEqualTo("조익성"); - assertThat(response.getClubList()).hasSize(4); - assertThat(response.getClubList()) - .extracting("name") - .containsExactlyInAnyOrder("메이커스팜", "목방", "건대교지편집위원회", "국어국문학과"); + assertThatThrownBy(() -> userService.updateMyPage(user.getId(), req)) + .isInstanceOf(BusinessException.class) + .extracting("errorCode") + .isEqualTo(ErrorCode.CLUB_NOT_FOUND); } } From 50f611ded893d46059f6b2a4d788e174e74822a9 Mon Sep 17 00:00:00 2001 From: Shinjongyun Date: Wed, 1 Oct 2025 23:56:58 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[Chore]=20#10=20Test=EC=BD=94=EB=93=9C=20gi?= =?UTF-8?q?tignore=EC=97=90=20=E3=85=8A=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- .../domain/user/service/UserServiceTest.java | 185 ------------------ 2 files changed, 1 insertion(+), 186 deletions(-) delete mode 100644 src/test/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserServiceTest.java diff --git a/.gitignore b/.gitignore index d9960a3..2ae3d34 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,4 @@ out/ *.env ### test file ### -/src/test/resources/* +/src/test/* diff --git a/src/test/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserServiceTest.java b/src/test/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserServiceTest.java deleted file mode 100644 index bfb45a8..0000000 --- a/src/test/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserServiceTest.java +++ /dev/null @@ -1,185 +0,0 @@ -package com.WhoIsRoom.WhoIs_Server.domain.user.service; - -import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; -import com.WhoIsRoom.WhoIs_Server.domain.club.repository.ClubRepository; -import com.WhoIsRoom.WhoIs_Server.domain.member.model.Member; -import com.WhoIsRoom.WhoIs_Server.domain.member.repository.MemberRepository; -import com.WhoIsRoom.WhoIs_Server.domain.user.dto.request.MyPageUpdateRequest; -import com.WhoIsRoom.WhoIs_Server.domain.user.dto.response.MyPageResponse; -import com.WhoIsRoom.WhoIs_Server.domain.user.model.Role; -import com.WhoIsRoom.WhoIs_Server.domain.user.model.User; -import com.WhoIsRoom.WhoIs_Server.domain.user.repository.UserRepository; -import com.WhoIsRoom.WhoIs_Server.domain.user.dto.response.ClubResponse; - -import com.WhoIsRoom.WhoIs_Server.global.common.exception.BusinessException; -import com.WhoIsRoom.WhoIs_Server.global.common.response.ErrorCode; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -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.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; - -import static org.assertj.core.api.Assertions.*; - - -@Slf4j -@SpringBootTest -@ActiveProfiles("test") -@Transactional -@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // H2로 대체 금지 -class UserServiceTest { - - @Autowired private UserService userService; - @Autowired private UserRepository userRepository; - @Autowired private ClubRepository clubRepository; - @Autowired - private MemberRepository memberRepository; - - @PersistenceContext - EntityManager em; - - private User user; - private Club c1, c2, c3, c4; - - @BeforeEach - void setUp() { - // 깨끗한 상태로 시작(FK 제약 있으면 순서 중요) - memberRepository.deleteAllInBatch(); - clubRepository.deleteAllInBatch(); - userRepository.deleteAllInBatch(); - - // ⚠️ User, Club 엔티티의 @NotNull 필드가 있다면 실제 필드 모두 채워줘야 함 - user = userRepository.save(User.builder() - .nickName("oldNick") - .role(Role.MEMBER) - .email("oldEmail") - .password("oldPassword") - .build()); - - c1 = clubRepository.save(Club.builder().name("C1").clubNumber("1").build()); - c2 = clubRepository.save(Club.builder().name("C2").clubNumber("2").build()); - c3 = clubRepository.save(Club.builder().name("C3").clubNumber("3").build()); - c4 = clubRepository.save(Club.builder().name("C4").clubNumber("4").build()); - - em.flush(); - em.clear(); - } - - @Test - @DisplayName("성공: 추가만 (현재 {c1,c2} → 요청 {c1,c2,c3})") - void addOnly() { - memberRepository.save(Member.builder().user(user).club(c1).build()); - memberRepository.save(Member.builder().user(user).club(c2).build()); - em.flush(); em.clear(); - - MyPageUpdateRequest req = MyPageUpdateRequest.builder() - .nickName("oldNick") - .clubList(List.of(c1.getId(), c2.getId(), c3.getId())) - .build(); - - MyPageResponse resp = userService.updateMyPage(user.getId(), req); - - assertThat(resp.getClubList()) - .extracting(ClubResponse::getId) - .containsExactlyInAnyOrder(c1.getId(), c2.getId(), c3.getId()); - - // DB 상태도 확인 - var after = memberRepository.findByUserId(user.getId()); - assertThat(after).extracting(m -> m.getClub().getId()) - .containsExactlyInAnyOrder(c1.getId(), c2.getId(), c3.getId()); - } - - @Test - @DisplayName("성공: 삭제만 (현재 {c1,c2,c3} → 요청 {c2,c3})") - void removeOnly() { - memberRepository.save(Member.builder().user(user).club(c1).build()); - memberRepository.save(Member.builder().user(user).club(c2).build()); - memberRepository.save(Member.builder().user(user).club(c3).build()); - em.flush(); em.clear(); - - MyPageUpdateRequest req = MyPageUpdateRequest.builder() - .nickName("oldNick") - .clubList(List.of(c2.getId(), c3.getId())) - .build(); - - MyPageResponse resp = userService.updateMyPage(user.getId(), req); - - assertThat(resp.getClubList()) - .extracting(ClubResponse::getId) - .containsExactlyInAnyOrder(c2.getId(), c3.getId()); - - var after = memberRepository.findByUserId(user.getId()); - assertThat(after).extracting(m -> m.getClub().getId()) - .containsExactlyInAnyOrder(c2.getId(), c3.getId()); - } - - @Test - @DisplayName("성공: 추가+삭제 (현재 {c1,c2,c3} → 요청 {c2,c4})") - void addAndRemove() { - memberRepository.save(Member.builder().user(user).club(c1).build()); - memberRepository.save(Member.builder().user(user).club(c2).build()); - memberRepository.save(Member.builder().user(user).club(c3).build()); - em.flush(); em.clear(); - - MyPageUpdateRequest req = MyPageUpdateRequest.builder() - .nickName("oldNick") - .clubList(List.of(c2.getId(), c4.getId())) - .build(); - - MyPageResponse resp = userService.updateMyPage(user.getId(), req); - - assertThat(resp.getClubList()) - .extracting(ClubResponse::getId) - .containsExactlyInAnyOrder(c2.getId(), c4.getId()); - - var after = memberRepository.findByUserId(user.getId()); - assertThat(after).extracting(m -> m.getClub().getId()) - .containsExactlyInAnyOrder(c2.getId(), c4.getId()); - } - - @Test - @DisplayName("성공: 전체 탈퇴 (요청 null)") - void leaveAll() { - memberRepository.save(Member.builder().user(user).club(c1).build()); - memberRepository.save(Member.builder().user(user).club(c2).build()); - em.flush(); em.clear(); - - MyPageUpdateRequest req = MyPageUpdateRequest.builder() - .nickName("oldNick") - .clubList(null) // 전체 탈퇴로 간주 - .build(); - - MyPageResponse resp = userService.updateMyPage(user.getId(), req); - - assertThat(resp.getClubList()).isEmpty(); - assertThat(memberRepository.findByUserId(user.getId())).isEmpty(); - } - - @Test - @DisplayName("실패: 존재하지 않는 Club ID 포함 → CLUB_NOT_FOUND") - void clubNotFound() { - Long notExistId = 9999L; - - memberRepository.save(Member.builder().user(user).club(c1).build()); - em.flush(); em.clear(); - - MyPageUpdateRequest req = MyPageUpdateRequest.builder() - .nickName("oldNick") - .clubList(List.of(c1.getId(), notExistId)) - .build(); - - assertThatThrownBy(() -> userService.updateMyPage(user.getId(), req)) - .isInstanceOf(BusinessException.class) - .extracting("errorCode") - .isEqualTo(ErrorCode.CLUB_NOT_FOUND); - } -}