From 960c33d235801b900e4121a501c5cad41fcc5a78 Mon Sep 17 00:00:00 2001 From: GiJung <101462387+GiJungPark@users.noreply.github.com> Date: Sun, 30 Aug 2026 15:18:00 +0900 Subject: [PATCH] =?UTF-8?q?[FIX]=20=EB=B9=84=EA=B3=B5=EA=B0=9C=20=ED=83=80?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=A0=95=EC=83=81=20=EC=A1=B0=ED=9A=8C=EB=A5=BC=20=EB=B3=B5?= =?UTF-8?q?=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/user/service/UserService.java | 2 +- .../service/UserServiceBlockPolicyTest.java | 18 +++ .../UserServiceProfileVisibilityTest.java | 137 ++++++++++++++++++ 3 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/websoso/WSSServer/user/service/UserServiceProfileVisibilityTest.java diff --git a/src/main/java/org/websoso/WSSServer/user/service/UserService.java b/src/main/java/org/websoso/WSSServer/user/service/UserService.java index 1438da094..52032501c 100644 --- a/src/main/java/org/websoso/WSSServer/user/service/UserService.java +++ b/src/main/java/org/websoso/WSSServer/user/service/UserService.java @@ -170,7 +170,7 @@ public ProfileGetResponse getProfileInfo(User visitor, Long ownerId) { } User owner = getUserOrException(ownerId); Long visitorId = visitor == null ? null : visitor.getUserId(); - validateProfileAccessible(owner, visitorId); + blockService.validateNotBlocked(visitorId, owner.getUserId()); Long avatarId = owner.getAvatarProfileId(); AvatarProfile avatar = findAvatarProfileByIdOrThrow(avatarId); diff --git a/src/test/java/org/websoso/WSSServer/user/service/UserServiceBlockPolicyTest.java b/src/test/java/org/websoso/WSSServer/user/service/UserServiceBlockPolicyTest.java index bac9991f8..c4058b38b 100644 --- a/src/test/java/org/websoso/WSSServer/user/service/UserServiceBlockPolicyTest.java +++ b/src/test/java/org/websoso/WSSServer/user/service/UserServiceBlockPolicyTest.java @@ -4,6 +4,7 @@ import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; import static org.mockito.BDDMockito.willThrow; +import static org.mockito.Mockito.never; import static org.websoso.WSSServer.user.exception.CustomBlockError.BLOCKED_USER_ACCESS; import java.util.Optional; @@ -66,4 +67,21 @@ void rejectsBlockedUserProfile() { then(avatarProfileRepository).shouldHaveNoInteractions(); then(genrePreferenceRepository).shouldHaveNoInteractions(); } + + @DisplayName("비공개 프로필도 차단 관계면 프로필 공개 여부와 무관하게 차단 오류가 발생한다") + @Test + void rejectsBlockedUserPrivateProfile() { + given(visitor.getUserId()).willReturn(1L); + given(owner.getUserId()).willReturn(2L); + given(userRepository.findById(2L)).willReturn(Optional.of(owner)); + willThrow(new CustomBlockException(BLOCKED_USER_ACCESS, "blocked")) + .given(blockService).validateNotBlocked(1L, 2L); + + assertThatThrownBy(() -> userService.getProfileInfo(visitor, 2L)) + .isInstanceOf(CustomBlockException.class) + .extracting(exception -> ((CustomBlockException) exception).getICustomError()) + .isEqualTo(BLOCKED_USER_ACCESS); + + then(owner).should(never()).getIsProfilePublic(); + } } diff --git a/src/test/java/org/websoso/WSSServer/user/service/UserServiceProfileVisibilityTest.java b/src/test/java/org/websoso/WSSServer/user/service/UserServiceProfileVisibilityTest.java new file mode 100644 index 000000000..9a9722f6b --- /dev/null +++ b/src/test/java/org/websoso/WSSServer/user/service/UserServiceProfileVisibilityTest.java @@ -0,0 +1,137 @@ +package org.websoso.WSSServer.user.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; + +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.websoso.WSSServer.domain.Genre; +import org.websoso.WSSServer.domain.GenrePreference; +import org.websoso.WSSServer.dto.user.ProfileGetResponse; +import org.websoso.WSSServer.infrastructure.discord.DiscordMessageClient; +import org.websoso.WSSServer.repository.GenrePreferenceRepository; +import org.websoso.WSSServer.repository.GenreRepository; +import org.websoso.WSSServer.user.domain.AvatarProfile; +import org.websoso.WSSServer.user.domain.User; +import org.websoso.WSSServer.user.repository.AvatarProfileRepository; +import org.websoso.WSSServer.user.repository.UserRepository; + +@ExtendWith(MockitoExtension.class) +class UserServiceProfileVisibilityTest { + + private static final Long VISITOR_ID = 1L; + private static final Long OWNER_ID = 2L; + private static final Long AVATAR_ID = 10L; + + @InjectMocks + private UserService userService; + + @Mock + private UserRepository userRepository; + + @Mock + private AvatarProfileRepository avatarProfileRepository; + + @Mock + private GenrePreferenceRepository genrePreferenceRepository; + + @Mock + private GenreRepository genreRepository; + + @Mock + private BlockService blockService; + + @Mock + private DiscordMessageClient discordMessageClient; + + @Mock + private User visitor; + + @Mock + private User owner; + + @Mock + private AvatarProfile avatar; + + @DisplayName("비공개 프로필도 차단 관계가 아니면 기존 프로필 정보와 함께 조회된다") + @Test + void returnsPrivateProfileOfOtherUser() { + givenOwnerProfile(false); + given(visitor.getUserId()).willReturn(VISITOR_ID); + + ProfileGetResponse response = userService.getProfileInfo(visitor, OWNER_ID); + + assertThat(response.isProfilePublic()).isFalse(); + assertThat(response.nickname()).isEqualTo("소소한 독자"); + assertThat(response.intro()).isEqualTo("한 줄 소개"); + assertThat(response.avatarImage()).isEqualTo("avatar.png"); + assertThat(response.genrePreferences()).containsExactly("로맨스"); + then(blockService).should().validateNotBlocked(VISITOR_ID, OWNER_ID); + } + + @DisplayName("비로그인 사용자도 비공개 프로필을 조회할 수 있다") + @Test + void returnsPrivateProfileForAnonymousVisitor() { + givenOwnerProfile(false); + + ProfileGetResponse response = userService.getProfileInfo(null, OWNER_ID); + + assertThat(response.isProfilePublic()).isFalse(); + assertThat(response.nickname()).isEqualTo("소소한 독자"); + then(blockService).should().validateNotBlocked(null, OWNER_ID); + } + + @DisplayName("공개 프로필은 공개 여부를 true로 응답한다") + @Test + void returnsPublicProfileOfOtherUser() { + givenOwnerProfile(true); + given(visitor.getUserId()).willReturn(VISITOR_ID); + + ProfileGetResponse response = userService.getProfileInfo(visitor, OWNER_ID); + + assertThat(response.isProfilePublic()).isTrue(); + } + + @DisplayName("본인 프로필은 비공개여도 공개 여부를 true로 응답한다") + @Test + void returnsOwnPrivateProfileAsPublic() { + givenOwnerProfileFields(); + + ProfileGetResponse response = userService.getProfileInfo(owner, OWNER_ID); + + assertThat(response.isProfilePublic()).isTrue(); + } + + private void givenOwnerProfile(boolean isProfilePublic) { + givenOwnerProfileFields(); + given(owner.getIsProfilePublic()).willReturn(isProfilePublic); + } + + private void givenOwnerProfileFields() { + given(userRepository.findById(OWNER_ID)).willReturn(Optional.of(owner)); + given(owner.getUserId()).willReturn(OWNER_ID); + given(owner.getAvatarProfileId()).willReturn(AVATAR_ID); + given(owner.getNickname()).willReturn("소소한 독자"); + given(owner.getIntro()).willReturn("한 줄 소개"); + given(avatarProfileRepository.findById(AVATAR_ID)).willReturn(Optional.of(avatar)); + given(avatar.getAvatarProfileImage()).willReturn("avatar.png"); + List genrePreferences = List.of(genrePreference("로맨스")); + given(genrePreferenceRepository.findByUser(owner)).willReturn(genrePreferences); + } + + private GenrePreference genrePreference(String genreName) { + Genre genre = mock(Genre.class); + given(genre.getGenreName()).willReturn(genreName); + GenrePreference genrePreference = mock(GenrePreference.class); + given(genrePreference.getGenre()).willReturn(genre); + return genrePreference; + } +}