From adc7c01dcd20acb7b4ab9466b0716d1ca7d17c8f Mon Sep 17 00:00:00 2001 From: Dimo-2562 Date: Mon, 25 May 2026 16:12:27 +0900 Subject: [PATCH] =?UTF-8?q?test:=20User=20aggregate=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=20=EC=A0=84=EC=9D=B4=20=ED=9A=8C=EA=B7=80=EB=A5=BC=20=EC=A7=81?= =?UTF-8?q?=EC=A0=91=20=EA=B3=A0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 서비스 테스트에만 의존하던 User aggregate 규칙을 전용 도메인 테스트로 고정해 이후 User Account 리팩터링에서 상태 전이와 익명화 회귀를 빠르게 감지할 수 있게 했다. Constraint: #396은 구조 변경보다 User aggregate 규칙 안전망 확보가 우선이었다 Rejected: 관심사 불변식 이동까지 같은 커밋에 포함 | #397 범위와 섞여 책임이 흐려져 보류 Confidence: high Scope-risk: narrow Directive: 이후 #399/#397/#398 작업은 UserTest를 유지한 채 패키지 재배치와 aggregate 책임 이동을 진행할 것 Tested: ./gradlew test --tests 'com.techfork.domain.useraccount.entity.UserTest' --tests 'com.techfork.domain.useraccount.service.UserCommandServiceTest' --tests 'com.techfork.domain.useraccount.service.UserQueryServiceTest' -PexcludeIntegration; ./gradlew test -PexcludeIntegration Not-tested: ./gradlew integrationTest; ./gradlew evaluationTest; ./gradlew evaluationSetup --- .../domain/useraccount/entity/UserTest.java | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/test/java/com/techfork/domain/useraccount/entity/UserTest.java diff --git a/src/test/java/com/techfork/domain/useraccount/entity/UserTest.java b/src/test/java/com/techfork/domain/useraccount/entity/UserTest.java new file mode 100644 index 0000000..01e81a2 --- /dev/null +++ b/src/test/java/com/techfork/domain/useraccount/entity/UserTest.java @@ -0,0 +1,141 @@ +package com.techfork.domain.useraccount.entity; + +import com.techfork.domain.useraccount.enums.Role; +import com.techfork.domain.useraccount.enums.SocialType; +import com.techfork.domain.useraccount.enums.UserStatus; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class UserTest { + + @Nested + @DisplayName("createSocialUser") + class CreateSocialUser { + + @Test + @DisplayName("소셜 사용자 생성 시 기본 상태는 PENDING이고 ROLE_USER를 가진다") + void createsPendingSocialUserWithUserRole() { + User user = User.createSocialUser( + SocialType.KAKAO, + "social-id-123", + "user@example.com", + "https://cdn.example.com/profile.png" + ); + + assertThat(user.getSocialType()).isEqualTo(SocialType.KAKAO); + assertThat(user.getSocialId()).isEqualTo("social-id-123"); + assertThat(user.getEmail()).isEqualTo("user@example.com"); + assertThat(user.getProfileImage()).isEqualTo("https://cdn.example.com/profile.png"); + assertThat(user.getRole()).isEqualTo(Role.USER); + assertThat(user.getStatus()).isEqualTo(UserStatus.PENDING); + assertThat(user.isActive()).isFalse(); + assertThat(user.isWithdrawn()).isFalse(); + assertThat(user.getInterestCategories()).isEmpty(); + } + } + + @Nested + @DisplayName("updateUser") + class UpdateUser { + + @Test + @DisplayName("온보딩 완료 시 계정 정보를 갱신하고 ACTIVE 상태가 된다") + void activatesUserWhenOnboardingCompletes() { + User user = User.createSocialUser(SocialType.KAKAO, "social-id-123", "before@example.com", null); + + user.updateUser("테크포크유저", "after@example.com", "백엔드 개발자입니다."); + + assertThat(user.getNickName()).isEqualTo("테크포크유저"); + assertThat(user.getEmail()).isEqualTo("after@example.com"); + assertThat(user.getDescription()).isEqualTo("백엔드 개발자입니다."); + assertThat(user.getStatus()).isEqualTo(UserStatus.ACTIVE); + assertThat(user.isActive()).isTrue(); + assertThat(user.isWithdrawn()).isFalse(); + } + } + + @Nested + @DisplayName("updateProfile") + class UpdateProfile { + + @Test + @DisplayName("계정 프로필 수정 시 전달된 필드만 변경한다") + void updatesOnlyProvidedProfileFields() { + User user = activeUser(); + + user.updateProfile("새닉네임", null); + + assertThat(user.getNickName()).isEqualTo("새닉네임"); + assertThat(user.getDescription()).isEqualTo("기존 자기소개"); + + user.updateProfile(null, "새 자기소개"); + + assertThat(user.getNickName()).isEqualTo("새닉네임"); + assertThat(user.getDescription()).isEqualTo("새 자기소개"); + assertThat(user.getStatus()).isEqualTo(UserStatus.ACTIVE); + } + } + + @Nested + @DisplayName("withdraw") + class Withdraw { + + @Test + @DisplayName("탈퇴 시 개인정보를 null 처리하고 WITHDRAWN 상태가 된다") + void anonymizesPersonalDataAndMarksWithdrawn() { + User user = activeUser(); + String originalSocialId = user.getSocialId(); + SocialType originalSocialType = user.getSocialType(); + Role originalRole = user.getRole(); + + user.withdraw(); + + assertThat(user.getStatus()).isEqualTo(UserStatus.WITHDRAWN); + assertThat(user.isWithdrawn()).isTrue(); + assertThat(user.isActive()).isFalse(); + assertThat(user.getNickName()).isNull(); + assertThat(user.getEmail()).isNull(); + assertThat(user.getProfileImage()).isNull(); + assertThat(user.getDescription()).isNull(); + assertThat(user.getSocialId()).isEqualTo(originalSocialId); + assertThat(user.getSocialType()).isEqualTo(originalSocialType); + assertThat(user.getRole()).isEqualTo(originalRole); + } + } + + @Nested + @DisplayName("reactivate") + class Reactivate { + + @Test + @DisplayName("재활성화 시 이메일과 프로필 이미지를 복구하고 PENDING 상태가 된다") + void reactivatesUserAsPendingWithRecoveredIdentityData() { + User user = activeUser(); + user.withdraw(); + + user.reactivate("reactivated@example.com", "https://cdn.example.com/reactivated.png"); + + assertThat(user.getEmail()).isEqualTo("reactivated@example.com"); + assertThat(user.getProfileImage()).isEqualTo("https://cdn.example.com/reactivated.png"); + assertThat(user.getStatus()).isEqualTo(UserStatus.PENDING); + assertThat(user.isActive()).isFalse(); + assertThat(user.isWithdrawn()).isFalse(); + assertThat(user.getNickName()).isNull(); + assertThat(user.getDescription()).isNull(); + } + } + + private User activeUser() { + User user = User.createSocialUser( + SocialType.KAKAO, + "social-id-123", + "user@example.com", + "https://cdn.example.com/profile.png" + ); + user.updateUser("기존닉네임", "user@example.com", "기존 자기소개"); + return user; + } +}