33import com .leets .tdd .global .jwt .JwtProvider ;
44import com .leets .tdd .global .jwt .RefreshTokenHasher ;
55import com .leets .tdd .auth .service .EmailVerificationService ;
6+ import com .leets .tdd .party .domain .PartyParticipant ;
7+ import com .leets .tdd .party .domain .PartyParticipantRole ;
8+ import com .leets .tdd .party .domain .PartyParticipantStatus ;
9+ import com .leets .tdd .party .domain .PartyStatus ;
10+ import com .leets .tdd .party .repository .DeliveryPartyRepository ;
11+ import com .leets .tdd .party .repository .PartyParticipantRepository ;
12+ import com .leets .tdd .settlement .domain .SettlementStatus ;
613import com .leets .tdd .user .domain .DormStatus ;
714import com .leets .tdd .user .domain .Dormitory ;
815import com .leets .tdd .user .domain .User ;
2936
3037import java .time .Duration ;
3138import java .time .LocalDateTime ;
39+ import java .util .List ;
3240import java .util .Optional ;
3341
3442import static org .assertj .core .api .Assertions .assertThat ;
3543import static org .assertj .core .api .Assertions .assertThatThrownBy ;
3644import static org .mockito .ArgumentMatchers .any ;
3745import static org .mockito .ArgumentMatchers .anyString ;
46+ import static org .mockito .ArgumentMatchers .eq ;
3847import static org .mockito .Mockito .never ;
3948import static org .mockito .Mockito .times ;
4049import static org .mockito .Mockito .verify ;
@@ -64,6 +73,12 @@ class UserServiceTest {
6473 @ Mock
6574 private NicknameGenerator nicknameGenerator ;
6675
76+ @ Mock
77+ private DeliveryPartyRepository deliveryPartyRepository ;
78+
79+ @ Mock
80+ private PartyParticipantRepository partyParticipantRepository ;
81+
6782 @ InjectMocks
6883 private UserService userService ;
6984
@@ -511,4 +526,100 @@ void withdraw_passwordMismatch() {
511526 assertThat (user .getStatus ().name ()).isEqualTo ("ACTIVE" );
512527 verify (userRepository , never ()).save (any ());
513528 }
529+
530+ @ Test
531+ @ DisplayName ("방장으로 진행 중인(RECRUITING/CLOSED/ORDERED) 팟이 있으면 탈퇴가 거부된다" )
532+ void withdraw_ongoingPartyAsCreator_throwsActivePotExists () {
533+ User user = newUser ();
534+ when (userRepository .findById (1L )).thenReturn (Optional .of (user ));
535+ when (passwordEncoder .matches ("raw-pw" , "encoded-pw" )).thenReturn (true );
536+ when (deliveryPartyRepository .existsByCreatorIdAndStatusIn (eq (1L ), any ())).thenReturn (true );
537+
538+ assertThatThrownBy (() -> userService .withdraw (1L , new WithdrawalRequest ("raw-pw" )))
539+ .isInstanceOf (UserException .class )
540+ .hasMessage (UserErrorCode .ACTIVE_POT_EXISTS .getMessage ());
541+
542+ verify (userRepository , never ()).save (any ());
543+ }
544+
545+ @ Test
546+ @ DisplayName ("방장이지만 COMPLETED인데 정산이 안 끝난 팟이 있으면 탈퇴가 거부된다" )
547+ void withdraw_completedButUnsettledAsCreator_throwsUnsettledPotExists () {
548+ User user = newUser ();
549+ when (userRepository .findById (1L )).thenReturn (Optional .of (user ));
550+ when (passwordEncoder .matches ("raw-pw" , "encoded-pw" )).thenReturn (true );
551+ when (deliveryPartyRepository .existsByCreatorIdAndStatusIn (eq (1L ), any ())).thenReturn (false );
552+ when (deliveryPartyRepository .existsByCreatorIdAndStatusAndSettlementStatusNotIn (
553+ eq (1L ), eq (PartyStatus .COMPLETED ), any ())).thenReturn (true );
554+
555+ assertThatThrownBy (() -> userService .withdraw (1L , new WithdrawalRequest ("raw-pw" )))
556+ .isInstanceOf (UserException .class )
557+ .hasMessage (UserErrorCode .UNSETTLED_POT_EXISTS .getMessage ());
558+
559+ verify (userRepository , never ()).save (any ());
560+ }
561+
562+ @ Test
563+ @ DisplayName ("참여자로 진행 중인(RECRUITING/CLOSED/ORDERED) 팟이 있으면 탈퇴가 거부된다" )
564+ void withdraw_ongoingPartyAsParticipant_throwsActivePotExists () {
565+ User user = newUser ();
566+ when (userRepository .findById (1L )).thenReturn (Optional .of (user ));
567+ when (passwordEncoder .matches ("raw-pw" , "encoded-pw" )).thenReturn (true );
568+ when (deliveryPartyRepository .existsByCreatorIdAndStatusIn (eq (1L ), any ())).thenReturn (false );
569+ when (deliveryPartyRepository .existsByCreatorIdAndStatusAndSettlementStatusNotIn (
570+ eq (1L ), eq (PartyStatus .COMPLETED ), any ())).thenReturn (false );
571+ PartyParticipant participation = new PartyParticipant (
572+ 10L , 1L , PartyParticipantRole .MEMBER , PartyParticipantStatus .JOINED , LocalDateTime .now ());
573+ when (partyParticipantRepository .findAllByUserIdAndStatus (1L , PartyParticipantStatus .JOINED ))
574+ .thenReturn (List .of (participation ));
575+ when (deliveryPartyRepository .existsByIdInAndStatusIn (eq (List .of (10L )), any ())).thenReturn (true );
576+
577+ assertThatThrownBy (() -> userService .withdraw (1L , new WithdrawalRequest ("raw-pw" )))
578+ .isInstanceOf (UserException .class )
579+ .hasMessage (UserErrorCode .ACTIVE_POT_EXISTS .getMessage ());
580+
581+ verify (userRepository , never ()).save (any ());
582+ }
583+
584+ @ Test
585+ @ DisplayName ("참여자로 속한 팟이 COMPLETED인데 정산이 안 끝났으면 탈퇴가 거부된다" )
586+ void withdraw_completedButUnsettledAsParticipant_throwsUnsettledPotExists () {
587+ User user = newUser ();
588+ when (userRepository .findById (1L )).thenReturn (Optional .of (user ));
589+ when (passwordEncoder .matches ("raw-pw" , "encoded-pw" )).thenReturn (true );
590+ when (deliveryPartyRepository .existsByCreatorIdAndStatusIn (eq (1L ), any ())).thenReturn (false );
591+ when (deliveryPartyRepository .existsByCreatorIdAndStatusAndSettlementStatusNotIn (
592+ eq (1L ), eq (PartyStatus .COMPLETED ), any ())).thenReturn (false );
593+ PartyParticipant participation = new PartyParticipant (
594+ 10L , 1L , PartyParticipantRole .MEMBER , PartyParticipantStatus .JOINED , LocalDateTime .now ());
595+ when (partyParticipantRepository .findAllByUserIdAndStatus (1L , PartyParticipantStatus .JOINED ))
596+ .thenReturn (List .of (participation ));
597+ when (deliveryPartyRepository .existsByIdInAndStatusIn (eq (List .of (10L )), any ())).thenReturn (false );
598+ when (deliveryPartyRepository .existsByIdInAndStatusAndSettlementStatusNotIn (
599+ eq (List .of (10L )), eq (PartyStatus .COMPLETED ), any ())).thenReturn (true );
600+
601+ assertThatThrownBy (() -> userService .withdraw (1L , new WithdrawalRequest ("raw-pw" )))
602+ .isInstanceOf (UserException .class )
603+ .hasMessage (UserErrorCode .UNSETTLED_POT_EXISTS .getMessage ());
604+
605+ verify (userRepository , never ()).save (any ());
606+ }
607+
608+ @ Test
609+ @ DisplayName ("참여 중인 팟이 없거나 모두 정산 완료/취소 상태면 탈퇴가 성공한다" )
610+ void withdraw_noOngoingParty_succeeds () {
611+ User user = newUser ();
612+ when (userRepository .findById (1L )).thenReturn (Optional .of (user ));
613+ when (passwordEncoder .matches ("raw-pw" , "encoded-pw" )).thenReturn (true );
614+ when (deliveryPartyRepository .existsByCreatorIdAndStatusIn (eq (1L ), any ())).thenReturn (false );
615+ when (deliveryPartyRepository .existsByCreatorIdAndStatusAndSettlementStatusNotIn (
616+ eq (1L ), eq (PartyStatus .COMPLETED ), any ())).thenReturn (false );
617+ when (partyParticipantRepository .findAllByUserIdAndStatus (1L , PartyParticipantStatus .JOINED ))
618+ .thenReturn (List .of ());
619+
620+ userService .withdraw (1L , new WithdrawalRequest ("raw-pw" ));
621+
622+ assertThat (user .getStatus ().name ()).isEqualTo ("DELETED" );
623+ verify (userRepository ).save (user );
624+ }
514625}
0 commit comments