1+ package BE_Elixir .Elixir .domain .achievement .service ;
2+
3+ import BE_Elixir .Elixir .domain .achievement .dto .AchievementCategoryDTO ;
4+ import BE_Elixir .Elixir .domain .achievement .dto .AllAchievementStatusResponseDTO ;
5+ import BE_Elixir .Elixir .domain .achievement .entity .Achievement ;
6+ import BE_Elixir .Elixir .domain .achievement .entity .MemberAchievement ;
7+ import BE_Elixir .Elixir .domain .achievement .entity .MemberStats ;
8+ import BE_Elixir .Elixir .domain .achievement .repository .AchievementRepository ;
9+ import BE_Elixir .Elixir .domain .achievement .repository .MemberAchievementRepository ;
10+ import BE_Elixir .Elixir .domain .achievement .repository .MemberStatsRepository ;
11+ import BE_Elixir .Elixir .domain .member .entity .Member ;
12+ import BE_Elixir .Elixir .domain .member .entity .MemberDetails ;
13+ import BE_Elixir .Elixir .domain .member .repository .MemberRepository ;
14+ import BE_Elixir .Elixir .global .enums .AchievementType ;
15+ import BE_Elixir .Elixir .global .exception .CustomException ;
16+ import BE_Elixir .Elixir .global .exception .ErrorCode ;
17+ import org .junit .jupiter .api .*;
18+ import org .junit .jupiter .api .extension .ExtendWith ;
19+ import org .mockito .InjectMocks ;
20+ import org .mockito .Mock ;
21+ import org .mockito .junit .jupiter .MockitoExtension ;
22+
23+ import java .time .LocalDateTime ;
24+ import java .util .List ;
25+ import java .util .Optional ;
26+
27+ import static org .assertj .core .api .Assertions .*;
28+ import static org .mockito .ArgumentMatchers .*;
29+ import static org .mockito .Mockito .*;
30+
31+ @ ExtendWith (MockitoExtension .class )
32+ @ DisplayName ("AchievementService 테스트" )
33+ class AchievementServiceTest {
34+ @ InjectMocks
35+ private AchievementService service ;
36+
37+ @ Mock private MemberAchievementRepository memberAchievementRepository ;
38+ @ Mock private AchievementRepository achievementRepository ;
39+ @ Mock private MemberRepository memberRepository ;
40+ @ Mock private MemberStatsRepository memberStatsRepository ;
41+
42+ private Member member ;
43+ private MemberDetails memberDetails ;
44+ private Achievement achievement ;
45+ private MemberAchievement memberAchievement ;
46+ private MemberStats memberStats ;
47+
48+ @ BeforeEach
49+ void setUp () {
50+ member = Member .builder ()
51+ .id (1L )
52+ .email ("test@example.com" )
53+ .nickname ("tester" )
54+ .build ();
55+
56+ memberDetails = new MemberDetails (member );
57+
58+ achievement = new Achievement ();
59+ achievement .setId (100L );
60+ achievement .setType (AchievementType .TOTAL_LOGIN_DAYS );
61+ achievement .setAchievementName ("첫 로그인 업적" );
62+ achievement .setDescription ("로그인을 처음 완료했습니다." );
63+ achievement .setLevel (1 );
64+ achievement .setTargetValue (1 );
65+
66+ memberAchievement = new MemberAchievement ();
67+ memberAchievement .setMember (member );
68+ memberAchievement .setAchievement (achievement );
69+ memberAchievement .setCurrentProgress (1 );
70+ memberAchievement .setCompleted (true );
71+ memberAchievement .setCompletedAt (LocalDateTime .now ());
72+
73+ memberStats = new MemberStats ();
74+ memberStats .setMemberId (member .getId ());
75+ memberStats .setTotalLoginDays (1 );
76+ memberStats .setConsecutiveLoginDays (1 );
77+ memberStats .setTotalDietLogs (0 );
78+ memberStats .setTotalRecipeLogs (0 );
79+ memberStats .setTotalScraps (0 );
80+ memberStats .setTotalFollowers (0 );
81+ }
82+
83+
84+ @ Nested
85+ @ DisplayName ("업적 목록 조회 테스트" )
86+ class GetMyAchievements {
87+
88+ @ Test
89+ @ DisplayName ("성공: 회원 업적 목록 조회" )
90+ void getMyAchievements_success () {
91+ // given
92+ when (memberRepository .findByEmail (memberDetails .getUsername ()))
93+ .thenReturn (Optional .of (member ));
94+ when (memberStatsRepository .findById (member .getId ()))
95+ .thenReturn (Optional .of (memberStats ));
96+ when (memberAchievementRepository .findAllByMember (member ))
97+ .thenReturn (List .of (memberAchievement ));
98+ when (achievementRepository .findAll ())
99+ .thenReturn (List .of (achievement ));
100+
101+ // when
102+ AllAchievementStatusResponseDTO result = service .getMyAchievements (memberDetails );
103+
104+ // then
105+ assertThat (result ).isNotNull ();
106+ assertThat (result .getAchievementMap ()).containsKey (AchievementType .TOTAL_LOGIN_DAYS );
107+
108+ AchievementCategoryDTO categoryDTO = result .getAchievementMap ().get (AchievementType .TOTAL_LOGIN_DAYS );
109+ assertThat (categoryDTO ).isNotNull ();
110+ assertThat (categoryDTO .getCurrentValue ()).isEqualTo (1 );
111+ assertThat (categoryDTO .getAchievements ()).hasSize (1 );
112+ assertThat (categoryDTO .getAchievements ().get (0 ).getName ())
113+ .isEqualTo ("첫 로그인 업적" );
114+
115+ verify (memberRepository ).findByEmail (anyString ());
116+ }
117+
118+ @ Test
119+ @ DisplayName ("예외: 회원이 존재하지 않을 경우" )
120+ void getMyAchievements_memberNotFound () {
121+ // given
122+ when (memberRepository .findByEmail (anyString ()))
123+ .thenReturn (Optional .empty ());
124+
125+ // when & then
126+ assertThatThrownBy (() -> service .getMyAchievements (memberDetails ))
127+ .isInstanceOf (CustomException .class )
128+ .hasMessage (ErrorCode .MEMBER_NOT_FOUND .getMessage ());
129+ }
130+ }
131+
132+ @ Nested
133+ @ DisplayName ("업적 달성 동기화 테스트" )
134+ class SyncUserAchievements {
135+
136+ @ Test
137+ @ DisplayName ("성공: 기존 업적의 진행도 갱신" )
138+ void syncUserAchievements_updateProgress () {
139+ // given
140+ when (memberRepository .findById (member .getId ()))
141+ .thenReturn (Optional .of (member ));
142+ when (achievementRepository .findAllByType (AchievementType .TOTAL_LOGIN_DAYS ))
143+ .thenReturn (List .of (achievement ));
144+ when (memberAchievementRepository .findAllByMember (member ))
145+ .thenReturn (List .of (memberAchievement ));
146+
147+ // when
148+ service .syncUserAchievements (member .getId (), AchievementType .TOTAL_LOGIN_DAYS , 5 );
149+
150+ // then
151+ assertThat (memberAchievement .getCurrentProgress ()).isEqualTo (5 );
152+ assertThat (memberAchievement .isCompleted ()).isTrue ();
153+ verify (memberAchievementRepository ).save (any (MemberAchievement .class ));
154+ }
155+
156+ @ Test
157+ @ DisplayName ("예외: 회원이 존재하지 않을 경우" )
158+ void syncUserAchievements_memberNotFound () {
159+ // given
160+ when (memberRepository .findById (anyLong ()))
161+ .thenReturn (Optional .empty ());
162+
163+ // when & then
164+ assertThatThrownBy (() -> service .syncUserAchievements (999L , AchievementType .TOTAL_LOGIN_DAYS , 5 ))
165+ .isInstanceOf (CustomException .class )
166+ .hasMessage (ErrorCode .MEMBER_NOT_FOUND .getMessage ());
167+ }
168+ }
169+ }
0 commit comments