diff --git a/.gitignore b/.gitignore index ce9b259..2ae3d34 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ out/ ### .env file ### *.env +### test file ### +/src/test/* 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 deleted file mode 100644 index dd3aed4..0000000 --- a/src/test/java/com/WhoIsRoom/WhoIs_Server/domain/user/service/UserServiceTest.java +++ /dev/null @@ -1,141 +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.User; -import com.WhoIsRoom.WhoIs_Server.domain.user.repository.UserRepository; - -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 java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyCollection; -import static org.mockito.Mockito.when; - -@Slf4j -@ExtendWith(MockitoExtension.class) -class UserServiceTest { - - @Mock private UserRepository userRepository; - @Mock private ClubRepository clubRepository; - @Mock private MemberRepository memberRepository; - - @InjectMocks - private UserService userService; - - private User user; - private List clubs; - - @BeforeEach - void setUp() { - System.out.println("\n[TEST] ========== setUp =========="); - user = User.builder() - .nickName("조익성") - .email("konkuk@gmail.com") - .password("1234") - .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); - - clubs = List.of(club1, club2, club3, club4); - - 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"); - } - - @Test - @DisplayName("닉네임과 클럽 목록을 업데이트하고 응답 DTO를 반환한다") - void updateMyPage_success() { - Long userId = user.getId(); - - MyPageUpdateRequest request = MyPageUpdateRequest.builder() - .nickName("조익성") - .clubList(List.of(1L, 2L, 3L, 4L)) - .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("메이커스팜", "목방", "건대교지편집위원회", "국어국문학과"); - } -}