diff --git a/splanet-test.log b/splanet-test.log index ee5e266f..39556ee3 100644 --- a/splanet-test.log +++ b/splanet-test.log @@ -11,3 +11,98 @@ eventType: API_REQUEST, userId: 2, deviceId: apiTestDeviceId, timestamp: 2024-11 eventType: LOGIN_SUCCESS, userId: 1, deviceId: testDeviceId, timestamp: 2024-11-12T01:09:31.692107, requestPath: /test/login, headers: User-Agent: TestAgent, Accept: */* eventType: API_REQUEST, userId: 2, deviceId: apiTestDeviceId, timestamp: 2024-11-12T01:31:28.960904, requestPath: /test/api, headers: User-Agent: ApiTestAgent, Accept: application/json eventType: LOGIN_SUCCESS, userId: 1, deviceId: testDeviceId, timestamp: 2024-11-12T01:31:28.972599, requestPath: /test/login, headers: User-Agent: TestAgent, Accept: */* +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:49.3028597, requestPath: /api/comments/999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:49.5203791, requestPath: /api/comments/999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:49.772894, requestPath: /api/comments, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:49.8690521, requestPath: /api/comments/1886, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:49.933985, requestPath: /api/comments/999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:49.9962563, requestPath: /api/comments/409, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:50.0760239, requestPath: /api/comments/1892, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:50.250223, requestPath: /api/comments/412, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:50.3120036, requestPath: /api/comments, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:50.3747438, requestPath: /api/comments/414, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:52.1246169, requestPath: /api/friends/999/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:52.28604, requestPath: /api/friends/999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:52.4156765, requestPath: /api/friends/1/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:52.6244195, requestPath: /api/friends/1904, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:52.8433793, requestPath: /api/friends, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:52.9487333, requestPath: /api/friends/1, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:53.0457083, requestPath: /api/friends, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:53.9521541, requestPath: /api/friends/requests, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:54.0340107, requestPath: /api/friends/requests/999/cancel, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:54.218513, requestPath: /api/friends/requests/158/accept, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:54.3053053, requestPath: /api/friends/requests, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:54.4208858, requestPath: /api/friends/requests/159/cancel, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:54.4540264, requestPath: /api/friends/requests/sent, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:54.5844674, requestPath: /api/friends/requests/160/cancel, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:54.6998971, requestPath: /api/friends/requests/161/reject, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:54.8226564, requestPath: /api/friends/requests, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:54.9297461, requestPath: /api/friends/requests/received, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:50:55.0453017, requestPath: /api/friends/requests/sent, headers: +eventType: API_REQUEST, userId: 2, deviceId: apiTestDeviceId, timestamp: 2024-11-12T01:51:11.4133233, requestPath: /test/api, headers: User-Agent: ApiTestAgent, Accept: application/json +eventType: LOGIN_SUCCESS, userId: 1, deviceId: testDeviceId, timestamp: 2024-11-12T01:51:11.4389194, requestPath: /test/login, headers: User-Agent: TestAgent, Accept: */* +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:12.1399193, requestPath: /api/payment/999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:12.2281242, requestPath: /api/payment/57, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:12.3120922, requestPath: /api/payment/999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:12.381132, requestPath: /api/payment/59, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:12.847827, requestPath: /api/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:12.9241445, requestPath: /api/plans/3, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:12.9484284, requestPath: /api/plans/3, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.008211, requestPath: /api/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.0400897, requestPath: /api/plans/4, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.0967577, requestPath: /api/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.1300244, requestPath: /api/plans/5, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.1877451, requestPath: /api/plans/9999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.2559353, requestPath: /api/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.3151272, requestPath: /api/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.4043286, requestPath: /api/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.4805706, requestPath: /api/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.5505545, requestPath: /api/plans/8, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.6053511, requestPath: /api/plans/9999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.6707081, requestPath: /api/plans/9, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.7459455, requestPath: /api/plans/10, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.8041065, requestPath: /api/plans/9999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.8724285, requestPath: /api/plans/9999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:13.9503133, requestPath: /api/plans/11, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:14.0083136, requestPath: /api/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:14.0726492, requestPath: /api/plans/12, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:14.1280526, requestPath: /api/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:14.9823068, requestPath: /api/subscription/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:15.0881529, requestPath: /api/subscription/me/subscribe, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:15.1681591, requestPath: /api/subscription/me/subscribe, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:15.2711891, requestPath: /api/subscriptions/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:15.4890487, requestPath: /api/teams/168/invite, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:15.6581786, requestPath: /api/teams/169/invite, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:16.3431456, requestPath: /api/teams/170/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:16.4168068, requestPath: /api/teams/170/plans/56, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:16.4852017, requestPath: /api/teams/171/plans/999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:16.5630507, requestPath: /api/teams/172/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:16.6365126, requestPath: /api/teams/173/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:16.7654453, requestPath: /api/teams/174/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:16.8461776, requestPath: /api/teams/175/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:16.8868654, requestPath: /api/teams/175/plans/58, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:16.9186508, requestPath: /api/teams/175/plans/58, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:16.984638, requestPath: /api/teams/176/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:17.0113651, requestPath: /api/teams/176/plans/59, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:17.0769448, requestPath: /api/teams/177/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:17.1066412, requestPath: /api/teams/177/plans/60, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:17.1805787, requestPath: /api/teams/178/plans/999, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:17.2588973, requestPath: /api/teams/179/plans, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:17.2859849, requestPath: /api/teams/179/plans/61, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.0306386, requestPath: /api/users/create, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.0792517, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.1027003, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.1498642, requestPath: /api/users/create, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.1789634, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.1974601, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.2124668, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.2585632, requestPath: /api/users/create, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.2998102, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.3729508, requestPath: /api/users/create, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.5490552, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.6035407, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.6598154, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.7129503, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.7684137, requestPath: /api/users/me, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.8315305, requestPath: /api/users/create, headers: +eventType: API_REQUEST, userId: null, deviceId: null, timestamp: 2024-11-12T01:51:18.9064907, requestPath: /api/users/me, headers: diff --git a/src/main/java/com/splanet/splanet/comment/controller/CommentApi.java b/src/main/java/com/splanet/splanet/comment/controller/CommentApi.java index 8926248d..fbe8ecef 100644 --- a/src/main/java/com/splanet/splanet/comment/controller/CommentApi.java +++ b/src/main/java/com/splanet/splanet/comment/controller/CommentApi.java @@ -44,8 +44,7 @@ ResponseEntity> getComments( content = @Content(schema = @Schema(implementation = ErrorResponse.class))) }) ResponseEntity createComment( - @Parameter(description = "작성자(인증된 유저)의 ID", required = true) @AuthenticationPrincipal Long writerId, - @Parameter(description = "대상 유저의 ID", required = true) @RequestParam Long userId, + @Parameter(description = "인증된 유저의 ID", required = true) @AuthenticationPrincipal Long userId, @Parameter(description = "댓글 작성 요청 정보", required = true) @RequestBody CommentRequest request); @PutMapping("/{commentId}") diff --git a/src/main/java/com/splanet/splanet/comment/controller/CommentController.java b/src/main/java/com/splanet/splanet/comment/controller/CommentController.java index c850fae4..ca807ded 100644 --- a/src/main/java/com/splanet/splanet/comment/controller/CommentController.java +++ b/src/main/java/com/splanet/splanet/comment/controller/CommentController.java @@ -22,8 +22,8 @@ public ResponseEntity> getComments(Long userId, Long userI } @Override - public ResponseEntity createComment(Long writerId, Long userId, CommentRequest request) { - commentService.createComment(writerId, userId, request); + public ResponseEntity createComment(Long userId, CommentRequest request) { + commentService.createComment(userId, request); return ResponseEntity.ok("댓글이 성공적으로 작성되었습니다."); } diff --git a/src/main/java/com/splanet/splanet/comment/dto/CommentRequest.java b/src/main/java/com/splanet/splanet/comment/dto/CommentRequest.java index df2afed8..0cfe04cd 100644 --- a/src/main/java/com/splanet/splanet/comment/dto/CommentRequest.java +++ b/src/main/java/com/splanet/splanet/comment/dto/CommentRequest.java @@ -1,4 +1,4 @@ package com.splanet.splanet.comment.dto; -public record CommentRequest(String content) { +public record CommentRequest(Long userId, String content) { } \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/comment/service/CommentService.java b/src/main/java/com/splanet/splanet/comment/service/CommentService.java index cab73be0..ef315d45 100644 --- a/src/main/java/com/splanet/splanet/comment/service/CommentService.java +++ b/src/main/java/com/splanet/splanet/comment/service/CommentService.java @@ -31,16 +31,12 @@ public List getComments(Long userId) { // 댓글 작성 @Transactional - public void createComment(Long writerId, Long userId, CommentRequest request) { - User user = userRepository.findById(userId) + public void createComment(Long userId, CommentRequest request) { + User user = userRepository.findById(request.userId()) .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); - User writer = userRepository.findById(writerId) + User writer = userRepository.findById(userId) .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); - if (user.getId().equals(writer.getId())) { - throw new BusinessException(ErrorCode.INVALID_COMMENT_ID); - } - Comment comment = Comment.builder() .user(user) .writer(writer) diff --git a/src/main/java/com/splanet/splanet/core/exception/ErrorCode.java b/src/main/java/com/splanet/splanet/core/exception/ErrorCode.java index e02c06f5..145a5913 100644 --- a/src/main/java/com/splanet/splanet/core/exception/ErrorCode.java +++ b/src/main/java/com/splanet/splanet/core/exception/ErrorCode.java @@ -39,6 +39,7 @@ public enum ErrorCode { INVITATION_ALREADY_PROCESSED("초대가 이미 처리되었습니다.", HttpStatus.BAD_REQUEST), INVITATION_ALREADY_SENT("초대가 이미 존재합니다.", HttpStatus.BAD_REQUEST), USER_ALREADY_IN_TEAM("해당 유저는 이미 팀에 속해 있습니다.", HttpStatus.BAD_REQUEST), + TEAM_NAME_NOT_FOUND("팀이름이 비어 있습니다.", HttpStatus.NOT_FOUND), // friend FRIEND_NOT_FOUND("친구가 아닙니다.",HttpStatus.NOT_FOUND), diff --git a/src/main/java/com/splanet/splanet/friend/service/FriendService.java b/src/main/java/com/splanet/splanet/friend/service/FriendService.java index 93d6891d..cfcbe7c3 100644 --- a/src/main/java/com/splanet/splanet/friend/service/FriendService.java +++ b/src/main/java/com/splanet/splanet/friend/service/FriendService.java @@ -84,15 +84,23 @@ public ResponseEntity> getFriendPlan(Long friendId, Long u return ResponseEntity.ok(planResponseDtos); } + // 친구 삭제(취소)하기 + // 친구 삭제(취소)하기 @Transactional public ResponseEntity> unfriend(Long friendId, Long userId) { + // 내 친구 목록에 존재하는지 if (!friendRepository.existsByUserIdAndFriendId(userId, friendId)) { throw new BusinessException(ErrorCode.FRIEND_NOT_FOUND); } + // 내가 내 친구 목록에서 친구 삭제 friendRepository.deleteByRequesterIdAndReceiverId(userId, friendId); + // 친구의 친구 목록에서도 나를 삭제 + friendRepository.deleteByRequesterIdAndReceiverId(friendId, userId); + + // 그 친구 관련 친구 요청도 있으면, 삭제 List pendingRequests = friendRequestRepository.findPendingRequestsByReceiverId(userId, friendId, FriendRequest.Status.PENDING); for (FriendRequest request : pendingRequests) { friendRequestRepository.delete(request); diff --git a/src/main/java/com/splanet/splanet/subscription/controller/SubscriptionApi.java b/src/main/java/com/splanet/splanet/subscription/controller/SubscriptionApi.java index fd28cec1..1ae30b9a 100644 --- a/src/main/java/com/splanet/splanet/subscription/controller/SubscriptionApi.java +++ b/src/main/java/com/splanet/splanet/subscription/controller/SubscriptionApi.java @@ -43,7 +43,7 @@ ResponseEntity getSubscription( ResponseEntity cancelSubscription( @Parameter(description = "JWT 인증으로 전달된 사용자 ID", required = true) @AuthenticationPrincipal Long userId); - @PostMapping("/payment") + @PostMapping("/subscribe") @Operation(summary = "구독", description = "사용자가 구독을 구매합니다.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "구독이 성공적으로 구매되었습니다.", diff --git a/src/main/java/com/splanet/splanet/team/controller/TeamApi.java b/src/main/java/com/splanet/splanet/team/controller/TeamApi.java index 6eec3798..a7eaa2e0 100644 --- a/src/main/java/com/splanet/splanet/team/controller/TeamApi.java +++ b/src/main/java/com/splanet/splanet/team/controller/TeamApi.java @@ -1,4 +1,4 @@ -package com.splanet.splanet.team.api; +package com.splanet.splanet.team.controller; import com.splanet.splanet.team.dto.TeamDto; import com.splanet.splanet.team.dto.TeamInvitationDto; diff --git a/src/main/java/com/splanet/splanet/team/controller/TeamController.java b/src/main/java/com/splanet/splanet/team/controller/TeamController.java index 2d598df9..8d052670 100644 --- a/src/main/java/com/splanet/splanet/team/controller/TeamController.java +++ b/src/main/java/com/splanet/splanet/team/controller/TeamController.java @@ -1,6 +1,6 @@ package com.splanet.splanet.team.controller; -import com.splanet.splanet.team.api.TeamApi; +import com.splanet.splanet.team.controller.TeamApi; import com.splanet.splanet.team.dto.TeamDto; import com.splanet.splanet.team.dto.TeamInvitationDto; import com.splanet.splanet.team.dto.TeamMemberDto; diff --git a/src/main/java/com/splanet/splanet/team/repository/TeamRepository.java b/src/main/java/com/splanet/splanet/team/repository/TeamRepository.java index f67f87c8..30611ac4 100644 --- a/src/main/java/com/splanet/splanet/team/repository/TeamRepository.java +++ b/src/main/java/com/splanet/splanet/team/repository/TeamRepository.java @@ -3,5 +3,8 @@ import com.splanet.splanet.team.entity.Team; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface TeamRepository extends JpaRepository { -} + Optional findByTeamName(String teamName); +} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/team/service/TeamService.java b/src/main/java/com/splanet/splanet/team/service/TeamService.java index 6cf77769..3c432c52 100644 --- a/src/main/java/com/splanet/splanet/team/service/TeamService.java +++ b/src/main/java/com/splanet/splanet/team/service/TeamService.java @@ -39,7 +39,7 @@ public TeamService(TeamRepository teamRepository, UserRepository userRepository, @Transactional public TeamDto createTeam(String teamName, Long userId) { if (teamName == null || teamName.isBlank()) { - throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE); + throw new BusinessException(ErrorCode.TEAM_NAME_NOT_FOUND); } User user = findUserById(userId); diff --git a/src/main/java/com/splanet/splanet/teamplan/controller/TeamPlanController.java b/src/main/java/com/splanet/splanet/teamplan/controller/TeamPlanController.java index 69db352a..2857565b 100644 --- a/src/main/java/com/splanet/splanet/teamplan/controller/TeamPlanController.java +++ b/src/main/java/com/splanet/splanet/teamplan/controller/TeamPlanController.java @@ -17,7 +17,7 @@ public class TeamPlanController implements TeamPlanApi{ private final TeamPlanService teamPlanService; // 팀 플랜 생성 - @PostMapping + @Override public ResponseEntity createTeamPlan( @AuthenticationPrincipal Long userId, @PathVariable Long teamId, @@ -27,7 +27,7 @@ public ResponseEntity createTeamPlan( } // 팀 플랜 조회 - @GetMapping("/{planId}") + @Override public ResponseEntity getTeamPlan( @PathVariable Long teamId, @PathVariable Long planId) { @@ -36,14 +36,14 @@ public ResponseEntity getTeamPlan( } // 팀의 모든 플랜 조회 - @GetMapping + @Override public ResponseEntity> getAllTeamPlans(@PathVariable Long teamId) { List plans = teamPlanService.getAllTeamPlans(teamId); return ResponseEntity.ok(plans); } // 팀 플랜 수정 - @PutMapping("/{planId}") + @Override public ResponseEntity updateTeamPlan( @AuthenticationPrincipal Long userId, @PathVariable Long teamId, @@ -54,7 +54,7 @@ public ResponseEntity updateTeamPlan( } // 팀 플랜 삭제 - @DeleteMapping("/{planId}") + @Override public ResponseEntity deleteTeamPlan( @AuthenticationPrincipal Long userId, @PathVariable Long teamId, diff --git a/src/test/java/com/splanet/splanet/comment/controller/CommentControllerIntegrationTest.java b/src/test/java/com/splanet/splanet/comment/controller/CommentControllerIntegrationTest.java index 6cf98a7e..268e149c 100644 --- a/src/test/java/com/splanet/splanet/comment/controller/CommentControllerIntegrationTest.java +++ b/src/test/java/com/splanet/splanet/comment/controller/CommentControllerIntegrationTest.java @@ -7,7 +7,6 @@ import com.splanet.splanet.comment.repository.CommentRepository; import com.splanet.splanet.comment.service.CommentService; import com.splanet.splanet.core.exception.BusinessException; -import com.splanet.splanet.core.exception.ErrorCode; import com.splanet.splanet.jwt.JwtTokenProvider; import com.splanet.splanet.user.entity.User; import com.splanet.splanet.user.repository.UserRepository; @@ -17,6 +16,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import org.springframework.transaction.annotation.Transactional; @@ -102,14 +102,41 @@ void setUp() { assertThat(comments.get(0).content()).isEqualTo("testUser의 댓글"); } + @Test + void 댓글_조회_특정_유저() throws Exception { + // testUser가 작성한 댓글 추가 + Comment comment = Comment.builder() + .user(testUser) + .writer(writerUser) + .content("testUser의 댓글") + .build(); + commentRepository.save(comment); + + // testUser의 댓글만 조회하는 API 요청 + mockMvc.perform(get("/api/comments/{userId}", testUser.getId()) + .header("Authorization", token)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(1)) + .andExpect(jsonPath("$[0].content").value("testUser의 댓글")); + } + + @Test + void 댓글_조회_존재하지_않는_댓글() throws Exception { + Long nonExistentCommentId = 999L; + + mockMvc.perform(get("/api/comments/{id}", nonExistentCommentId) + .header("Authorization", token)) + .andExpect(status().isOk()) + .andExpect(content().json("[]")); + } + @Test void 댓글_작성_성공() throws Exception { - CommentRequest commentRequest = new CommentRequest("새로운 댓글 내용"); + // CommentRequest에서 userId와 content만 전달하도록 수정 + CommentRequest commentRequest = new CommentRequest(testUser.getId(), "새로운 댓글 내용"); mockMvc.perform(post("/api/comments") .header("Authorization", token) - .param("writerId", String.valueOf(writerUser.getId())) - .param("userId", String.valueOf(testUser.getId())) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(commentRequest))) .andExpect(status().isOk()) @@ -121,13 +148,28 @@ void setUp() { assertThat(comments.get(0).getContent()).isEqualTo("새로운 댓글 내용"); } + @Test + void 댓글_작성_실패_존재하지않은_사용자ID() throws Exception { + // 존재하지 않는 userId로 댓글 작성 요청 + Long invalidUserId = 999L; + CommentRequest commentRequest = new CommentRequest(invalidUserId, "존재하지않는 userId로 댓글 작성 시도"); + + mockMvc.perform(post("/api/comments") + .header("Authorization", token) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(commentRequest))) + .andExpect(status().isNotFound()) + .andExpect(result -> assertThat(result.getResolvedException()) + .isInstanceOf(BusinessException.class) + .hasMessageContaining("유저가 존재하지 않습니다.")); + } + @Test void 댓글_수정_성공() throws Exception { - CommentRequest updatedRequest = new CommentRequest("수정된 댓글 내용"); + CommentRequest updatedRequest = new CommentRequest(testUser.getId(), "수정된 댓글 내용"); mockMvc.perform(put("/api/comments/" + testComment.getId()) .header("Authorization", token) - .param("userId", String.valueOf(writerUser.getId())) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updatedRequest))) .andExpect(status().isOk()) @@ -138,27 +180,14 @@ void setUp() { assertThat(updatedComment.getContent()).isEqualTo("수정된 댓글 내용"); } - @Test - void 댓글_삭제_성공() throws Exception { - mockMvc.perform(delete("/api/comments/" + testComment.getId()) - .header("Authorization", token) - .param("userId", String.valueOf(writerUser.getId()))) - .andExpect(status().isOk()) - .andExpect(content().string("댓글이 성공적으로 삭제되었습니다.")); - - // 댓글이 실제로 삭제되었는지 확인 - assertThat(commentRepository.findById(testComment.getId())).isEmpty(); - } - @Test void 댓글_수정_실패_작성자가_아님() throws Exception { - CommentRequest updatedRequest = new CommentRequest("권한 없는 사용자의 수정 시도"); + CommentRequest updatedRequest = new CommentRequest(testUser.getId(), "권한 없는 사용자의 수정 시도"); String unauthorizedToken = "Bearer " + jwtTokenProvider.createAccessToken(testUser.getId()); mockMvc.perform(put("/api/comments/" + testComment.getId()) .header("Authorization", unauthorizedToken) - .param("userId", String.valueOf(testUser.getId())) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updatedRequest))) .andExpect(status().isForbidden()) @@ -169,14 +198,13 @@ void setUp() { @Test void 댓글_수정_실패_존재하지_않는_댓글() throws Exception { - CommentRequest updatedRequest = new CommentRequest("수정된 댓글 내용"); + CommentRequest updatedRequest = new CommentRequest(testUser.getId(), "수정된 댓글 내용"); // 존재하지 않는 댓글 ID로 수정 요청 Long nonExistentCommentId = 999L; mockMvc.perform(put("/api/comments/" + nonExistentCommentId) .header("Authorization", token) - .param("userId", String.valueOf(writerUser.getId())) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(updatedRequest))) .andExpect(status().isNotFound()) @@ -185,14 +213,24 @@ void setUp() { .hasMessageContaining("댓글을 찾을 수 없습니다.")); } + @Test + void 댓글_삭제_성공() throws Exception { + mockMvc.perform(delete("/api/comments/" + testComment.getId()) + .header("Authorization", token)) + .andExpect(status().isOk()) + .andExpect(content().string("댓글이 성공적으로 삭제되었습니다.")); + + // 댓글이 실제로 삭제되었는지 확인 + assertThat(commentRepository.findById(testComment.getId())).isEmpty(); + } + @Test void 댓글_삭제_실패_존재하지_않는_댓글() throws Exception { // 존재하지 않는 댓글 ID로 삭제 요청 Long nonExistentCommentId = 999L; mockMvc.perform(delete("/api/comments/" + nonExistentCommentId) - .header("Authorization", token) - .param("userId", String.valueOf(writerUser.getId()))) + .header("Authorization", token)) .andExpect(status().isNotFound()) .andExpect(result -> assertThat(result.getResolvedException()) .isInstanceOf(BusinessException.class) diff --git a/src/test/java/com/splanet/splanet/comment/service/CommentServiceTest.java b/src/test/java/com/splanet/splanet/comment/service/CommentServiceTest.java index b1b79c91..3923f0e0 100644 --- a/src/test/java/com/splanet/splanet/comment/service/CommentServiceTest.java +++ b/src/test/java/com/splanet/splanet/comment/service/CommentServiceTest.java @@ -5,7 +5,6 @@ import com.splanet.splanet.comment.entity.Comment; import com.splanet.splanet.comment.repository.CommentRepository; import com.splanet.splanet.core.exception.BusinessException; -import com.splanet.splanet.core.exception.ErrorCode; import com.splanet.splanet.user.entity.User; import com.splanet.splanet.user.repository.UserRepository; import org.junit.jupiter.api.BeforeEach; @@ -45,7 +44,7 @@ void setUp() { Comment comment = Comment.builder() .id(1L) .writer(user) - .content("테스트 댓글") + .content("test content") .user(user) .build(); when(commentRepository.findByUserId(anyLong())).thenReturn(List.of(comment)); @@ -55,7 +54,17 @@ void setUp() { // then assertEquals(1, responses.size()); - assertEquals("테스트 댓글", responses.get(0).content()); + assertEquals("test content", responses.get(0).content()); + } + + @Test + void 댓글조회_사용자없음() { + // given + when(commentRepository.findByUserId(anyLong())).thenReturn(List.of()); + + // when & then + List responses = commentService.getComments(99L); + assertEquals(0, responses.size()); } @Test @@ -63,51 +72,49 @@ void setUp() { // given User user = User.builder().id(1L).build(); User writer = User.builder().id(2L).build(); - CommentRequest request = new CommentRequest("테스트 댓글 내용"); + CommentRequest request = new CommentRequest(1L, "test content"); when(userRepository.findById(1L)).thenReturn(Optional.of(user)); when(userRepository.findById(2L)).thenReturn(Optional.of(writer)); when(commentRepository.save(any(Comment.class))).thenReturn(Comment.builder().id(1L).build()); // when - commentService.createComment(2L, 1L, request); + commentService.createComment(2L, request); // then verify(commentRepository, times(1)).save(any(Comment.class)); } @Test - void 댓글작성_본인에게댓글달때_실패() { + void 댓글작성_사용자없음() { // given - User user = User.builder().id(1L).build(); - CommentRequest request = new CommentRequest("자기 자신에게 댓글을 달려고 합니다."); - when(userRepository.findById(1L)).thenReturn(Optional.of(user)); + CommentRequest request = new CommentRequest(1L, "test content"); + when(userRepository.findById(1L)).thenReturn(Optional.empty()); // when & then - BusinessException exception = assertThrows(BusinessException.class, () -> commentService.createComment(1L, 1L, request)); - assertEquals(ErrorCode.INVALID_COMMENT_ID, exception.getErrorCode()); + assertThrows(BusinessException.class, () -> commentService.createComment(2L, request)); } @Test void 댓글수정성공() { // given User writer = User.builder().id(2L).build(); - Comment comment = Comment.builder().id(1L).writer(writer).content("기존 내용").build(); - CommentRequest request = new CommentRequest("새로운 내용"); + Comment comment = Comment.builder().id(1L).writer(writer).content("old content").build(); + CommentRequest request = new CommentRequest(1L, "new content"); when(commentRepository.findById(1L)).thenReturn(Optional.of(comment)); // when commentService.updateComment(1L, request, 2L); // then - assertEquals("새로운 내용", comment.getContent()); + assertEquals("new content", comment.getContent()); } @Test - void 댓글수정_작성자불일치_실패() { + void 댓글수정_작성자불일치() { // given User writer = User.builder().id(2L).build(); - Comment comment = Comment.builder().id(1L).writer(writer).content("기존 내용").build(); - CommentRequest request = new CommentRequest("새로운 내용"); + Comment comment = Comment.builder().id(1L).writer(writer).content("old content").build(); + CommentRequest request = new CommentRequest(1L, "new content"); when(commentRepository.findById(1L)).thenReturn(Optional.of(comment)); // when & then @@ -129,7 +136,7 @@ void setUp() { } @Test - void 댓글삭제_작성자불일치_실패() { + void 댓글삭제_작성자불일치() { // given User writer = User.builder().id(2L).build(); Comment comment = Comment.builder().id(1L).writer(writer).build(); diff --git a/src/test/java/com/splanet/splanet/friend/controller/FriendControllerIntegrationTest.java b/src/test/java/com/splanet/splanet/friend/controller/FriendControllerIntegrationTest.java new file mode 100644 index 00000000..efa981ce --- /dev/null +++ b/src/test/java/com/splanet/splanet/friend/controller/FriendControllerIntegrationTest.java @@ -0,0 +1,184 @@ +package com.splanet.splanet.friend.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.splanet.splanet.friend.dto.FriendResponse; +import com.splanet.splanet.friend.entity.Friend; +import com.splanet.splanet.friend.repository.FriendRepository; +import com.splanet.splanet.friend.service.FriendService; +import com.splanet.splanet.friendRequest.repository.FriendRequestRepository; +import com.splanet.splanet.jwt.JwtTokenProvider; +import com.splanet.splanet.plan.dto.PlanRequestDto; +import com.splanet.splanet.plan.dto.PlanResponseDto; +import com.splanet.splanet.plan.service.PlanService; +import com.splanet.splanet.user.entity.User; +import com.splanet.splanet.user.repository.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +class FriendControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private FriendService friendService; + + @Autowired + private PlanService planService; + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + @Autowired + private FriendRepository friendRepository; + + @Autowired + private FriendRequestRepository friendRequestRepository; + + @Autowired + private UserRepository userRepository; + + private String accessToken; + private Long userId; + + @BeforeEach + void setUp() { + User user = User.builder() + .nickname("testuser") + .profileImage("testimage.png") + .build(); + userRepository.save(user); + userId = user.getId(); + + accessToken = "Bearer " + jwtTokenProvider.createAccessToken(userId); + } + + @Test + @DisplayName("친구 목록 조회 성공") + @WithMockUser + void 친구_목록_조회_성공() throws Exception { + // 친구 목록 조회 결과가 비어 있지 않음을 확인 + List friendResponses = friendService.getFriends(userId); + + // 친구 목록이 비어 있지 않으면 테스트 진행 + if (!friendResponses.isEmpty()) { + mockMvc.perform(get("/api/friends") + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].nickname").value(friendResponses.get(0).nickname())) + .andExpect(jsonPath("$[0].profileImage").value(friendResponses.get(0).profileImage())); + } else { + mockMvc.perform(get("/api/friends") + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isEmpty()); + } + } + + @Test + @DisplayName("친구 목록 조회 실패 - 인증되지 않은 사용자") + void 친구_목록_조회_실패_인증되지않은_사용자() throws Exception { + mockMvc.perform(get("/api/friends") + .header("Authorization", "Bearer invalid-token") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.message").value("유효하지 않은 토큰입니다.")); + } + + @Test + @DisplayName("친구 플랜 조회 실패 - 인증되지 않은 사용자") + void 친구_플랜_조회_실패_인증되지않은_사용자() throws Exception { + Long friendId = 1L; + + mockMvc.perform(get("/api/friends/{friendId}/plans", friendId) + .header("Authorization", "Bearer invalid-token") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.message").value("유효하지 않은 토큰입니다.")); + } + + @Test + @DisplayName("친구 삭제 성공") + @WithMockUser + void 친구_삭제_성공() throws Exception { + // 친구 관계를 만들기 위한 유저 생성 + User friendUser = User.builder() + .nickname("frienduser") + .profileImage("friendimage.png") + .build(); + userRepository.save(friendUser); + + // 기존 userId로 조회하여 친구 관계를 만들기 위해 user 객체를 조회 + User user = userRepository.findById(userId).orElseThrow(); + + // 친구 관계 등록 (예: userId와 friendUserId 사이에 친구 관계가 만들어진다고 가정) + friendRepository.save(new Friend(user, friendUser)); + + mockMvc.perform(delete("/api/friends/{friendId}", friendUser.getId()) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("친구 맺기 취소되었습니다!")); + } + + @Test + @DisplayName("친구 삭제 실패 - 친구가 아님") + @WithMockUser + void 친구_삭제_실패_친구가_아님() throws Exception { + // 존재하지 않는 친구의 ID + Long nonFriendId = 999L; + + mockMvc.perform(delete("/api/friends/{friendId}", nonFriendId) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("친구가 아닙니다.")); + } + + @Test + @DisplayName("친구 삭제 실패 - 인증되지 않은 사용자") + void 친구_삭제_실패_인증되지않은_사용자() throws Exception { + // 인증되지 않은 토큰 + mockMvc.perform(delete("/api/friends/{friendId}", 1L) + .header("Authorization", "Bearer invalid-token") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.message").value("유효하지 않은 토큰입니다.")); + } + + @Test + @WithMockUser + void 친구_플랜_조회_실패_친구가_아님() throws Exception { + Long nonFriendId = 999L; + + mockMvc.perform(get("/api/friends/{friendId}/plans", nonFriendId) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("친구가 아닙니다.")); + } +} \ No newline at end of file diff --git a/src/test/java/com/splanet/splanet/friendRequest/controller/FriendRequestControllerIntegrationTest.java b/src/test/java/com/splanet/splanet/friendRequest/controller/FriendRequestControllerIntegrationTest.java new file mode 100644 index 00000000..39544bfc --- /dev/null +++ b/src/test/java/com/splanet/splanet/friendRequest/controller/FriendRequestControllerIntegrationTest.java @@ -0,0 +1,281 @@ +package com.splanet.splanet.friendRequest.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.splanet.splanet.friend.repository.FriendRepository; +import com.splanet.splanet.friendRequest.dto.FriendRequestCreateRequest; +import com.splanet.splanet.friendRequest.entity.FriendRequest; +import com.splanet.splanet.friendRequest.repository.FriendRequestRepository; +import com.splanet.splanet.user.entity.User; +import com.splanet.splanet.user.repository.UserRepository; +import com.splanet.splanet.jwt.JwtTokenProvider; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.List; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +class FriendRequestControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + @Autowired + private UserRepository userRepository; + + @Autowired + private FriendRepository friendRepository; + + @Autowired + private FriendRequestRepository friendRequestRepository; + + private String accessToken; + private Long userId; + + @BeforeEach + void setUp() { + User user = User.builder() + .nickname("testuser") + .profileImage("testimage.png") + .build(); + userRepository.save(user); + userId = user.getId(); + + accessToken = "Bearer " + jwtTokenProvider.createAccessToken(userId); + } + + @Test + @WithMockUser + void 친구_요청_실패_본인에게_친구_요청() throws Exception { + FriendRequestCreateRequest request = new FriendRequestCreateRequest(userId); + + mockMvc.perform(post("/api/friends/requests") + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("본인에게 친구요청을 보낼 수 없습니다.")); + } + + @Test + @WithMockUser + void 친구_요청_실패_이미_요청한_사용자() throws Exception { + User receiverUser = User.builder() + .nickname("friendUser") + .profileImage("friendImage.png") + .build(); + userRepository.save(receiverUser); + + // 이미 보낸 요청이 있다고 가정 + FriendRequestCreateRequest request = new FriendRequestCreateRequest(receiverUser.getId()); + friendRequestRepository.save(FriendRequest.builder() + .requester(userRepository.findById(userId).orElseThrow()) + .receiver(receiverUser) + .status(FriendRequest.Status.PENDING) + .build()); + + mockMvc.perform(post("/api/friends/requests") + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("이미 요청을 보냈습니다.")); + } + + @Test + @WithMockUser + void 친구_요청_실패_존재하지_않는_사용자() throws Exception { + // 존재하지 않는 사용자 ID로 친구 요청 시도 + FriendRequestCreateRequest request = new FriendRequestCreateRequest(999L); + + mockMvc.perform(post("/api/friends/requests") + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("유저가 존재하지 않습니다.")); + } + + @Test + @WithMockUser + void 친구_요청_수락_실패_이미_수락된_요청() throws Exception { + // 이미 수락된 친구 요청을 시도 + User receiverUser = User.builder() + .nickname("friendUser") + .profileImage("friendImage.png") + .build(); + userRepository.save(receiverUser); + + User requesterUser = User.builder() + .nickname("requesterUser") + .profileImage("requesterImage.png") + .build(); + userRepository.save(requesterUser); + + // 친구 요청 생성 후 수락 + FriendRequest friendRequest = FriendRequest.builder() + .requester(requesterUser) + .receiver(receiverUser) + .status(FriendRequest.Status.PENDING) + .build(); + friendRequestRepository.save(friendRequest); + + // 친구 요청 수락 + friendRequest.setStatus(FriendRequest.Status.ACCEPTED); + friendRequestRepository.save(friendRequest); + + // 이미 수락된 요청을 다시 수락하려고 시도 + mockMvc.perform(post("/api/friends/requests/{requestId}/accept", friendRequest.getId()) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("이미 수락하거나 거절한 사용자 입니다.")); + } + + @Test + @WithMockUser + void 친구_요청_거절_실패_이미_수락된_요청() throws Exception { + // 이미 수락된 친구 요청을 시도 + User receiverUser = User.builder() + .nickname("friendUser") + .profileImage("friendImage.png") + .build(); + userRepository.save(receiverUser); + + User requesterUser = User.builder() + .nickname("requesterUser") + .profileImage("requesterImage.png") + .build(); + userRepository.save(requesterUser); + + // 친구 요청 생성 후 수락 + FriendRequest friendRequest = FriendRequest.builder() + .requester(requesterUser) + .receiver(receiverUser) + .status(FriendRequest.Status.PENDING) + .build(); + friendRequestRepository.save(friendRequest); + + // 친구 요청 수락 + friendRequest.setStatus(FriendRequest.Status.ACCEPTED); + friendRequestRepository.save(friendRequest); + + // 이미 수락된 요청을 다시 거절하려고 시도 + mockMvc.perform(post("/api/friends/requests/{requestId}/reject", friendRequest.getId()) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("이미 수락하거나 거절한 사용자 입니다.")); + } + + @Test + @WithMockUser + void 친구_요청_취소_성공() throws Exception { + // 친구 요청 생성 (보낸 요청) + User requester = userRepository.findById(userId).orElseThrow(); + User receiver = User.builder() + .nickname("receiver") + .profileImage("receiverImage.png") + .build(); + userRepository.save(receiver); + + FriendRequest friendRequest = FriendRequest.builder() + .requester(requester) + .receiver(receiver) + .status(FriendRequest.Status.PENDING) + .build(); + friendRequestRepository.save(friendRequest); + + // 친구 요청 취소 + mockMvc.perform(delete("/api/friends/requests/{requestId}/cancel", friendRequest.getId()) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("친구 요청이 성공적으로 취소되었습니다.")); + + mockMvc.perform(get("/api/friends/requests/sent") + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.size()").value(0)); // 취소되었으므로 보낸 요청 목록에 없음 + } + + @Test + @WithMockUser + void 친구_요청_취소_실패_내가_보낸_요청_아님() throws Exception { + // 친구 요청 생성 (다른 사용자가 보낸 요청) + User requester = User.builder() + .nickname("requester") + .profileImage("requesterImage.png") + .build(); + userRepository.save(requester); + + User receiver = userRepository.findById(userId).orElseThrow(); + FriendRequest friendRequest = FriendRequest.builder() + .requester(requester) + .receiver(receiver) + .status(FriendRequest.Status.PENDING) + .build(); + friendRequestRepository.save(friendRequest); + + // 친구 요청 취소 (다른 사용자가 보낸 요청을 내가 취소하려고 시도) + mockMvc.perform(delete("/api/friends/requests/{requestId}/cancel", friendRequest.getId()) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.message").value("권한이 없습니다.")); + } + + @Test + @WithMockUser + void 친구_요청_취소_실패_존재하지_않는_요청() throws Exception { + // 존재하지 않는 요청 ID로 취소 시도 + mockMvc.perform(delete("/api/friends/requests/{requestId}/cancel", 999L) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("해당 친구 요청을 찾을 수 없습니다.")); + } + + @Test + @WithMockUser + void 친구_요청_목록_조회_성공_받은_요청() throws Exception { + mockMvc.perform(get("/api/friends/requests/received") + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()); + } + + @Test + @WithMockUser + void 친구_요청_목록_조회_성공_보낸_요청() throws Exception { + mockMvc.perform(get("/api/friends/requests/sent") + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()); + } +} \ No newline at end of file diff --git a/src/test/java/com/splanet/splanet/payment/controller/PaymentControllerIntegrationTest.java b/src/test/java/com/splanet/splanet/payment/controller/PaymentControllerIntegrationTest.java new file mode 100644 index 00000000..5ddff179 --- /dev/null +++ b/src/test/java/com/splanet/splanet/payment/controller/PaymentControllerIntegrationTest.java @@ -0,0 +1,142 @@ +package com.splanet.splanet.payment.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.splanet.splanet.jwt.JwtTokenProvider; +import com.splanet.splanet.payment.entity.Payment; +import com.splanet.splanet.payment.repository.PaymentRepository; +import com.splanet.splanet.subscription.entity.Subscription; +import com.splanet.splanet.subscription.repository.SubscriptionRepository; +import com.splanet.splanet.user.entity.User; +import com.splanet.splanet.user.repository.UserRepository; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.time.LocalDateTime; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +public class PaymentControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private UserRepository userRepository; + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + @Autowired + private PaymentRepository paymentRepository; + + @Autowired + private SubscriptionRepository subscriptionRepository; + + private User testUser; + private String token; + private Subscription testSubscription; + private Payment testPayment; + + @BeforeEach + void setUp() { + // 테스트용 유저 생성 + testUser = User.builder() + .nickname("테스트유저") + .profileImage("http://example.com/profile.jpg") + .kakaoId(123456789L) + .isPremium(false) + .build(); + userRepository.save(testUser); + + // JWT 토큰 생성 + token = "Bearer " + jwtTokenProvider.createAccessToken(testUser.getId()); + + // 테스트용 Subscription 생성 + testSubscription = Subscription.builder() + .user(testUser) + .status(Subscription.Status.ACTIVE) + .type(Subscription.Type.MONTHLY) + .startDate(LocalDateTime.now()) + .endDate(LocalDateTime.now().plusMonths(1)) + .build(); + subscriptionRepository.save(testSubscription); + + // Payment 생성 + testPayment = Payment.builder() + .price(1000) + .status(Payment.Status.PENDING) + .paymentDate(LocalDateTime.now()) + .subscription(testSubscription) + .build(); + paymentRepository.save(testPayment); + } + + @Test + public void 결제상태조회_존재하지않는_결제ID_실패() throws Exception { + // given + Long invalidPaymentId = 999L; + + // when & then + mockMvc.perform(get("/api/payment/{paymentId}", invalidPaymentId) + .header("Authorization", token) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("결제 정보가 존재하지 않습니다.")) + .andExpect(jsonPath("$.status").value("404")); + } + + @Test + public void 결제삭제_성공() throws Exception { + // given + Long validPaymentId = testPayment.getId(); + Long validUserId = testPayment.getSubscription().getUser().getId(); + + // when & then + mockMvc.perform(MockMvcRequestBuilders.delete("/api/payment/{paymentId}", validPaymentId) + .header("Authorization", token) + .param("userId", validUserId.toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("결제가 성공적으로 삭제되었습니다.")); + } + + @Test + public void 결제삭제_결제없음_실패() throws Exception { + // given + Long invalidPaymentId = 999L; + + // when & then + mockMvc.perform(MockMvcRequestBuilders.delete("/api/payment/{paymentId}", invalidPaymentId) + .header("Authorization", token)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("결제 정보가 존재하지 않습니다.")) + .andExpect(jsonPath("$.status").value("404")); + } + + @Test + public void 결제삭제_다른유저의_결제정보접근_실패() throws Exception { + Long validPaymentId = testPayment.getId(); + Long invalidUserId = 123L; + String invalidToken = jwtTokenProvider.createAccessToken(invalidUserId); + + mockMvc.perform(MockMvcRequestBuilders.delete("/api/payment/{paymentId}", validPaymentId) + .header("Authorization", "Bearer " + invalidToken)) + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.message").value("다른 유저의 결제 정보에 접근할 수 없습니다.")) + .andExpect(jsonPath("$.status").value("401")); + } +} \ No newline at end of file diff --git a/src/test/java/com/splanet/splanet/previewplan/controller/PreviewPlanControllerIntegrationTest.java b/src/test/java/com/splanet/splanet/previewplan/controller/PreviewPlanControllerIntegrationTest.java new file mode 100644 index 00000000..f2437b8a --- /dev/null +++ b/src/test/java/com/splanet/splanet/previewplan/controller/PreviewPlanControllerIntegrationTest.java @@ -0,0 +1,237 @@ +//package com.splanet.splanet.previewplan.controller; +// +//import com.fasterxml.jackson.databind.ObjectMapper; +//import com.jayway.jsonpath.JsonPath; +//import com.splanet.splanet.jwt.JwtTokenProvider; +//import com.splanet.splanet.previewplan.dto.PlanCardRequestDto; +//import com.splanet.splanet.previewplan.dto.PlanCardResponseDto; +//import com.splanet.splanet.previewplan.service.PreviewPlanService; +//import com.splanet.splanet.user.entity.User; +//import com.splanet.splanet.user.repository.UserRepository; +//import org.junit.jupiter.api.BeforeEach; +//import org.junit.jupiter.api.Test; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +//import org.springframework.boot.test.context.SpringBootTest; +//import org.springframework.boot.test.mock.mockito.MockBean; +//import org.springframework.data.redis.core.RedisTemplate; +//import org.springframework.http.MediaType; +//import org.springframework.security.test.context.support.WithMockUser; +//import org.springframework.test.web.servlet.MockMvc; +//import org.springframework.test.web.servlet.MvcResult; +//import org.springframework.transaction.annotation.Transactional; +// +//import java.time.LocalDateTime; +//import java.time.ZoneId; +// +//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +// +//@SpringBootTest +//@AutoConfigureMockMvc +//@Transactional +//class PreviewPlanControllerIntegrationTest { +// +// @Autowired +// private MockMvc mockMvc; +// +// @Autowired +// private ObjectMapper objectMapper; +// +// @Autowired +// private PreviewPlanService previewPlanService; +// +// @Autowired +// private JwtTokenProvider jwtTokenProvider; +// +// @Autowired +// private UserRepository userRepository; +// +// @MockBean +// private RedisTemplate redisTemplate; +// +// private String accessToken; +// private Long userId; +// +// @BeforeEach +// void setUp() { +// // 테스트용 유저 생성 +// User user = User.builder() +// .nickname("testuser") +// .profileImage("testimage.png") +// .build(); +// userRepository.save(user); +// userId = user.getId(); +// +// // JWT 토큰 생성 +// accessToken = "Bearer " + jwtTokenProvider.createAccessToken(userId); +// } +// +// @Test +// @WithMockUser +// void 플랜카드_생성_성공() throws Exception { +// LocalDateTime now = LocalDateTime.now(); +// PlanCardRequestDto planCardRequestDto = new PlanCardRequestDto( +// "테스트 플랜 카드", +// "플랜 카드 설명", +// now.toString(), +// now.plusHours(2).toString() +// ); +// +// long startTimestamp = now.atZone(ZoneId.systemDefault()).toEpochSecond(); +// long endTimestamp = now.plusHours(2).atZone(ZoneId.systemDefault()).toEpochSecond(); +// +// PlanCardResponseDto planCardResponseDto = new PlanCardResponseDto( +// "device-123", +// "group-456", +// "ed0525be", +// "테스트 플랜 카드", +// "플랜 카드 설명", +// startTimestamp, +// endTimestamp +// ); +// +// mockMvc.perform(post("/api/preview-plan/card") +// .param("deviceId", "device-123") +// .param("groupId", "group-456") +// .header("Authorization", accessToken) +// .contentType(MediaType.APPLICATION_JSON) +// .content(objectMapper.writeValueAsString(planCardRequestDto))) +// .andExpect(status().isOk()) +// .andExpect(jsonPath("$.deviceId").value(planCardResponseDto.deviceId())) +// .andExpect(jsonPath("$.groupId").value(planCardResponseDto.groupId())) +// .andExpect(jsonPath("$.title").value(planCardResponseDto.title())) +// .andExpect(jsonPath("$.description").value(planCardResponseDto.description())) +// .andExpect(jsonPath("$.startTimestamp").value(startTimestamp)) +// .andExpect(jsonPath("$.endTimestamp").value(endTimestamp)); +// } +// +// @Test +// @WithMockUser +// void 플랜카드_조회_성공() throws Exception { +// // 현재 시간 설정 +// LocalDateTime now = LocalDateTime.now(); +// PlanCardRequestDto planCardRequestDto = new PlanCardRequestDto( +// "테스트 플랜 카드", +// "플랜 카드 설명", +// now.toString(), +// now.plusHours(2).toString() +// ); +// +// long startTimestamp = now.atZone(ZoneId.systemDefault()).toEpochSecond(); +// long endTimestamp = now.plusHours(2).atZone(ZoneId.systemDefault()).toEpochSecond(); +// +// MvcResult result = mockMvc.perform(post("/api/preview-plan/card") +// .param("deviceId", "device-123") +// .param("groupId", "group-456") +// .header("Authorization", accessToken) +// .contentType(MediaType.APPLICATION_JSON) +// .content(objectMapper.writeValueAsString(planCardRequestDto))) +// .andExpect(status().isOk()) +// .andReturn(); +// +// String cardId = JsonPath.read(result.getResponse().getContentAsString(), "$.cardId"); +// +// PlanCardResponseDto planCardResponseDto = new PlanCardResponseDto( +// "device-123", +// "group-456", +// cardId, +// "테스트 플랜 카드", +// "플랜 카드 설명", +// startTimestamp, +// endTimestamp +// ); +// +// mockMvc.perform(get("/api/preview-plan/card/{deviceId}/{groupId}/{cardId}", "device-123", "group-456", cardId)) +// .andExpect(status().isOk()) +// .andExpect(jsonPath("$.deviceId").value(planCardResponseDto.deviceId())) +// .andExpect(jsonPath("$.groupId").value(planCardResponseDto.groupId())) +// .andExpect(jsonPath("$.title").value(planCardResponseDto.title())) +// .andExpect(jsonPath("$.description").value(planCardResponseDto.description())) +// .andExpect(jsonPath("$.startTimestamp").value(startTimestamp)) +// .andExpect(jsonPath("$.endTimestamp").value(endTimestamp)); +// } +// +// @Test +// @WithMockUser +// void 플랜카드_수정_성공() throws Exception { +// LocalDateTime now = LocalDateTime.now(); +// PlanCardRequestDto planCardRequestDto = new PlanCardRequestDto( +// "테스트 플랜 카드", +// "플랜 카드 설명", +// now.toString(), +// now.plusHours(2).toString() +// ); +// +// long startTimestamp = now.atZone(ZoneId.systemDefault()).toEpochSecond(); +// long endTimestamp = now.plusHours(2).atZone(ZoneId.systemDefault()).toEpochSecond(); +// +// MvcResult result = mockMvc.perform(post("/api/preview-plan/card") +// .param("deviceId", "device-123") +// .param("groupId", "group-456") +// .header("Authorization", accessToken) +// .contentType(MediaType.APPLICATION_JSON) +// .content(objectMapper.writeValueAsString(planCardRequestDto))) +// .andExpect(status().isOk()) +// .andReturn(); +// +// String cardId = JsonPath.read(result.getResponse().getContentAsString(), "$.cardId"); +// +// PlanCardRequestDto updatedPlanCardRequestDto = new PlanCardRequestDto( +// "수정된 플랜 카드", +// "수정된 플랜 카드 설명", +// now.plusHours(1).toString(), +// now.plusHours(3).toString() +// ); +// +// long updatedStartTimestamp = now.plusHours(1).atZone(ZoneId.systemDefault()).toEpochSecond(); +// long updatedEndTimestamp = now.plusHours(3).atZone(ZoneId.systemDefault()).toEpochSecond(); +// +// mockMvc.perform(put("/api/preview-plan/card/{deviceId}/{groupId}/{cardId}", "device-123", "group-456", cardId) +// .header("Authorization", accessToken) +// .contentType(MediaType.APPLICATION_JSON) +// .content(objectMapper.writeValueAsString(updatedPlanCardRequestDto))) +// .andExpect(status().isOk()) +// .andExpect(jsonPath("$.deviceId").value("device-123")) +// .andExpect(jsonPath("$.groupId").value("group-456")) +// .andExpect(jsonPath("$.title").value(updatedPlanCardRequestDto.title())) +// .andExpect(jsonPath("$.description").value(updatedPlanCardRequestDto.description())) +// .andExpect(jsonPath("$.startTimestamp").value(updatedStartTimestamp)) +// .andExpect(jsonPath("$.endTimestamp").value(updatedEndTimestamp)); +// } +// +// @Test +// @WithMockUser +// void 플랜카드_삭제_성공() throws Exception { +// LocalDateTime now = LocalDateTime.now(); +// PlanCardRequestDto planCardRequestDto = new PlanCardRequestDto( +// "테스트 플랜 카드", +// "플랜 카드 설명", +// now.toString(), +// now.plusHours(2).toString() +// ); +// +// long startTimestamp = now.atZone(ZoneId.systemDefault()).toEpochSecond(); +// long endTimestamp = now.plusHours(2).atZone(ZoneId.systemDefault()).toEpochSecond(); +// +// MvcResult result = mockMvc.perform(post("/api/preview-plan/card") +// .param("deviceId", "device-123") +// .param("groupId", "group-456") +// .header("Authorization", accessToken) +// .contentType(MediaType.APPLICATION_JSON) +// .content(objectMapper.writeValueAsString(planCardRequestDto))) +// .andExpect(status().isOk()) +// .andReturn(); +// +// String cardId = JsonPath.read(result.getResponse().getContentAsString(), "$.cardId"); +// +// mockMvc.perform(delete("/api/preview-plan/card/{deviceId}/{groupId}/{cardId}", "device-123", "group-456", cardId) +// .header("Authorization", accessToken)) +// .andExpect(status().isOk()); +// +// mockMvc.perform(get("/api/preview-plan/card/{deviceId}/{groupId}/{cardId}", "device-123", "group-456", cardId)) +// .andExpect(status().isNotFound()) +// .andExpect(jsonPath("$.message").value("플랜이 존재하지 않습니다.")); +// } +//} diff --git a/src/test/java/com/splanet/splanet/subscription/controller/SubscriptionControllerIntegrationTest.java b/src/test/java/com/splanet/splanet/subscription/controller/SubscriptionControllerIntegrationTest.java new file mode 100644 index 00000000..5dc94ff5 --- /dev/null +++ b/src/test/java/com/splanet/splanet/subscription/controller/SubscriptionControllerIntegrationTest.java @@ -0,0 +1,146 @@ +package com.splanet.splanet.subscription.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.splanet.splanet.core.exception.ErrorCode; +import com.splanet.splanet.subscription.dto.SubscriptionRequest; +import com.splanet.splanet.subscription.entity.Subscription; +import com.splanet.splanet.subscription.repository.SubscriptionRepository; +import com.splanet.splanet.user.entity.User; +import com.splanet.splanet.user.repository.UserRepository; +import com.splanet.splanet.jwt.JwtTokenProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +class SubscriptionControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private UserRepository userRepository; + + @Autowired + private SubscriptionRepository subscriptionRepository; + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + private String token; + private User testUser; + + @BeforeEach + void setUp() { + // 테스트용 유저 생성 + testUser = User.builder() + .nickname("구독 테스트 사용자") + .profileImage("http://example.com/profile.jpg") + .kakaoId(123456789L) + .isPremium(false) + .build(); + userRepository.save(testUser); + + // JWT 토큰 생성 + token = "Bearer " + jwtTokenProvider.createAccessToken(testUser.getId()); + } + + @Test + void 구독_생성_성공() throws Exception { + // Given + SubscriptionRequest request = new SubscriptionRequest(); + request.setType(Subscription.Type.MONTHLY); + + // When & Then + mockMvc.perform(post("/api/subscription/me/subscribe") + .header("Authorization", token) + .contentType(MediaType.APPLICATION_JSON) + .content(new ObjectMapper().writeValueAsString(request))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("구독이 성공적으로 완료되었습니다.")) + .andExpect(jsonPath("$.subscription.id").exists()) + .andExpect(jsonPath("$.subscription.startDate").exists()) + .andExpect(jsonPath("$.subscription.endDate").doesNotExist()); + + // 데이터베이스에 구독 정보가 저장되었는지 확인 + Subscription newSubscription = subscriptionRepository.findTopByUserIdAndStatusOrderByStartDateDesc(testUser.getId(), Subscription.Status.ACTIVE) + .orElseThrow(() -> new AssertionError("구독 정보가 저장되지 않았습니다.")); + + assertThat(newSubscription.getType()).isEqualTo(request.getType()); + assertThat(newSubscription.getStatus()).isEqualTo(Subscription.Status.ACTIVE); + } + + @Test + void 구독_생성_실패_이미_구독중() throws Exception { + // Given + Subscription existingSubscription = Subscription.builder() + .user(testUser) + .status(Subscription.Status.ACTIVE) + .type(Subscription.Type.MONTHLY) + .startDate(LocalDateTime.now()) + .endDate(LocalDateTime.now().plusMonths(1)) + .build(); + subscriptionRepository.save(existingSubscription); + + // When & Then + SubscriptionRequest request = new SubscriptionRequest(); + request.setType(Subscription.Type.MONTHLY); + + mockMvc.perform(post("/api/subscription/me/subscribe") + .header("Authorization", token) + .contentType(MediaType.APPLICATION_JSON) + .content(new ObjectMapper().writeValueAsString(request))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value(ErrorCode.ALREADY_SUBSCRIBED.getMessage())) + .andExpect(jsonPath("$.status").value(ErrorCode.ALREADY_SUBSCRIBED.getStatus().value())); + } + + @Test + void 구독_정보_조회_성공() throws Exception { + // Given + Subscription subscription = Subscription.builder() + .user(testUser) + .type(Subscription.Type.MONTHLY) + .startDate(LocalDateTime.now()) + .endDate(LocalDateTime.now().plusMonths(1)) + .build(); + subscriptionRepository.save(subscription); + + // When & Then + mockMvc.perform(get("/api/subscription/me") + .header("Authorization", token) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("구독 정보가 성공적으로 조회되었습니다.")) + .andExpect(jsonPath("$.subscription.id").exists()) + .andExpect(jsonPath("$.subscription.startDate").exists()) + .andExpect(jsonPath("$.subscription.endDate").exists()); + } + + @Test + void 구독_정보_조회_실패_구독없음() throws Exception { + // Given + + // When & Then + mockMvc.perform(get("/api/subscriptions/me") + .header("Authorization", "Bearer ") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + } +} \ No newline at end of file diff --git a/src/test/java/com/splanet/splanet/team/controller/TeamControllerIntegrationTest.java b/src/test/java/com/splanet/splanet/team/controller/TeamControllerIntegrationTest.java new file mode 100644 index 00000000..c52b1d00 --- /dev/null +++ b/src/test/java/com/splanet/splanet/team/controller/TeamControllerIntegrationTest.java @@ -0,0 +1,125 @@ +package com.splanet.splanet.team.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.splanet.splanet.team.entity.*; +import com.splanet.splanet.team.repository.TeamInvitationRepository; +import com.splanet.splanet.team.repository.TeamRepository; +import com.splanet.splanet.team.repository.TeamUserRelationRepository; +import com.splanet.splanet.team.service.TeamService; +import com.splanet.splanet.jwt.JwtTokenProvider; +import com.splanet.splanet.user.entity.User; +import com.splanet.splanet.user.repository.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpHeaders; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +class TeamControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private TeamService teamService; + + @Autowired + private TeamRepository teamRepository; + + @Autowired + private TeamInvitationRepository teamInvitationRepository; + + @Autowired + private TeamUserRelationRepository teamUserRelationRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + private String accessToken; + private Long adminUserId; + private Long invitedUserId; + + private Team team; + + @BeforeEach + void setUp() { + // 관리자 유저 생성 + User adminUser = User.builder() + .nickname("adminUser") + .profileImage("admin.png") + .build(); + userRepository.save(adminUser); + adminUserId = adminUser.getId(); + + // 초대할 유저 생성 + User invitedUser = User.builder() + .nickname("invitedUser") + .profileImage("invited.png") + .build(); + userRepository.save(invitedUser); + invitedUserId = invitedUser.getId(); + + // 팀 생성 + team = new Team("Test Team", adminUser); + teamRepository.save(team); + + // 관리자 권한 설정 + TeamUserRelation adminRelation = new TeamUserRelation(team, adminUser, UserTeamRole.ADMIN); + teamUserRelationRepository.save(adminRelation); + + // JWT 토큰 생성 + accessToken = "Bearer " + jwtTokenProvider.createAccessToken(adminUserId); + } + + @Test + void 팀_유저_초대_성공() throws Exception { + mockMvc.perform(post("/api/teams/{teamId}/invite", team.getId()) + .header("Authorization", accessToken) + .param("nickname", "invitedUser")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.nickname").value("invitedUser")) + .andExpect(jsonPath("$.teamId").value(team.getId())) + .andExpect(jsonPath("$.status").value("PENDING")); + + TeamInvitation invitation = teamInvitationRepository.findAllByUserAndStatus(userRepository.findById(invitedUserId).get(), InvitationStatus.PENDING).get(0); + assertThat(invitation.getUser().getId()).isEqualTo(invitedUserId); + assertThat(invitation.getTeam().getId()).isEqualTo(team.getId()); + assertThat(invitation.getStatus()).isEqualTo(InvitationStatus.PENDING); + } + + @Test + void 팀_유저_초대_실패_관리자권한없음() throws Exception { + // 다른 유저로 JWT 토큰 발급 (관리자 권한 없음) + User nonAdminUser = User.builder() + .nickname("nonAdminUser") + .profileImage("nonAdmin.png") + .build(); + userRepository.save(nonAdminUser); + + String nonAdminToken = "Bearer " + jwtTokenProvider.createAccessToken(nonAdminUser.getId()); + + mockMvc.perform(post("/api/teams/{teamId}/invite", team.getId()) + .header("Authorization", nonAdminToken) + .param("nickname", "invitedUser")) + .andExpect(status().isForbidden()); + } +} \ No newline at end of file diff --git a/src/test/java/com/splanet/splanet/team/service/TeamServiceTest.java b/src/test/java/com/splanet/splanet/team/service/TeamServiceTest.java index 3d9591a1..1edf52fe 100644 --- a/src/test/java/com/splanet/splanet/team/service/TeamServiceTest.java +++ b/src/test/java/com/splanet/splanet/team/service/TeamServiceTest.java @@ -80,7 +80,7 @@ public void testCreateTeam_InvalidInput() { BusinessException exception = assertThrows(BusinessException.class, () -> teamService.createTeam("", 1L) ); - assertEquals(ErrorCode.INVALID_INPUT_VALUE, exception.getErrorCode()); + assertEquals(ErrorCode.TEAM_NAME_NOT_FOUND, exception.getErrorCode()); } @Test diff --git a/src/test/java/com/splanet/splanet/teamplan/controller/TeamPlanControllerIntegrationTest.java b/src/test/java/com/splanet/splanet/teamplan/controller/TeamPlanControllerIntegrationTest.java new file mode 100644 index 00000000..7a6d3421 --- /dev/null +++ b/src/test/java/com/splanet/splanet/teamplan/controller/TeamPlanControllerIntegrationTest.java @@ -0,0 +1,358 @@ +package com.splanet.splanet.teamplan.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.jsonpath.JsonPath; +import com.splanet.splanet.team.entity.Team; +import com.splanet.splanet.team.entity.TeamUserRelation; +import com.splanet.splanet.team.entity.UserTeamRole; +import com.splanet.splanet.team.repository.TeamRepository; +import com.splanet.splanet.team.repository.TeamUserRelationRepository; +import com.splanet.splanet.teamplan.dto.TeamPlanRequestDto; +import com.splanet.splanet.teamplan.entity.TeamPlan; +import com.splanet.splanet.teamplan.repository.TeamPlanRepository; +import com.splanet.splanet.teamplan.service.TeamPlanService; +import com.splanet.splanet.jwt.JwtTokenProvider; +import com.splanet.splanet.user.entity.User; +import com.splanet.splanet.user.repository.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +class TeamPlanControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private TeamPlanService teamPlanService; + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + @Autowired + private TeamUserRelationRepository teamUserRelationRepository; + + @Autowired + private TeamPlanRepository teamPlanRepository; + + @Autowired + private TeamRepository teamRepository; + + @Autowired + private UserRepository userRepository; + + private String accessToken; + private Long userId; + private Long teamId; + + @BeforeEach + void setUp() { + // 사용자 생성 + User user = User.builder() + .nickname("testuser") + .profileImage("testimage.png") + .build(); + userRepository.save(user); + userId = user.getId(); + + // 팀 생성 + Team team = new Team("팀네임여기있음!", user); + teamRepository.save(team); + teamId = team.getId(); + + // 팀 사용자 관계 설정 + TeamUserRelation relation = new TeamUserRelation(team, user, UserTeamRole.ADMIN); + teamUserRelationRepository.save(relation); + + // JWT 토큰 생성 + accessToken = "Bearer " + jwtTokenProvider.createAccessToken(userId); + } + + @Test + void 팀플랜_생성_성공() throws Exception { + // 팀 플랜 생성 요청 DTO + TeamPlanRequestDto requestDto = new TeamPlanRequestDto( + "11/10 테스트용 플랜", + "11/10 테스트용 플랜", + LocalDateTime.parse("2024-11-10T09:59:23.542"), + LocalDateTime.parse("2024-11-10T09:59:23.542"), + true, + true + ); + + // 팀 플랜을 생성하는 요청 + mockMvc.perform(post("/api/teams/{teamId}/plans", teamId) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.title").value("11/10 테스트용 플랜")) + .andExpect(jsonPath("$.description").value("11/10 테스트용 플랜")) + .andExpect(jsonPath("$.startDate").value("2024-11-10T09:59:23.542")) + .andExpect(jsonPath("$.endDate").value("2024-11-10T09:59:23.542")) + .andExpect(jsonPath("$.accessibility").value(true)) + .andExpect(jsonPath("$.isCompleted").value(true)); + } + + @Test + void 팀플랜_생성_실패_필드누락() throws Exception { + TeamPlanRequestDto invalidRequestDto = new TeamPlanRequestDto( + null, + "필드 누락 테스트 설명", + LocalDateTime.now(), + LocalDateTime.now().plusHours(1), + true, + false + ); + + mockMvc.perform(post("/api/teams/{teamId}/plans", teamId) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(invalidRequestDto))) + .andExpect(status().isBadRequest()); + } + + @Test + void 팀플랜_생성_실패_권한없음() throws Exception { + User otherUser = User.builder() + .nickname("unauthorizedUser") + .profileImage("otherimage.png") + .build(); + userRepository.save(otherUser); + + String otherUserAccessToken = "Bearer " + jwtTokenProvider.createAccessToken(otherUser.getId()); + + TeamPlanRequestDto requestDto = new TeamPlanRequestDto( + "권한 없는 사용자 테스트 플랜", + "권한 없는 사용자 설명", + LocalDateTime.parse("2024-11-12T09:00:00"), + LocalDateTime.parse("2024-11-12T10:00:00"), + true, + false + ); + + mockMvc.perform(post("/api/teams/{teamId}/plans", teamId) + .header("Authorization", otherUserAccessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto))) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.message").value("권한이 없습니다.")); + } + + @Test + void 팀플랜_조회_성공() throws Exception { + TeamPlanRequestDto requestDto = new TeamPlanRequestDto( + "11/10 테스트용 플랜", + "11/10 테스트용 플랜", + LocalDateTime.parse("2024-11-10T09:59:23.542"), + LocalDateTime.parse("2024-11-10T09:59:23.542"), + true, + true + ); + + String response = mockMvc.perform(post("/api/teams/{teamId}/plans", teamId) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.title").value("11/10 테스트용 플랜")) + .andReturn().getResponse().getContentAsString(); + + String planId = JsonPath.parse(response).read("$.id").toString(); + + mockMvc.perform(get("/api/teams/{teamId}/plans/{planId}", teamId, planId) + .header("Authorization", accessToken)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.title").value("11/10 테스트용 플랜")); + } + + @Test + void 팀플랜_수정_성공() throws Exception { + TeamPlanRequestDto createRequestDto = new TeamPlanRequestDto( + "수정 전 테스트 플랜", + "수정 전 테스트 설명", + LocalDateTime.parse("2024-11-11T09:00:00"), + LocalDateTime.parse("2024-11-11T10:00:00"), + true, + false + ); + + String response = mockMvc.perform(post("/api/teams/{teamId}/plans", teamId) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createRequestDto))) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + Long planId = JsonPath.parse(response).read("$.id", Long.class); + + TeamPlanRequestDto updateRequestDto = new TeamPlanRequestDto( + "수정된 플랜 제목", + "수정된 플랜 설명", + LocalDateTime.parse("2024-11-11T10:00:00"), + LocalDateTime.parse("2024-11-11T11:00:00"), + false, + true + ); + + mockMvc.perform(put("/api/teams/{teamId}/plans/{planId}", teamId, planId) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequestDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.title").value("수정된 플랜 제목")) + .andExpect(jsonPath("$.description").value("수정된 플랜 설명")) + .andExpect(jsonPath("$.startDate").value("2024-11-11T10:00:00")) + .andExpect(jsonPath("$.endDate").value("2024-11-11T11:00:00")) + .andExpect(jsonPath("$.accessibility").value(false)) + .andExpect(jsonPath("$.isCompleted").value(true)); + } + + @Test + void 팀플랜_수정_플랜없음() throws Exception { + // 존재하지 않는 플랜 ID로 수정 요청을 보낼 때 + mockMvc.perform(put("/api/teams/{teamId}/plans/{planId}", teamId, 999L) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new TeamPlanRequestDto( + "수정된 플랜 제목", "수정된 플랜 설명", LocalDateTime.now(), LocalDateTime.now(), true, true)))) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("플랜이 존재하지 않습니다.")); + } + + @Test + void 팀플랜_수정_권한없음() throws Exception { + User otherUser = User.builder() + .nickname("unauthorizedUser") + .profileImage("otherimage.png") + .build(); + userRepository.save(otherUser); + + String otherUserAccessToken = "Bearer " + jwtTokenProvider.createAccessToken(otherUser.getId()); + + TeamPlanRequestDto createRequestDto = new TeamPlanRequestDto( + "권한 테스트 플랜", + "권한 테스트 설명", + LocalDateTime.parse("2024-11-12T09:00:00"), + LocalDateTime.parse("2024-11-12T10:00:00"), + true, + false + ); + String response = mockMvc.perform(post("/api/teams/{teamId}/plans", teamId) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createRequestDto))) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + Long planId = JsonPath.parse(response).read("$.id", Long.class); + + TeamPlanRequestDto updateRequestDto = new TeamPlanRequestDto( + "권한 없는 수정", + "권한 없는 설명", + LocalDateTime.now(), + LocalDateTime.now().plusHours(1), + true, + true + ); + + mockMvc.perform(put("/api/teams/{teamId}/plans/{planId}", teamId, planId) + .header("Authorization", otherUserAccessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateRequestDto))) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.message").value("권한이 없습니다.")); + } + + @Test + void 팀플랜_삭제_성공() throws Exception { + TeamPlanRequestDto requestDto = new TeamPlanRequestDto( + "삭제용 테스트 플랜", + "삭제용 테스트 설명", + LocalDateTime.parse("2024-11-11T09:59:23.542"), + LocalDateTime.parse("2024-11-11T10:59:23.542"), + true, + false + ); + + String response = mockMvc.perform(post("/api/teams/{teamId}/plans", teamId) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto))) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + Long planId = JsonPath.parse(response).read("$.id", Long.class); + + mockMvc.perform(delete("/api/teams/{teamId}/plans/{planId}", teamId, planId) + .header("Authorization", accessToken)) + .andExpect(status().isNoContent()); + + mockMvc.perform(get("/api/teams/{teamId}/plans/{planId}", teamId, planId) + .header("Authorization", accessToken)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("플랜이 존재하지 않습니다.")); + } + + @Test + void 팀플랜_삭제_실패_플랜없음() throws Exception { + Long nonExistentPlanId = 999L; + + mockMvc.perform(delete("/api/teams/{teamId}/plans/{planId}", teamId, nonExistentPlanId) + .header("Authorization", accessToken)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("플랜이 존재하지 않습니다.")); + } + + @Test + void 팀플랜_삭제_실패_권한없음() throws Exception { + TeamPlanRequestDto requestDto = new TeamPlanRequestDto( + "삭제 권한 없는 사용자 테스트 플랜", + "삭제 권한 없는 사용자 설명", + LocalDateTime.parse("2024-11-11T09:00:00"), + LocalDateTime.parse("2024-11-11T10:00:00"), + true, + false + ); + + String response = mockMvc.perform(post("/api/teams/{teamId}/plans", teamId) + .header("Authorization", accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto))) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(); + + Long planId = JsonPath.parse(response).read("$.id", Long.class); + + User otherUser = User.builder() + .nickname("unauthorizedUser") + .profileImage("otherimage.png") + .build(); + userRepository.save(otherUser); + + String otherUserAccessToken = "Bearer " + jwtTokenProvider.createAccessToken(otherUser.getId()); + + mockMvc.perform(delete("/api/teams/{teamId}/plans/{planId}", teamId, planId) + .header("Authorization", otherUserAccessToken)) + .andExpect(status().isForbidden()) + .andExpect(jsonPath("$.message").value("권한이 없습니다.")); + } +} \ No newline at end of file