diff --git a/build.gradle b/build.gradle index 5c4f11d5..c592fc67 100644 --- a/build.gradle +++ b/build.gradle @@ -59,6 +59,9 @@ dependencies { // fcm implementation 'com.google.firebase:firebase-admin:6.8.1' + // thymeleaf + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + // etc implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/src/main/java/com/backend/blooming/admin/application/AdminPageService.java b/src/main/java/com/backend/blooming/admin/application/AdminPageService.java new file mode 100644 index 00000000..9f627352 --- /dev/null +++ b/src/main/java/com/backend/blooming/admin/application/AdminPageService.java @@ -0,0 +1,45 @@ +package com.backend.blooming.admin.application; + +import com.backend.blooming.admin.controller.dto.CreateUserRequest; +import com.backend.blooming.authentication.infrastructure.oauth.OAuthType; +import com.backend.blooming.themecolor.domain.ThemeColor; +import com.backend.blooming.user.domain.Email; +import com.backend.blooming.user.domain.Name; +import com.backend.blooming.user.domain.User; +import com.backend.blooming.user.infrastructure.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.UUID; + +@Service +@Transactional +@RequiredArgsConstructor +public class AdminPageService { + + private static final String DEFAULT_EMAIL = "test@email.com"; + private final UserRepository userRepository; + + public Long createUser(final CreateUserRequest request) { + final User user = User.builder() + .oAuthId(UUID.randomUUID().toString()) + .oAuthType(OAuthType.KAKAO) + .name(new Name(request.name())) + .email(processEmail(request.email())) + .color(ThemeColor.valueOf(request.theme())) + .statusMessage(request.statusMessage()) + .build(); + + return userRepository.save(user) + .getId(); + } + + private Email processEmail(final String email) { + if (email == null || email.isEmpty()) { + return new Email(DEFAULT_EMAIL); + } + + return new Email(email); + } +} diff --git a/src/main/java/com/backend/blooming/admin/controller/AdminPageController.java b/src/main/java/com/backend/blooming/admin/controller/AdminPageController.java new file mode 100644 index 00000000..567af31f --- /dev/null +++ b/src/main/java/com/backend/blooming/admin/controller/AdminPageController.java @@ -0,0 +1,138 @@ +package com.backend.blooming.admin.controller; + +import com.backend.blooming.admin.application.AdminPageService; +import com.backend.blooming.admin.controller.dto.CreateGoalRequest; +import com.backend.blooming.admin.controller.dto.CreateUserRequest; +import com.backend.blooming.admin.controller.dto.FriendStatus; +import com.backend.blooming.admin.controller.dto.RequestFriendRequest; +import com.backend.blooming.admin.controller.dto.UpdateFriendRequest; +import com.backend.blooming.admin.controller.exception.InvalidFriendStatusException; +import com.backend.blooming.friend.application.FriendService; +import com.backend.blooming.friend.application.exception.NotFoundFriendRequestException; +import com.backend.blooming.friend.infrastructure.repository.FriendRepository; +import com.backend.blooming.goal.application.GoalService; +import com.backend.blooming.goal.application.dto.CreateGoalDto; +import com.backend.blooming.themecolor.domain.ThemeColor; +import com.backend.blooming.user.domain.User; +import com.backend.blooming.user.infrastructure.repository.UserRepository; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Profile; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.Arrays; +import java.util.List; + +@Profile("test | local | dev") +@Controller +@RequestMapping("/admin") +@RequiredArgsConstructor +public class AdminPageController { + + private final AdminPageService adminPageService; + private final UserRepository userRepository; + private final FriendService friendService; + private final FriendRepository friendRepository; + private final GoalService goalService; + + @GetMapping + public String adminPage(final Model model) { + model.addAttribute("themes", getThemes()); + model.addAttribute("users", getUsers()); + + return "admin/test"; + } + + private static List getThemes() { + return Arrays.stream(ThemeColor.values()) + .map(ThemeColor::name) + .toList(); + } + + private List getUsers() { + return userRepository.findAll(); + } + + @PostMapping("/user") + public ResponseEntity createUser(@RequestBody @Valid CreateUserRequest request) { + adminPageService.createUser(request); + + return ResponseEntity.noContent() + .build(); + } + + @PostMapping("/friend") + public ResponseEntity requestFriend(@RequestBody RequestFriendRequest request) { + friendService.request(request.requestUser(), request.requestedUser()); + + return ResponseEntity.noContent() + .build(); + } + + @PatchMapping("/friend") + public ResponseEntity updateFriendStatus(@RequestBody UpdateFriendRequest request) { + final Long friendId = getFriendId(request); + updateByStatus(request, friendId); + + return ResponseEntity.noContent() + .build(); + } + + private Long getFriendId(final UpdateFriendRequest request) { + final Long friendId = friendRepository.findByRequestUserIdAndRequestedUserId( + request.requestUser(), + request.requestedUser() + ); + + if (friendId == null) { + throw new NotFoundFriendRequestException(); + } + + return friendId; + } + + private void updateByStatus(final UpdateFriendRequest request, final Long friendId) { + final FriendStatus friendStatus = FriendStatus.valueOf(request.status()); + + if (FriendStatus.FRIEND.equals(friendStatus)) { + friendService.accept(request.requestedUser(), friendId); + return; + } + if (FriendStatus.DELETE_BY_REQUEST.equals(friendStatus)) { + friendService.delete(request.requestUser(), friendId); + return; + } + if (FriendStatus.DELETE_BY_REQUESTED.equals(friendStatus)) { + friendService.delete(request.requestedUser(), friendId); + return; + } + + throw new InvalidFriendStatusException(); + } + + @PostMapping("/goal") + public ResponseEntity createGoal(@RequestBody @Valid CreateGoalRequest request) { + goalService.createGoal(convertToDto(request)); + + return ResponseEntity.noContent() + .build(); + } + + private CreateGoalDto convertToDto(final CreateGoalRequest request) { + return new CreateGoalDto( + request.name(), + request.memo(), + request.startDate(), + request.endDate(), + request.manager(), + List.of(request.manager(), request.team()) + ); + } +} diff --git a/src/main/java/com/backend/blooming/admin/controller/dto/CreateGoalRequest.java b/src/main/java/com/backend/blooming/admin/controller/dto/CreateGoalRequest.java new file mode 100644 index 00000000..cf9f0e81 --- /dev/null +++ b/src/main/java/com/backend/blooming/admin/controller/dto/CreateGoalRequest.java @@ -0,0 +1,29 @@ +package com.backend.blooming.admin.controller.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; + +import java.time.LocalDate; + +public record CreateGoalRequest( + @NotEmpty(message = "이름이 입력되지 않았습니다.") + String name, + + String memo, + + @NotNull(message = "시작일이 입력되지 않았습니다.") + LocalDate startDate, + + @NotNull(message = "종료일이 입력되지 않았습니다.") + LocalDate endDate, + + @NotNull(message = "골 생성자가 설정되지 않았습니다.") + @Positive(message = "골 생성자의 아이디가 잘못됐습니다.") + Long manager, + + @NotNull(message = "팀원이 설정되지 않았습니다.") + @Positive(message = "팀원의 아이디가 잘못됐습니다.") + Long team +) { +} diff --git a/src/main/java/com/backend/blooming/admin/controller/dto/CreateUserRequest.java b/src/main/java/com/backend/blooming/admin/controller/dto/CreateUserRequest.java new file mode 100644 index 00000000..c590b93d --- /dev/null +++ b/src/main/java/com/backend/blooming/admin/controller/dto/CreateUserRequest.java @@ -0,0 +1,12 @@ +package com.backend.blooming.admin.controller.dto; + +import jakarta.validation.constraints.NotEmpty; + +public record CreateUserRequest( + @NotEmpty(message = "사용자 이름을 입력되지 않았습니다.") + String name, + String email, + String theme, + String statusMessage +) { +} diff --git a/src/main/java/com/backend/blooming/admin/controller/dto/FriendStatus.java b/src/main/java/com/backend/blooming/admin/controller/dto/FriendStatus.java new file mode 100644 index 00000000..36b48df0 --- /dev/null +++ b/src/main/java/com/backend/blooming/admin/controller/dto/FriendStatus.java @@ -0,0 +1,8 @@ +package com.backend.blooming.admin.controller.dto; + +public enum FriendStatus { + + FRIEND, + DELETE_BY_REQUEST, + DELETE_BY_REQUESTED +} diff --git a/src/main/java/com/backend/blooming/admin/controller/dto/RequestFriendRequest.java b/src/main/java/com/backend/blooming/admin/controller/dto/RequestFriendRequest.java new file mode 100644 index 00000000..b41b6f7b --- /dev/null +++ b/src/main/java/com/backend/blooming/admin/controller/dto/RequestFriendRequest.java @@ -0,0 +1,15 @@ +package com.backend.blooming.admin.controller.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Positive; + +public record RequestFriendRequest( + @NotEmpty(message = "친구 요청자가 입력되지 않았습니다.") + @Positive(message = "친구 요청자의 아이디가 잘못됐습니다.") + Long requestUser, + + @NotEmpty(message = "친구 요청 수신자가 입력되지 않았습니다.") + @Positive(message = "친구 요청 수신자의 아이디가 잘못됐습니다.") + Long requestedUser +) { +} diff --git a/src/main/java/com/backend/blooming/admin/controller/dto/UpdateFriendRequest.java b/src/main/java/com/backend/blooming/admin/controller/dto/UpdateFriendRequest.java new file mode 100644 index 00000000..6ce79146 --- /dev/null +++ b/src/main/java/com/backend/blooming/admin/controller/dto/UpdateFriendRequest.java @@ -0,0 +1,18 @@ +package com.backend.blooming.admin.controller.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Positive; + +public record UpdateFriendRequest( + @NotEmpty(message = "친구 요청자가 입력되지 않았습니다.") + @Positive(message = "친구 요청자의 아이디가 잘못됐습니다.") + Long requestUser, + + @NotEmpty(message = "친구 요청 수신자가 입력되지 않았습니다.") + @Positive(message = "친구 요청 수신자의 아이디가 잘못됐습니다.") + Long requestedUser, + + @NotEmpty(message = "친구 요청 상태가 입력되지 않았습니다.") + String status +) { +} diff --git a/src/main/java/com/backend/blooming/admin/controller/exception/InvalidFriendStatusException.java b/src/main/java/com/backend/blooming/admin/controller/exception/InvalidFriendStatusException.java new file mode 100644 index 00000000..cf290818 --- /dev/null +++ b/src/main/java/com/backend/blooming/admin/controller/exception/InvalidFriendStatusException.java @@ -0,0 +1,11 @@ +package com.backend.blooming.admin.controller.exception; + +import com.backend.blooming.exception.BloomingException; +import com.backend.blooming.exception.ExceptionMessage; + +public class InvalidFriendStatusException extends BloomingException { + + public InvalidFriendStatusException() { + super(ExceptionMessage.INVALID_FRIEND_STATUS); + } +} diff --git a/src/main/java/com/backend/blooming/authentication/configuration/AuthenticationWebMvcConfiguration.java b/src/main/java/com/backend/blooming/authentication/configuration/AuthenticationWebMvcConfiguration.java index 0d96ad22..0da5d1c7 100644 --- a/src/main/java/com/backend/blooming/authentication/configuration/AuthenticationWebMvcConfiguration.java +++ b/src/main/java/com/backend/blooming/authentication/configuration/AuthenticationWebMvcConfiguration.java @@ -20,7 +20,8 @@ public class AuthenticationWebMvcConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(final InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor) - .addPathPatterns("/**"); + .addPathPatterns("/**") + .excludePathPatterns("/admin/**"); } @Override diff --git a/src/main/java/com/backend/blooming/exception/ExceptionMessage.java b/src/main/java/com/backend/blooming/exception/ExceptionMessage.java index ede2c3c8..5108818f 100644 --- a/src/main/java/com/backend/blooming/exception/ExceptionMessage.java +++ b/src/main/java/com/backend/blooming/exception/ExceptionMessage.java @@ -34,6 +34,7 @@ public enum ExceptionMessage { NOT_FOUND_DEVICE_TOKEN("디바이스 토큰을 찾을 수 없습니다."), // 친구 + SELF_REQUEST_NOT_ALLOWED("자신에게는 친구 요청할 수 없습니다."), ALREADY_REQUESTED_FRIEND("이미 친구를 요청한 사용자입니다."), NOT_FOUND_FRIEND_REQUEST("해당 친구 요청을 조회할 수 없습니다."), FRIEND_ACCEPTANCE_FORBIDDEN("친구 요청을 수락할 권한이 없습니다."), @@ -52,7 +53,10 @@ public enum ExceptionMessage { INVALID_USER_TO_PARTICIPATE("골에 참여할 수 없는 사용자입니다. 골에는 친구인 사용자만 초대할 수 있습니다."), DELETE_GOAL_FORBIDDEN("골을 삭제할 권한이 없습니다."), UPDATE_GOAL_FORBIDDEN("골을 수정할 권한이 없습니다."), - UPDATE_TEAMS_FORBIDDEN("골 참여자 목록은 비어있을 수 없습니다."); + UPDATE_TEAMS_FORBIDDEN("골 참여자 목록은 비어있을 수 없습니다."), + + // 관리자 페이지 + INVALID_FRIEND_STATUS("잘못된 친구 상태입니다."); private final String message; } diff --git a/src/main/java/com/backend/blooming/exception/GlobalExceptionHandler.java b/src/main/java/com/backend/blooming/exception/GlobalExceptionHandler.java index 6833fbff..0844b6e1 100644 --- a/src/main/java/com/backend/blooming/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/backend/blooming/exception/GlobalExceptionHandler.java @@ -5,9 +5,9 @@ import com.backend.blooming.authentication.infrastructure.exception.OAuthException; import com.backend.blooming.authentication.infrastructure.exception.UnsupportedOAuthTypeException; import com.backend.blooming.exception.dto.ExceptionResponse; -import com.backend.blooming.friend.application.exception.AlreadyRequestedFriendException; import com.backend.blooming.friend.application.exception.DeleteFriendForbiddenException; import com.backend.blooming.friend.application.exception.FriendAcceptanceForbiddenException; +import com.backend.blooming.friend.application.exception.FriendRequestNotAllowedException; import com.backend.blooming.friend.application.exception.NotFoundFriendRequestException; import com.backend.blooming.goal.application.exception.DeleteGoalForbiddenException; import com.backend.blooming.goal.application.exception.InvalidGoalException; @@ -136,9 +136,9 @@ public ResponseEntity handleNotFoundGoalException( .body(new ExceptionResponse(exception.getMessage())); } - @ExceptionHandler(AlreadyRequestedFriendException.class) + @ExceptionHandler(FriendRequestNotAllowedException.class) public ResponseEntity handleAlreadyRequestedFriendException( - final AlreadyRequestedFriendException exception + final FriendRequestNotAllowedException exception ) { logger.warn(String.format(LOG_MESSAGE_FORMAT, exception.getClass().getSimpleName(), exception.getMessage())); diff --git a/src/main/java/com/backend/blooming/friend/application/FriendService.java b/src/main/java/com/backend/blooming/friend/application/FriendService.java index 8c83726e..873ede69 100644 --- a/src/main/java/com/backend/blooming/friend/application/FriendService.java +++ b/src/main/java/com/backend/blooming/friend/application/FriendService.java @@ -2,9 +2,9 @@ import com.backend.blooming.friend.application.dto.FriendType; import com.backend.blooming.friend.application.dto.ReadFriendsDto; -import com.backend.blooming.friend.application.exception.AlreadyRequestedFriendException; import com.backend.blooming.friend.application.exception.DeleteFriendForbiddenException; import com.backend.blooming.friend.application.exception.FriendAcceptanceForbiddenException; +import com.backend.blooming.friend.application.exception.FriendRequestNotAllowedException; import com.backend.blooming.friend.application.exception.NotFoundFriendRequestException; import com.backend.blooming.friend.domain.Friend; import com.backend.blooming.friend.infrastructure.repository.FriendRepository; @@ -28,7 +28,7 @@ public class FriendService { private final NotificationService notificationService; public Long request(final Long userId, final Long friendId) { - validateFriendStatus(userId, friendId); + validateRequestFriend(userId, friendId); final User user = getUser(userId); final User friendUser = getUser(friendId); @@ -40,9 +40,12 @@ public Long request(final Long userId, final Long friendId) { return friend.getId(); } - private void validateFriendStatus(final Long userId, final Long friendId) { + private void validateRequestFriend(final Long userId, final Long friendId) { + if (userId.equals(friendId)) { + throw new FriendRequestNotAllowedException.SelfRequestNotAllowedException(); + } if (friendRepository.existsByRequestFriend(userId, friendId)) { - throw new AlreadyRequestedFriendException(); + throw new FriendRequestNotAllowedException.AlreadyRequestedFriendException(); } } diff --git a/src/main/java/com/backend/blooming/friend/application/exception/AlreadyRequestedFriendException.java b/src/main/java/com/backend/blooming/friend/application/exception/AlreadyRequestedFriendException.java deleted file mode 100644 index 0b7bfdfd..00000000 --- a/src/main/java/com/backend/blooming/friend/application/exception/AlreadyRequestedFriendException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.backend.blooming.friend.application.exception; - -import com.backend.blooming.exception.BloomingException; -import com.backend.blooming.exception.ExceptionMessage; - -public class AlreadyRequestedFriendException extends BloomingException { - - public AlreadyRequestedFriendException() { - super(ExceptionMessage.ALREADY_REQUESTED_FRIEND); - } -} diff --git a/src/main/java/com/backend/blooming/friend/application/exception/FriendRequestNotAllowedException.java b/src/main/java/com/backend/blooming/friend/application/exception/FriendRequestNotAllowedException.java new file mode 100644 index 00000000..c070cfdf --- /dev/null +++ b/src/main/java/com/backend/blooming/friend/application/exception/FriendRequestNotAllowedException.java @@ -0,0 +1,25 @@ +package com.backend.blooming.friend.application.exception; + +import com.backend.blooming.exception.BloomingException; +import com.backend.blooming.exception.ExceptionMessage; + +public class FriendRequestNotAllowedException extends BloomingException { + + private FriendRequestNotAllowedException(final ExceptionMessage exceptionMessage) { + super(exceptionMessage); + } + + public static class SelfRequestNotAllowedException extends FriendRequestNotAllowedException { + + public SelfRequestNotAllowedException() { + super(ExceptionMessage.SELF_REQUEST_NOT_ALLOWED); + } + } + + public static class AlreadyRequestedFriendException extends FriendRequestNotAllowedException { + + public AlreadyRequestedFriendException() { + super(ExceptionMessage.ALREADY_REQUESTED_FRIEND); + } + } +} diff --git a/src/main/java/com/backend/blooming/friend/infrastructure/repository/FriendRepository.java b/src/main/java/com/backend/blooming/friend/infrastructure/repository/FriendRepository.java index 7cfb07fa..69d3f354 100644 --- a/src/main/java/com/backend/blooming/friend/infrastructure/repository/FriendRepository.java +++ b/src/main/java/com/backend/blooming/friend/infrastructure/repository/FriendRepository.java @@ -9,40 +9,47 @@ public interface FriendRepository extends JpaRepository { @Query(""" - SELECT EXISTS( - SELECT 1 - FROM Friend f - WHERE (f.requestUser.id = :requestUserId AND f.requestedUser.id = :requestedUserId) - OR (f.requestUser.id = :requestedUserId AND f.requestedUser.id = :requestUserId) - ) as exist + SELECT EXISTS( + SELECT 1 + FROM Friend f + WHERE (f.requestUser.id = :requestUserId AND f.requestedUser.id = :requestedUserId) + OR (f.requestUser.id = :requestedUserId AND f.requestedUser.id = :requestUserId) + ) as exist """) boolean existsByRequestFriend(final Long requestUserId, final Long requestedUserId); @Query(""" - SELECT f - FROM Friend f - JOIN FETCH f.requestedUser - WHERE f.requestUser.id = :userId AND f.isFriends = FALSE + SELECT f + FROM Friend f + JOIN FETCH f.requestedUser + WHERE f.requestUser.id = :userId AND f.isFriends = FALSE """) List findAllByRequestUserId(final Long userId); @Query(""" - SELECT f - FROM Friend f - JOIN FETCH f.requestUser - WHERE f.requestedUser.id = :userId AND f.isFriends = FALSE + SELECT f + FROM Friend f + JOIN FETCH f.requestUser + WHERE f.requestedUser.id = :userId AND f.isFriends = FALSE """) List findAllByRequestedUserId(final Long userId); @Query(""" - SELECT f - FROM Friend f - JOIN FETCH f.requestUser - JOIN FETCH f.requestedUser - WHERE (f.requestUser.id = :userId OR f.requestedUser.id = :userId) AND f.isFriends = TRUE + SELECT f + FROM Friend f + JOIN FETCH f.requestUser + JOIN FETCH f.requestedUser + WHERE (f.requestUser.id = :userId OR f.requestedUser.id = :userId) AND f.isFriends = TRUE """) List findAllByUserIdAndIsFriends(final Long userId); + @Query(""" + SELECT f.id + FROM Friend f + WHERE f.requestUser.id = :requestId AND f.requestedUser.id = :requestedId + """) + Long findByRequestUserIdAndRequestedUserId(final Long requestId, final Long requestedId); + @Query(""" SELECT COUNT (f) FROM Friend f diff --git a/src/main/resources/templates/admin/test.html b/src/main/resources/templates/admin/test.html new file mode 100644 index 00000000..61e4e652 --- /dev/null +++ b/src/main/resources/templates/admin/test.html @@ -0,0 +1,274 @@ + + + + + + 블루밍 어드민 테스트 + + + +
+

블루밍 관리자 테스트 페이지

+ +
+

사용자 생성

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+

친구 요청 생성

+
+
+ + +
+
+ + +
+
+ +
+
+
+
+

친구 요청 상태 변경

+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ +
+

골 생성

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + + diff --git a/src/test/java/com/backend/blooming/admin/application/AdminPageServiceTest.java b/src/test/java/com/backend/blooming/admin/application/AdminPageServiceTest.java new file mode 100644 index 00000000..6b96a067 --- /dev/null +++ b/src/test/java/com/backend/blooming/admin/application/AdminPageServiceTest.java @@ -0,0 +1,53 @@ +package com.backend.blooming.admin.application; + +import com.backend.blooming.configuration.IsolateDatabase; +import com.backend.blooming.user.domain.User; +import com.backend.blooming.user.infrastructure.repository.UserRepository; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@IsolateDatabase +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class AdminPageServiceTest extends AdminPageServiceTestFixture { + + @Autowired + private AdminPageService adminPageService; + @Autowired + private UserRepository userRepository; + + @Test + void 사용자를_저장한다() { + // when + final Long actual = adminPageService.createUser(사용자_생성_요청); + + // then + final User user = userRepository.findById(actual).get(); + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).isPositive(); + softAssertions.assertThat(user.getName()).isEqualTo(사용자_생성_요청.name()); + softAssertions.assertThat(user.getEmail()).isEqualTo(사용자_생성_요청.email()); + softAssertions.assertThat(user.getColor().name()).isEqualTo(사용자_생성_요청.theme()); + softAssertions.assertThat(user.getStatusMessage()).isEqualTo(사용자_생성_요청.statusMessage()); + }); + } + + @Test + void 사용자_생성시_이메일이_없다면_기본_이메일로_저장한다() { + // when + final Long actual = adminPageService.createUser(이메일_없이_사용자_생성_요청); + + // then + final User user = userRepository.findById(actual).get(); + SoftAssertions.assertSoftly(softAssertions -> { + softAssertions.assertThat(actual).isPositive(); + softAssertions.assertThat(user.getName()).isEqualTo(사용자_생성_요청.name()); + softAssertions.assertThat(user.getEmail()).isEqualTo("test@email.com"); + softAssertions.assertThat(user.getColor().name()).isEqualTo(사용자_생성_요청.theme()); + softAssertions.assertThat(user.getStatusMessage()).isEqualTo(사용자_생성_요청.statusMessage()); + }); + } +} diff --git a/src/test/java/com/backend/blooming/admin/application/AdminPageServiceTestFixture.java b/src/test/java/com/backend/blooming/admin/application/AdminPageServiceTestFixture.java new file mode 100644 index 00000000..d56f7801 --- /dev/null +++ b/src/test/java/com/backend/blooming/admin/application/AdminPageServiceTestFixture.java @@ -0,0 +1,21 @@ +package com.backend.blooming.admin.application; + +import com.backend.blooming.admin.controller.dto.CreateUserRequest; +import com.backend.blooming.themecolor.domain.ThemeColor; + +@SuppressWarnings("NonAsciiCharacters") +public class AdminPageServiceTestFixture { + + protected CreateUserRequest 사용자_생성_요청 = new CreateUserRequest( + "사용자", + "test@email.com", + ThemeColor.INDIGO.name(), + "상태 메시지" + ); + protected CreateUserRequest 이메일_없이_사용자_생성_요청 = new CreateUserRequest( + "사용자", + null, + ThemeColor.INDIGO.name(), + "상태 메시지" + ); +} diff --git a/src/test/java/com/backend/blooming/admin/controller/AdminPageControllerTest.java b/src/test/java/com/backend/blooming/admin/controller/AdminPageControllerTest.java new file mode 100644 index 00000000..b426129a --- /dev/null +++ b/src/test/java/com/backend/blooming/admin/controller/AdminPageControllerTest.java @@ -0,0 +1,140 @@ +package com.backend.blooming.admin.controller; + +import com.backend.blooming.admin.application.AdminPageService; +import com.backend.blooming.admin.controller.dto.UpdateFriendRequest; +import com.backend.blooming.authentication.infrastructure.jwt.TokenProvider; +import com.backend.blooming.authentication.presentation.argumentresolver.AuthenticatedThreadLocal; +import com.backend.blooming.friend.application.FriendService; +import com.backend.blooming.friend.infrastructure.repository.FriendRepository; +import com.backend.blooming.goal.application.GoalService; +import com.backend.blooming.user.infrastructure.repository.UserRepository; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.stream.Stream; + +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +@WebMvcTest(AdminPageController.class) +@Import(AuthenticatedThreadLocal.class) +@MockBean({TokenProvider.class, UserRepository.class}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class AdminPageControllerTest extends AdminPageControllerTestFixture { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private AdminPageService adminPageService; + + @MockBean + private FriendService friendService; + + @MockBean + private FriendRepository friendRepository; + + @MockBean + private GoalService goalService; + + @Autowired + private ObjectMapper objectMapper; + + @Test + void 관리자_페이지를_로드한다() throws Exception { + // when & then + mockMvc.perform(get("/admin")) + .andExpectAll( + status().isOk(), + view().name("admin/test"), + model().attributeExists("themes", "users") + ); + } + + @Test + void 사용자를_생성한다() throws Exception { + // given + given(adminPageService.createUser(사용자_생성_요청)).willReturn(사용자_아아디); + + // when & then + mockMvc.perform(post("/admin/user") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(사용자_생성_요청)) + ).andExpect(status().isNoContent()); + } + + @Test + void 친구_신청한다() throws Exception { + // given + given(friendService.request(친구_요청_하는_사용자_아이디, 친구_요청_받는_사용자_아이디)).willReturn(친구_생성_아이디); + + // when & then + mockMvc.perform(post("/admin/friend") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(친구_요청)) + ).andExpect(status().isNoContent()); + } + + @ParameterizedTest + @MethodSource + void 친구_상태를_수정한다(final UpdateFriendRequest 친구_상태_수정_요청) throws Exception { + // given + given(friendRepository.findByRequestUserIdAndRequestedUserId(친구_요청_하는_사용자_아이디, 친구_요청_받는_사용자_아이디)) + .willReturn(친구_아이디); + + // when & then + mockMvc.perform(patch("/admin/friend") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(친구_상태_수정_요청)) + ).andExpect(status().isNoContent()); + } + + public static Stream 친구_상태를_수정한다() { + return Stream.of(친구_상태_친구로_수정_요청, 친구_상태_요청_취소로_수정_요청, 친구_상태_거절로_수정_요청); + } + + @Test + void 친구_상태_수정시_존재하지_않는_친구_사이라면_404_예외를_발생시킨다() throws Exception { + // given + given(friendRepository.findByRequestUserIdAndRequestedUserId(친구_요청_하는_사용자_아이디, 친구_요청_받는_사용자_아이디)) + .willReturn(null); + + // when & then + mockMvc.perform(patch("/admin/friend") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(친구_상태_친구로_수정_요청)) + ).andExpectAll( + status().isNotFound(), + jsonPath("$.message").exists() + ); + } + + @Test + void 골을_생성한다() throws Exception { + // given + given(goalService.createGoal(골_생성_요청_dto)).willReturn(골_아이디); + + // when & then + mockMvc.perform(post("/admin/goal") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(골_생성_요청)) + ).andExpect(status().isNoContent()); + } +} diff --git a/src/test/java/com/backend/blooming/admin/controller/AdminPageControllerTestFixture.java b/src/test/java/com/backend/blooming/admin/controller/AdminPageControllerTestFixture.java new file mode 100644 index 00000000..816e0969 --- /dev/null +++ b/src/test/java/com/backend/blooming/admin/controller/AdminPageControllerTestFixture.java @@ -0,0 +1,61 @@ +package com.backend.blooming.admin.controller; + +import com.backend.blooming.admin.controller.dto.CreateGoalRequest; +import com.backend.blooming.admin.controller.dto.CreateUserRequest; +import com.backend.blooming.admin.controller.dto.FriendStatus; +import com.backend.blooming.admin.controller.dto.RequestFriendRequest; +import com.backend.blooming.admin.controller.dto.UpdateFriendRequest; +import com.backend.blooming.goal.application.dto.CreateGoalDto; +import com.backend.blooming.themecolor.domain.ThemeColor; + +import java.time.LocalDate; +import java.util.List; + +@SuppressWarnings("NonAsciiCharacters") +public class AdminPageControllerTestFixture { + + protected CreateUserRequest 사용자_생성_요청 = new CreateUserRequest( + "사용자", + "test@email.com", + ThemeColor.INDIGO.name(), + "상태 메시지" + ); + protected Long 사용자_아아디 = 1L; + protected Long 친구_생성_아이디 = 1L; + protected static Long 친구_요청_하는_사용자_아이디 = 1L; + protected static Long 친구_요청_받는_사용자_아이디 = 2L; + protected RequestFriendRequest 친구_요청 = new RequestFriendRequest(친구_요청_하는_사용자_아이디, 친구_요청_받는_사용자_아이디); + protected static UpdateFriendRequest 친구_상태_친구로_수정_요청 = new UpdateFriendRequest( + 친구_요청_하는_사용자_아이디, + 친구_요청_받는_사용자_아이디, + FriendStatus.FRIEND.name() + ); + protected static UpdateFriendRequest 친구_상태_요청_취소로_수정_요청 = new UpdateFriendRequest( + 친구_요청_하는_사용자_아이디, + 친구_요청_받는_사용자_아이디, + FriendStatus.DELETE_BY_REQUEST.name() + ); + protected static UpdateFriendRequest 친구_상태_거절로_수정_요청 = new UpdateFriendRequest( + 친구_요청_하는_사용자_아이디, + 친구_요청_받는_사용자_아이디, + FriendStatus.DELETE_BY_REQUESTED.name() + ); + protected Long 친구_아이디 = 1L; + protected CreateGoalRequest 골_생성_요청 = new CreateGoalRequest( + "골 이름", + "메모", + LocalDate.now(), + LocalDate.now().plusDays(2), + 1L, + 2L + ); + protected CreateGoalDto 골_생성_요청_dto = new CreateGoalDto( + 골_생성_요청.name(), + 골_생성_요청.memo(), + 골_생성_요청.startDate(), + 골_생성_요청.endDate(), + 골_생성_요청.manager(), + List.of(골_생성_요청.manager(), 골_생성_요청.team()) + ); + protected Long 골_아이디 = 1L; +} diff --git a/src/test/java/com/backend/blooming/friend/application/FriendServiceTest.java b/src/test/java/com/backend/blooming/friend/application/FriendServiceTest.java index bc15e6b4..9be490ad 100644 --- a/src/test/java/com/backend/blooming/friend/application/FriendServiceTest.java +++ b/src/test/java/com/backend/blooming/friend/application/FriendServiceTest.java @@ -2,9 +2,9 @@ import com.backend.blooming.configuration.IsolateDatabase; import com.backend.blooming.friend.application.dto.ReadFriendsDto; -import com.backend.blooming.friend.application.exception.AlreadyRequestedFriendException; import com.backend.blooming.friend.application.exception.DeleteFriendForbiddenException; import com.backend.blooming.friend.application.exception.FriendAcceptanceForbiddenException; +import com.backend.blooming.friend.application.exception.FriendRequestNotAllowedException; import com.backend.blooming.friend.application.exception.NotFoundFriendRequestException; import com.backend.blooming.friend.domain.Friend; import com.backend.blooming.friend.infrastructure.repository.FriendRepository; @@ -76,18 +76,25 @@ class FriendServiceTest extends FriendServiceTestFixture { .isInstanceOf(NotFoundUserException.class); } + @Test + void 본인에게_친구_요청할_경우_예외가_발생한다() { + // when & than + assertThatThrownBy(() -> friendService.request(사용자_아이디, 사용자_아이디)) + .isInstanceOf(FriendRequestNotAllowedException.SelfRequestNotAllowedException.class); + } + @Test void 이미_친구_요청을_보낸_사용자에게_다시_친구_요청할_경우_예외가_발생한다() { // when & then assertThatThrownBy(() -> friendService.request(사용자_아이디, 이미_친구_요청을_받은_사용자_아이디)) - .isInstanceOf(AlreadyRequestedFriendException.class); + .isInstanceOf(FriendRequestNotAllowedException.AlreadyRequestedFriendException.class); } @Test void 이미_친구_요청이_온_사용자에게_친구_요청할_경우_예외가_발생한다() { // when & then assertThatThrownBy(() -> friendService.request(이미_친구_요청을_받은_사용자_아이디, 사용자_아이디)) - .isInstanceOf(AlreadyRequestedFriendException.class); + .isInstanceOf(FriendRequestNotAllowedException.AlreadyRequestedFriendException.class); } @Test diff --git a/src/test/java/com/backend/blooming/friend/infrastructure/repository/FriendRepositoryTest.java b/src/test/java/com/backend/blooming/friend/infrastructure/repository/FriendRepositoryTest.java index d18db96f..bd7e4a5b 100644 --- a/src/test/java/com/backend/blooming/friend/infrastructure/repository/FriendRepositoryTest.java +++ b/src/test/java/com/backend/blooming/friend/infrastructure/repository/FriendRepositoryTest.java @@ -95,6 +95,18 @@ class FriendRepositoryTest extends FriendRepositoryTestFixture { }); } + @Test + void 친구_요청자와_친구_요청_수신자_아이디에_대한_친구_아이디를_반환한다() { + // when + final Long actual = friendRepository.findByRequestUserIdAndRequestedUserId( + 친구가_있는_사용자.getId(), + 친구인_사용자1.getId() + ); + + // then + assertThat(actual).isEqualTo(사용자와_친구요청1의_친구_아이디); + } + @Test void 입력받은_아이디_목록_중_현재_로그인한_사용자와_서로_친구_관계인_사용자_수를_반환한다() { // when diff --git a/src/test/java/com/backend/blooming/friend/infrastructure/repository/FriendRepositoryTestFixture.java b/src/test/java/com/backend/blooming/friend/infrastructure/repository/FriendRepositoryTestFixture.java index 1d3c845f..63212dfb 100644 --- a/src/test/java/com/backend/blooming/friend/infrastructure/repository/FriendRepositoryTestFixture.java +++ b/src/test/java/com/backend/blooming/friend/infrastructure/repository/FriendRepositoryTestFixture.java @@ -26,6 +26,7 @@ public class FriendRepositoryTestFixture { protected User 친구_요청을_받은_사용자; protected User 친구_요청을_받은적_없는_사용자; protected User 친구가_있는_사용자; + protected User 친구인_사용자1; protected Friend 보낸_친구_요청1; protected Friend 보낸_친구_요청2; protected Friend 보낸_친구_요청3; @@ -39,6 +40,7 @@ public class FriendRepositoryTestFixture { protected List 골_초대받은_사용자_아이디_목록 = new ArrayList<>(); protected Long 친구인_사용자_아이디; protected Long 친구가_아닌_사용자_아이디; + protected Long 사용자와_친구요청1의_친구_아이디; @BeforeEach void setUpFixture() { @@ -72,7 +74,7 @@ void setUpFixture() { .name(new Name("사용자5")) .email(new Email("user5@email.com")) .build(); - final User 친구인_사용자1 = User.builder() + 친구인_사용자1 = User.builder() .oAuthId("23456") .oAuthType(OAuthType.KAKAO) .name(new Name("친구1")) @@ -111,9 +113,9 @@ void setUpFixture() { 받은_친구_요청3 = new Friend(친구_요청을_받은_사용자3, 친구_요청을_받은_사용자); 친구가_있는_사용자 = 친구_요청을_받은_사용자3; - 친구인_요청1 = new Friend(친구_요청을_받은_사용자3, 친구인_사용자1); - 친구인_요청2 = new Friend(친구인_사용자2, 친구_요청을_받은_사용자3); - 친구인_요청3 = new Friend(친구_요청을_받은_사용자3, 친구인_사용자3); + 친구인_요청1 = new Friend(친구가_있는_사용자, 친구인_사용자1); + 친구인_요청2 = new Friend(친구인_사용자2, 친구가_있는_사용자); + 친구인_요청3 = new Friend(친구가_있는_사용자, 친구인_사용자3); final Friend 현재_로그인한_사용자와_친구인_상태 = new Friend(친구인_사용자1, 친구인_사용자2); final Friend 현재_로그인한_사용자와_친구인_상태2 = new Friend(친구인_사용자1, 친구인_사용자3); @@ -129,6 +131,7 @@ void setUpFixture() { 현재_로그인한_사용자와_친구인_상태, 현재_로그인한_사용자와_친구인_상태2 )); + 사용자와_친구요청1의_친구_아이디 = 친구인_요청1.getId(); 친구인_요청1.acceptRequest(); 친구인_요청2.acceptRequest(); diff --git a/src/test/java/com/backend/blooming/friend/presentation/FriendControllerTest.java b/src/test/java/com/backend/blooming/friend/presentation/FriendControllerTest.java index 35862ec8..68d975ca 100644 --- a/src/test/java/com/backend/blooming/friend/presentation/FriendControllerTest.java +++ b/src/test/java/com/backend/blooming/friend/presentation/FriendControllerTest.java @@ -5,9 +5,9 @@ import com.backend.blooming.common.RestDocsConfiguration; import com.backend.blooming.friend.application.FriendService; import com.backend.blooming.friend.application.dto.FriendType; -import com.backend.blooming.friend.application.exception.AlreadyRequestedFriendException; import com.backend.blooming.friend.application.exception.DeleteFriendForbiddenException; import com.backend.blooming.friend.application.exception.FriendAcceptanceForbiddenException; +import com.backend.blooming.friend.application.exception.FriendRequestNotAllowedException; import com.backend.blooming.friend.application.exception.NotFoundFriendRequestException; import com.backend.blooming.user.application.exception.NotFoundUserException; import com.backend.blooming.user.infrastructure.repository.UserRepository; @@ -105,12 +105,31 @@ class FriendControllerTest extends FriendControllerTestFixture { ).andDo(print()); } + @Test + void 본인에게_친구_요청시_400_예외를_발생시킨다() throws Exception { + // given + given(tokenProvider.parseToken(액세스_토큰_타입, 액세스_토큰)).willReturn(사용자_토큰_정보); + given(userRepository.existsByIdAndDeletedIsFalse(사용자_아이디)).willReturn(true); + given(friendService.request(사용자_아이디, 사용자_아이디)) + .willThrow(new FriendRequestNotAllowedException.SelfRequestNotAllowedException()); + + // when & then + mockMvc.perform(post("/friends/{requestedUserId}", 사용자_아이디) + .header("X-API-VERSION", 1) + .header(HttpHeaders.AUTHORIZATION, 액세스_토큰) + ).andExpectAll( + status().isBadRequest(), + jsonPath("$.message").exists() + ).andDo(print()); + } + @Test void 이미_친구인_사용자에게_친구_요청시_400_예외를_발생시킨다() throws Exception { // given given(tokenProvider.parseToken(액세스_토큰_타입, 액세스_토큰)).willReturn(사용자_토큰_정보); given(userRepository.existsByIdAndDeletedIsFalse(사용자_아이디)).willReturn(true); - given(friendService.request(사용자_아이디, 이미_친구인_사용자_아이디)).willThrow(new AlreadyRequestedFriendException()); + given(friendService.request(사용자_아이디, 이미_친구인_사용자_아이디)) + .willThrow(new FriendRequestNotAllowedException.AlreadyRequestedFriendException()); // when & then mockMvc.perform(post("/friends/{requestedUserId}", 이미_친구인_사용자_아이디) diff --git a/src/test/java/com/backend/blooming/user/presentation/UserControllerTest.java b/src/test/java/com/backend/blooming/user/presentation/UserControllerTest.java index c402b9dc..2de2d8dc 100644 --- a/src/test/java/com/backend/blooming/user/presentation/UserControllerTest.java +++ b/src/test/java/com/backend/blooming/user/presentation/UserControllerTest.java @@ -171,8 +171,7 @@ class UserControllerTest extends UserControllerTestFixture { fieldWithPath("users.[].name").type(JsonFieldType.STRING).description("사용자 이름"), fieldWithPath("users.[].color").type(JsonFieldType.STRING).description("사용자 테마 색상 코드"), fieldWithPath("users.[].statusMessage").type(JsonFieldType.STRING).description("사용자 상태 메시지"), - fieldWithPath("users.[].friendsStatus").type(JsonFieldType.STRING) - .description("로그인한 사용자와의 친구 상태") + fieldWithPath("users.[].friendsStatus").type(JsonFieldType.STRING).description("로그인한 사용자와의 친구 상태") ) )); }