Skip to content

Commit 7ddb651

Browse files
authored
feature : admin 기능 분리 및 UserDomainService 테스트코드 작성 (#124)
* feature : admin 기능 분리 및 UserDomainService 테스트코드 작성 * refactor : response implements 추가 * test 개수 줄이기
1 parent 4587dc5 commit 7ddb651

File tree

9 files changed

+282
-26
lines changed

9 files changed

+282
-26
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.ezcode.codetest.application.usermanagement.user.service;
2+
3+
import org.ezcode.codetest.application.usermanagement.user.dto.response.GrantAdminRoleResponse;
4+
import org.ezcode.codetest.domain.user.exception.AdminException;
5+
import org.ezcode.codetest.domain.user.exception.code.AdminExceptionCode;
6+
import org.ezcode.codetest.domain.user.model.entity.AuthUser;
7+
import org.ezcode.codetest.domain.user.model.entity.User;
8+
import org.ezcode.codetest.domain.user.model.enums.UserRole;
9+
import org.ezcode.codetest.domain.user.service.UserDomainService;
10+
import org.springframework.stereotype.Service;
11+
import org.springframework.transaction.annotation.Transactional;
12+
13+
import lombok.RequiredArgsConstructor;
14+
15+
@Service
16+
@RequiredArgsConstructor
17+
public class AdminService {
18+
private final UserDomainService userDomainService;
19+
20+
@Transactional
21+
public GrantAdminRoleResponse grantAdminRole(AuthUser authUser, Long userId) {
22+
if (authUser.getId().equals(userId)) {
23+
throw new AdminException(AdminExceptionCode.GRANT_ADMIN_SELF);
24+
}
25+
User user = userDomainService.getUserById(userId);
26+
if (user.getRole().equals(UserRole.ADMIN)) {
27+
throw new AdminException(AdminExceptionCode.ALREADY_ADMIN_USER);
28+
}
29+
user.modifyUserRole(UserRole.ADMIN);
30+
31+
return new GrantAdminRoleResponse("ADMIN 권한을 부여합니다");
32+
}
33+
}

src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -176,18 +176,4 @@ public UserProfileImageResponse deleteUserProfileImage(AuthUser authUser) {
176176

177177
return new UserProfileImageResponse(null);
178178
}
179-
180-
@Transactional
181-
public GrantAdminRoleResponse grantAdminRole(AuthUser authUser, Long userId) {
182-
if (authUser.getId().equals(userId)) {
183-
throw new UserException(UserExceptionCode.GRANT_ADMIN_SELF);
184-
}
185-
User user = userDomainService.getUserById(userId);
186-
if (user.getRole().equals(UserRole.ADMIN)) {
187-
throw new UserException(UserExceptionCode.ALREADY_ADMIN_USER);
188-
}
189-
user.modifyUserRole(UserRole.ADMIN);
190-
191-
return new GrantAdminRoleResponse("ADMIN 권한을 부여합니다");
192-
}
193179
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.ezcode.codetest.domain.user.exception;
2+
3+
import org.ezcode.codetest.common.base.exception.ResponseCode;
4+
import org.ezcode.codetest.domain.user.exception.code.AdminExceptionCode;
5+
import org.springframework.http.HttpStatus;
6+
7+
import lombok.Getter;
8+
9+
@Getter
10+
public class AdminException extends RuntimeException {
11+
private final AdminExceptionCode responseCode;
12+
private final HttpStatus httpStatus;
13+
private final String message;
14+
15+
public AdminException(AdminExceptionCode responseCode) {
16+
this.responseCode = responseCode;
17+
this.httpStatus = responseCode.getStatus();
18+
this.message = responseCode.getMessage();
19+
}
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.ezcode.codetest.domain.user.exception.code;
2+
3+
import org.ezcode.codetest.common.base.exception.ResponseCode;
4+
import org.springframework.http.HttpStatus;
5+
6+
import lombok.Getter;
7+
import lombok.RequiredArgsConstructor;
8+
9+
@Getter
10+
@RequiredArgsConstructor
11+
public enum AdminExceptionCode implements ResponseCode {
12+
GRANT_ADMIN_SELF(false, HttpStatus.BAD_REQUEST, "본인에게 ADMIN 권한을 부여할 수 없습니다."),
13+
ALREADY_ADMIN_USER(false, HttpStatus.BAD_REQUEST, "이미 ADMIN 권한을 가진 유저입니다.");
14+
15+
private final boolean success;
16+
private final HttpStatus status;
17+
private final String message;
18+
}

src/main/java/org/ezcode/codetest/domain/user/exception/code/UserExceptionCode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ public enum UserExceptionCode implements ResponseCode {
1414
NOT_MATCH_CODE(false, HttpStatus.BAD_REQUEST, "이메일 인증 코드가 일치하지 않습니다."),
1515
NO_GITHUB_INFO(false, HttpStatus.BAD_REQUEST, "깃허브 정보가 없습니다."),
1616
NO_GITHUB_REPO(false, HttpStatus.BAD_REQUEST, "해당하는 Repository를 찾을 수 없습니다."),
17-
GRANT_ADMIN_SELF(false, HttpStatus.BAD_REQUEST, "본인에게 ADMIN 권한을 부여할 수 없습니다."),
18-
ALREADY_ADMIN_USER(false, HttpStatus.BAD_REQUEST, "이미 ADMIN 권한을 가진 유저입니다.");
1917

18+
19+
;
2020
private final boolean success;
2121
private final HttpStatus status;
2222
private final String message;

src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@
77
import org.ezcode.codetest.domain.user.exception.UserException;
88
import org.ezcode.codetest.domain.user.exception.code.UserExceptionCode;
99
import org.ezcode.codetest.domain.user.model.entity.UserAuthType;
10-
import org.ezcode.codetest.domain.user.model.entity.UserGithubInfo;
1110
import org.ezcode.codetest.domain.user.model.enums.Adjective;
1211
import org.ezcode.codetest.domain.user.model.enums.AuthType;
1312
import org.ezcode.codetest.domain.user.model.enums.Noun;
1413
import org.ezcode.codetest.domain.user.exception.AuthException;
1514
import org.ezcode.codetest.domain.user.exception.code.AuthExceptionCode;
1615
import org.ezcode.codetest.domain.user.model.entity.User;
1716
import org.ezcode.codetest.domain.user.repository.UserAuthTypeRepository;
18-
import org.ezcode.codetest.domain.user.repository.UserGithubInfoRepository;
1917
import org.ezcode.codetest.domain.user.repository.UserRepository;
2018
import org.ezcode.codetest.common.security.util.PasswordEncoder;
2119
import org.springframework.stereotype.Service;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.ezcode.codetest.presentation.usermanagement;
2+
3+
import org.ezcode.codetest.application.usermanagement.user.dto.response.GrantAdminRoleResponse;
4+
import org.ezcode.codetest.application.usermanagement.user.service.AdminService;
5+
import org.ezcode.codetest.domain.user.model.entity.AuthUser;
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
9+
import org.springframework.web.bind.annotation.PathVariable;
10+
import org.springframework.web.bind.annotation.PostMapping;
11+
import org.springframework.web.bind.annotation.RequestMapping;
12+
import org.springframework.web.bind.annotation.RestController;
13+
14+
import io.swagger.v3.oas.annotations.Operation;
15+
import io.swagger.v3.oas.annotations.tags.Tag;
16+
import lombok.RequiredArgsConstructor;
17+
18+
@RestController
19+
@RequestMapping("/api/admin")
20+
@RequiredArgsConstructor
21+
@Tag(name = "관리자(Admin) 전용 기능", description = "관리자 권한을 가진 유저만 접근 가능한 기능입니다")
22+
public class AdminController {
23+
private final AdminService adminService;
24+
25+
@Operation(summary = "관리자로 전환", description = "관리자 권한을 가지고 있는 유저는 다른 유저의 권한을 관리자로 수정할 수 있습니다.")
26+
@PostMapping("/users/{userId}/grant-admin")
27+
public ResponseEntity<GrantAdminRoleResponse> grantAdminRole(
28+
@AuthenticationPrincipal AuthUser authUser,
29+
@PathVariable Long userId
30+
){
31+
return ResponseEntity.status(HttpStatus.OK).body(adminService.grantAdminRole(authUser, userId));
32+
}
33+
}

src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,4 @@ public ResponseEntity<WithdrawUserResponse> withdraw(
9494
return ResponseEntity.status(HttpStatus.OK).body(userService.withdrawUser(authUser));
9595
}
9696

97-
@Operation(summary = "유저 권한 전환", description = "관리자 권한을 가지고 있는 유저는 다른 유저의 권한을 수정할 수 있습니다.")
98-
@PostMapping("/admin/users/{userId}/grant-admin")
99-
public ResponseEntity<GrantAdminRoleResponse> grantAdminRole(
100-
@AuthenticationPrincipal AuthUser authUser,
101-
@PathVariable Long userId
102-
){
103-
return ResponseEntity.status(HttpStatus.OK).body(userService.grantAdminRole(authUser, userId));
104-
}
10597
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package org.ezcode.codetest.domain.user;
2+
3+
import org.ezcode.codetest.common.security.util.PasswordEncoder;
4+
import org.ezcode.codetest.domain.user.exception.AuthException;
5+
import org.ezcode.codetest.domain.user.exception.UserException;
6+
import org.ezcode.codetest.domain.user.exception.code.AuthExceptionCode;
7+
import org.ezcode.codetest.domain.user.exception.code.UserExceptionCode;
8+
import org.ezcode.codetest.domain.user.model.entity.User;
9+
import org.ezcode.codetest.domain.user.model.entity.UserAuthType;
10+
import org.ezcode.codetest.domain.user.model.enums.AuthType;
11+
import org.ezcode.codetest.domain.user.model.enums.Tier;
12+
import org.ezcode.codetest.domain.user.model.enums.UserRole;
13+
import org.ezcode.codetest.domain.user.repository.UserAuthTypeRepository;
14+
import org.ezcode.codetest.domain.user.repository.UserRepository;
15+
import org.ezcode.codetest.domain.user.service.UserDomainService;
16+
import org.junit.jupiter.api.BeforeEach;
17+
import org.junit.jupiter.api.Test;
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+
import java.util.List;
23+
import java.util.Optional;
24+
25+
import static org.junit.jupiter.api.Assertions.*;
26+
import static org.mockito.ArgumentMatchers.any;
27+
import static org.mockito.Mockito.*;
28+
29+
@ExtendWith(MockitoExtension.class)
30+
public class UserDomainServiceTest {
31+
32+
@Mock
33+
private UserRepository userRepository;
34+
35+
@Mock
36+
private UserAuthTypeRepository userAuthTypeRepository;
37+
38+
@Mock
39+
private PasswordEncoder passwordEncoder;
40+
41+
42+
@InjectMocks
43+
private UserDomainService userDomainService;
44+
45+
// 테스트 유저 정보 설정
46+
private final User testUser = new User(
47+
48+
"hashedPassword",
49+
"testUser",
50+
"TestNick",
51+
30,
52+
Tier.NEWBIE,
53+
UserRole.USER,
54+
false, // isDeleted
55+
true, // verified
56+
"https://github.com/test",
57+
false // gitPushStatus
58+
) {
59+
public Long getId() { return 1L; }
60+
public int getReviewToken() { return 5; }
61+
public int getZeroReviewToken() { return 0; }
62+
};
63+
private final UserAuthType testAuthType = new UserAuthType(testUser, AuthType.EMAIL);
64+
65+
// 1. 이메일 존재 여부 테스트
66+
@Test
67+
void checkEmailUnique_shouldPassWhenEmailAvailable() {
68+
when(userRepository.findByEmail("[email protected]")).thenReturn(Optional.empty());
69+
assertDoesNotThrow(() -> userDomainService.checkEmailUnique("[email protected]"));
70+
}
71+
72+
@Test
73+
void checkEmailUnique_shouldThrowWhenEmailExistsWithAuthType() {
74+
when(userRepository.findByEmail("[email protected]")).thenReturn(Optional.of(testUser));
75+
when(userAuthTypeRepository.getUserAuthType(testUser)).thenReturn(List.of(AuthType.EMAIL));
76+
77+
AuthException exception = assertThrows(AuthException.class,
78+
() -> userDomainService.checkEmailUnique("[email protected]"));
79+
assertEquals(AuthExceptionCode.ALREADY_EXIST_USER, exception.getResponseCode());
80+
}
81+
82+
// 2. 유저 생성 테스트
83+
@Test
84+
void createUser_shouldCallRepository() {
85+
userDomainService.createUser(testUser);
86+
verify(userRepository).createUser(testUser);
87+
}
88+
89+
@Test
90+
void createUserAuthType_shouldCallRepository() {
91+
userDomainService.createUserAuthType(testAuthType);
92+
verify(userAuthTypeRepository).createUserAuthType(testAuthType);
93+
}
94+
95+
// 3. 유저 존재 테스트
96+
@Test
97+
void getUser_shouldReturnUserWhenExists() {
98+
when(userRepository.findByEmail("[email protected]")).thenReturn(Optional.of(testUser));
99+
User result = userDomainService.getUser("[email protected]");
100+
assertEquals(testUser, result);
101+
}
102+
103+
@Test
104+
void getUser_shouldThrowWhenNotFound() {
105+
when(userRepository.findByEmail("[email protected]")).thenReturn(Optional.empty());
106+
assertThrows(AuthException.class, () -> userDomainService.getUser("[email protected]"));
107+
}
108+
109+
// 4. 비번 검증 테스트
110+
@Test
111+
void userPasswordCheck_shouldPassWhenValid() {
112+
when(userRepository.findByEmail("[email protected]")).thenReturn(Optional.of(testUser));
113+
when(passwordEncoder.matches("correct", "hashedPassword")).thenReturn(true);
114+
115+
assertDoesNotThrow(() ->
116+
userDomainService.userPasswordCheck("[email protected]", "correct"));
117+
}
118+
119+
@Test
120+
void userPasswordCheck_shouldThrowWhenInvalid() {
121+
when(passwordEncoder.matches(anyString(), anyString())).thenReturn(false);
122+
123+
when(userRepository.findByEmail("[email protected]")).thenReturn(Optional.of(testUser));
124+
125+
AuthException exception = assertThrows(AuthException.class,
126+
() -> userDomainService.userPasswordCheck("[email protected]", "wrong"));
127+
128+
assertEquals(AuthExceptionCode.PASSWORD_NOT_MATCH, exception.getResponseCode());
129+
}
130+
131+
132+
// 5. 비번 인코딩 테스트
133+
@Test
134+
void encodePassword_shouldReturnEncodedValue() {
135+
when(passwordEncoder.encode("rawPassword")).thenReturn("encodedPassword");
136+
assertEquals("encodedPassword", userDomainService.encodePassword("rawPassword"));
137+
}
138+
139+
// 6. 인증 타입 테스트
140+
@Test
141+
void getUserAuthTypes_shouldReturnAuthTypes() {
142+
List<AuthType> expectedTypes = List.of(AuthType.EMAIL, AuthType.GOOGLE);
143+
when(userAuthTypeRepository.getUserAuthType(testUser)).thenReturn(expectedTypes);
144+
145+
assertEquals(expectedTypes, userDomainService.getUserAuthTypes(testUser));
146+
}
147+
148+
// 7. 비번 검증 테스트
149+
@Test
150+
void passwordComparison_shouldThrowWhenSame() {
151+
when(passwordEncoder.matches("newPass", "oldHashed")).thenReturn(true);
152+
assertThrows(AuthException.class,
153+
() -> userDomainService.passwordComparison("newPass", "oldHashed"));
154+
}
155+
156+
@Test
157+
void passwordComparison_shouldPassWhenDifferent() {
158+
when(passwordEncoder.matches("newPass", "oldHashed")).thenReturn(false);
159+
assertDoesNotThrow(() ->
160+
userDomainService.passwordComparison("newPass", "oldHashed"));
161+
}
162+
163+
// 8. 탈퇴 회원 테스트
164+
@Test
165+
void isDeletedUser_shouldThrowWhenDeleted() {
166+
User deletedUser = new User("[email protected]","Aa12345**", "username",
167+
"[email protected]", 100, Tier.CODER, UserRole.USER, true, true, "gitUrl.com", true);
168+
assertThrows(AuthException.class, () -> userDomainService.isDeletedUser(deletedUser));
169+
}
170+
171+
@Test
172+
void isDeletedUser_shouldPassWhenActive() {
173+
assertDoesNotThrow(() -> userDomainService.isDeletedUser(testUser));
174+
}
175+
176+
}

0 commit comments

Comments
 (0)