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 e2a5dda6..26a0cbaa 100644 --- a/src/main/java/com/splanet/splanet/core/exception/ErrorCode.java +++ b/src/main/java/com/splanet/splanet/core/exception/ErrorCode.java @@ -36,6 +36,17 @@ public enum ErrorCode { INVITATION_ALREADY_PROCESSED("초대가 이미 처리되었습니다.", HttpStatus.BAD_REQUEST), USER_ALREADY_IN_TEAM("해당 유저는 이미 팀에 속해 있습니다.", HttpStatus.BAD_REQUEST), + + // friend + FRIEND_NOT_FOUND("친구가 아닙니다.",HttpStatus.NOT_FOUND), + FRIEND_REQUEST_NOT_FOUND("해당 친구 요청을 찾을 수 없습니다.", HttpStatus.NOT_FOUND), + FRIEND_ALREADY_EXISTS("이미 친구 목록에 있습니다.", HttpStatus.BAD_REQUEST), + FRIEND_REQUEST_ALREADY_SENT("이미 요청을 보냈습니다.",HttpStatus.BAD_REQUEST), + FRIEND_REQUEST_NOT_FOUND_IN_RECEIVED_LIST("내가 받은 요청이 아닙니다.", HttpStatus.NOT_FOUND), + FRIEND_REQUEST_NOT_RECEIVER("본인이 보낸 요청은 수락하거나 거절할 수 없습니다.", HttpStatus.BAD_REQUEST), + FRIEND_REQUEST_ALREADY_ACCEPTED_OR_REJECTED("이미 수락하거나 거절한 사용자 입니다.", HttpStatus.BAD_REQUEST), + SELF_FRIEND_REQUEST_NOT_ALLOWED("본인에게 친구요청을 보낼 수 없습니다.", HttpStatus.BAD_REQUEST), + // redis REDIS_SCAN_FAILED("Redis 키 스캔 중 오류가 발생했습니다.", HttpStatus.SERVICE_UNAVAILABLE); diff --git a/src/main/java/com/splanet/splanet/friend/controller/FriendApi.java b/src/main/java/com/splanet/splanet/friend/controller/FriendApi.java new file mode 100644 index 00000000..d3a2114e --- /dev/null +++ b/src/main/java/com/splanet/splanet/friend/controller/FriendApi.java @@ -0,0 +1,50 @@ +package com.splanet.splanet.friend.controller; + +import com.splanet.splanet.friend.dto.FriendResponse; +import com.splanet.splanet.plan.dto.PlanResponseDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.List; +import java.util.Map; + +@RequestMapping("/api/friends") +@Tag(name = "Friend", description = "친구 관련 API") +public interface FriendApi { + + @GetMapping + @Operation(summary = "친구 목록 조회", description = "사용자의 친구 목록을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "친구 목록이 성공적으로 조회되었습니다."), + @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자입니다.") + }) + ResponseEntity> getFriends( + @Parameter(description = "JWT 인증으로 전달된 사용자 ID", required = true) @AuthenticationPrincipal Long userId); + + @GetMapping("/{friendId}/plans") + @Operation(summary = "친구 플랜 조회", description = "친구의 공개된 플랜 목록을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "친구의 공개된 계획이 성공적으로 조회되었습니다."), + @ApiResponse(responseCode = "400", description = "잘못된 요청입니다 (유효하지 않은 친구 ID)."), + @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자입니다."), + @ApiResponse(responseCode = "404", description = "친구를 찾을 수 없습니다.") + }) + ResponseEntity> getFriendPlan( + @Parameter(description = "조회할 친구 ID", required = true) @PathVariable("friendId") Long friendId, + @Parameter(description = "JWT 인증으로 전달된 사용자 ID", required = true) @AuthenticationPrincipal Long userId); + + @DeleteMapping("/{friendId}") + @Operation(summary = "친구 삭제하기", description = "친구 목록에서 삭제합니다.") + ResponseEntity> unfriend( + @Parameter(description = "삭제할 친구 ID", required = true) @PathVariable("friendId") Long friendId, + @Parameter(description = "JWT 인증으로 전달된 사용자 ID", required = true) @AuthenticationPrincipal Long userId); +} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friend/controller/FriendController.java b/src/main/java/com/splanet/splanet/friend/controller/FriendController.java new file mode 100644 index 00000000..ebbbccc7 --- /dev/null +++ b/src/main/java/com/splanet/splanet/friend/controller/FriendController.java @@ -0,0 +1,45 @@ +package com.splanet.splanet.friend.controller; + +import com.splanet.splanet.friend.dto.FriendResponse; +import com.splanet.splanet.friend.service.FriendService; +import com.splanet.splanet.plan.dto.PlanResponseDto; +import com.splanet.splanet.plan.repository.PlanRepository; +import com.splanet.splanet.user.repository.UserRepository; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + +@RestController +public class FriendController implements FriendApi { + + private final FriendService friendService; + + public FriendController(FriendService friendService) { + this.friendService = friendService;; + } + + @Override + public ResponseEntity> getFriends(Long userId) { + List friends = friendService.getFriends(userId); + return ResponseEntity.ok(friends); + } + + @Override + public ResponseEntity> getFriendPlan( + @PathVariable Long friendId, + @AuthenticationPrincipal Long userId) { + return friendService.getFriendPlan(friendId, userId); + } + + @Override + public ResponseEntity> unfriend( + @PathVariable Long friendId, + @AuthenticationPrincipal Long userId) { + ResponseEntity> responseEntity = friendService.unfriend(friendId, userId); + return responseEntity; + } +} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friend/dto/FriendResponse.java b/src/main/java/com/splanet/splanet/friend/dto/FriendResponse.java new file mode 100644 index 00000000..1ba32453 --- /dev/null +++ b/src/main/java/com/splanet/splanet/friend/dto/FriendResponse.java @@ -0,0 +1,4 @@ +package com.splanet.splanet.friend.dto; + +public record FriendResponse(Long userId, String nickname, String profileImage) { +} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friend/entity/Friend.java b/src/main/java/com/splanet/splanet/friend/entity/Friend.java index 0036adbf..8be6e070 100644 --- a/src/main/java/com/splanet/splanet/friend/entity/Friend.java +++ b/src/main/java/com/splanet/splanet/friend/entity/Friend.java @@ -1,6 +1,7 @@ package com.splanet.splanet.friend.entity; import com.splanet.splanet.core.BaseEntity; +import com.splanet.splanet.user.entity.User; import jakarta.persistence.*; import lombok.*; import lombok.experimental.SuperBuilder; @@ -13,9 +14,11 @@ @Entity public class Friend extends BaseEntity { - @Column(name = "user_id", nullable = false) - private Long userId; + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; - @Column(name = "friend_id", nullable = false) - private Long friendId; + @ManyToOne + @JoinColumn(name = "friend_id", nullable = false) + private User friend; } \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friend/repository/FriendRepository.java b/src/main/java/com/splanet/splanet/friend/repository/FriendRepository.java index 48b5fc00..03b87d14 100644 --- a/src/main/java/com/splanet/splanet/friend/repository/FriendRepository.java +++ b/src/main/java/com/splanet/splanet/friend/repository/FriendRepository.java @@ -2,8 +2,20 @@ import com.splanet.splanet.friend.entity.Friend; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.List; + + @Repository public interface FriendRepository extends JpaRepository { + List findByUserId(Long userId); + boolean existsByUserIdAndFriendId(Long userId, Long friendId); + + @Modifying + @Query("DELETE FROM Friend f WHERE f.user.id = :requesterId AND f.friend.id = :receiverId") + void deleteByRequesterIdAndReceiverId(@Param("requesterId") Long requesterId, @Param("receiverId") Long receiverId); } \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friend/service/FriendService.java b/src/main/java/com/splanet/splanet/friend/service/FriendService.java new file mode 100644 index 00000000..93d6891d --- /dev/null +++ b/src/main/java/com/splanet/splanet/friend/service/FriendService.java @@ -0,0 +1,103 @@ +package com.splanet.splanet.friend.service; + +import com.splanet.splanet.core.exception.BusinessException; +import com.splanet.splanet.core.exception.ErrorCode; +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.friendRequest.entity.FriendRequest; +import com.splanet.splanet.friendRequest.repository.FriendRequestRepository; +import com.splanet.splanet.plan.dto.PlanResponseDto; +import com.splanet.splanet.plan.entity.Plan; +import com.splanet.splanet.plan.repository.PlanRepository; +import com.splanet.splanet.user.entity.User; +import com.splanet.splanet.user.repository.UserRepository; +import jakarta.transaction.Transactional; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class FriendService { + + private final FriendRepository friendRepository; + private final PlanRepository planRepository; + private final FriendRequestRepository friendRequestRepository; + + public FriendService(FriendRepository friendRepository, PlanRepository planRepository, FriendRequestRepository friendRequestRepository) { + this.friendRepository = friendRepository; + this.planRepository = planRepository; + this.friendRequestRepository = friendRequestRepository; + } + + // 친구 목록 조회 + public List getFriends(Long userId) { + List friends = friendRepository.findByUserId(userId); + + // Friend 엔티티를 FriendResponse DTO로 변환 + return friends.stream() + .map(friend -> { + User friendUser = friend.getFriend(); + return new FriendResponse( + friendUser.getId(), + friendUser.getNickname(), + friendUser.getProfileImage() + ); + }) + .collect(Collectors.toList()); + } + + // 친구의 공개 플랜 조회 + public ResponseEntity> getFriendPlan(Long friendId, Long userId) { + // 친구 목록에 friendId가 있는지 확인 + boolean isFriend = friendRepository.existsByUserIdAndFriendId(userId, friendId); + + if (!isFriend) { + throw new BusinessException(ErrorCode.FRIEND_NOT_FOUND); + } + + List publicPlans = planRepository.findAllByUserIdAndAccessibility(friendId, true); + + // 공개된 플랜이 없을 경우, 빈 목록 반환 + if (publicPlans.isEmpty()) { + return ResponseEntity.ok(Collections.emptyList()); + } + + List planResponseDtos = publicPlans.stream() + .map(plan -> PlanResponseDto.builder() + .id(plan.getId()) + .title(plan.getTitle()) + .description(plan.getDescription()) + .startDate(plan.getStartDate()) + .endDate(plan.getEndDate()) + .accessibility(plan.getAccessibility()) + .isCompleted(plan.getIsCompleted()) + .createdAt(plan.getCreatedAt()) + .updatedAt(plan.getUpdatedAt()) + .build()) + .collect(Collectors.toList()); + + 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); + + List pendingRequests = friendRequestRepository.findPendingRequestsByReceiverId(userId, friendId, FriendRequest.Status.PENDING); + for (FriendRequest request : pendingRequests) { + friendRequestRepository.delete(request); + } + + return ResponseEntity.ok(Map.of("message", "친구 맺기 취소되었습니다!")); + } +} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friendRequest/controller/FriendRequestApi.java b/src/main/java/com/splanet/splanet/friendRequest/controller/FriendRequestApi.java new file mode 100644 index 00000000..b31c72d5 --- /dev/null +++ b/src/main/java/com/splanet/splanet/friendRequest/controller/FriendRequestApi.java @@ -0,0 +1,72 @@ +package com.splanet.splanet.friendRequest.controller; + +import com.splanet.splanet.friendRequest.dto.FriendRequestCreateRequest; +import com.splanet.splanet.friendRequest.dto.ReceivedFriendRequestResponse; +import com.splanet.splanet.friendRequest.dto.SentFriendRequestResponse; +import com.splanet.splanet.friendRequest.dto.SuccessResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RequestMapping("/api/friends/requests") +@Tag(name = "FriendRequest", description = "친구 요청 관련 API") +public interface FriendRequestApi { + + @PostMapping + @Operation(summary = "친구 요청", description = "특정 사용자에게 친구 요청을 보냅니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "친구 요청이 성공적으로 전송되었습니다."), + @ApiResponse(responseCode = "400", description = "잘못된 요청입니다 (유효하지 않은 유저 ID)."), + @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자입니다.") + }) + ResponseEntity sendFriendRequest( + @Parameter(description = "친구 요청을 보낼 사용자 ID", required = true) @AuthenticationPrincipal Long userId, + @RequestBody FriendRequestCreateRequest request); + + @PostMapping("/{requestId}/accept") + @Operation(summary = "친구 요청 수락", description = "친구 요청 수락") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "친구 요청 성공적으로 수락되었습니다."), + @ApiResponse(responseCode = "400", description = "잘못된 요청입니다 (유효하지 않은 유저 ID)."), + @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자입니다."), + @ApiResponse(responseCode = "404", description = "친구 요청을 찾을 수 없습니다.") + }) + ResponseEntity acceptFriendRequest(@AuthenticationPrincipal Long userId, + @Parameter(description = "친구 요청 ID", required = true) @PathVariable Long requestId); + + @PostMapping("/{requestId}/reject") + @Operation(summary = "친구 요청 거절", description = "친구 요청 거절") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "친구 요청이 성공적으로 거절되었습니다."), + @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자입니다."), + @ApiResponse(responseCode = "404", description = "친구 요청을 찾을 수 없습니다."), + @ApiResponse(responseCode = "404", description = "친구 요청을 찾을 수 없습니다.") + }) + ResponseEntity rejectFriendRequest( + @Parameter(description = "친구 요청 ID", required = true) @PathVariable Long requestId, @AuthenticationPrincipal Long userId); + + @GetMapping("/received") + @Operation(summary = "친구 요청 목록 조회 (받은 요청)", description = "사용자가 받은 친구 요청 목록을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "받은 친구 요청 목록이 성공적으로 조회되었습니다."), + @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자입니다.") + }) + ResponseEntity> getReceivedRequests( + @Parameter(description = "JWT 인증으로 전달된 사용자 ID", required = true) @AuthenticationPrincipal Long userId); + + @GetMapping("/sent") + @Operation(summary = "친구 요청 목록 조회 (보낸 요청)", description = "사용자가 보낸 친구 요청 목록을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "보낸 친구 요청 목록이 성공적으로 조회되었습니다."), + @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자입니다.") + }) + ResponseEntity> getSentRequests( + @Parameter(description = "JWT 인증으로 전달된 사용자 ID", required = true) @AuthenticationPrincipal Long userId); +} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friendRequest/controller/FriendRequestController.java b/src/main/java/com/splanet/splanet/friendRequest/controller/FriendRequestController.java new file mode 100644 index 00000000..a952084b --- /dev/null +++ b/src/main/java/com/splanet/splanet/friendRequest/controller/FriendRequestController.java @@ -0,0 +1,61 @@ +package com.splanet.splanet.friendRequest.controller; + +import com.splanet.splanet.friendRequest.dto.FriendRequestCreateRequest; +import com.splanet.splanet.friendRequest.dto.ReceivedFriendRequestResponse; +import com.splanet.splanet.friendRequest.dto.SentFriendRequestResponse; +import com.splanet.splanet.friendRequest.dto.SuccessResponse; +import com.splanet.splanet.friendRequest.service.FriendRequestService; +import com.splanet.splanet.user.repository.UserRepository; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +public class FriendRequestController implements FriendRequestApi{ + + private final FriendRequestService friendRequestService; + private final UserRepository userRepository; + + public FriendRequestController(FriendRequestService friendRequestService, UserRepository userRepository) { + this.friendRequestService = friendRequestService; + this.userRepository = userRepository; + } + + // 친구 요청 전송 + @Override + public ResponseEntity sendFriendRequest(@AuthenticationPrincipal Long userId, + @RequestBody FriendRequestCreateRequest request) { + Long receiverId = request.receiverId(); + friendRequestService.sendFriendRequest(userId, receiverId); + SuccessResponse response = new SuccessResponse("친구 요청이 성공적으로 전송되었습니다."); + + return ResponseEntity.ok(response); + } + + // 친구 요청 수락 + @Override + public ResponseEntity acceptFriendRequest(@AuthenticationPrincipal Long userId, + @PathVariable Long requestId) { + ReceivedFriendRequestResponse response = friendRequestService.acceptFriendRequest(requestId, userId); + return ResponseEntity.ok(response); + } + + // 친구 요청 거절 + @Override + public ResponseEntity rejectFriendRequest(@PathVariable Long requestId, @AuthenticationPrincipal Long userId) { + return ResponseEntity.ok(friendRequestService.rejectFriendRequest(requestId, userId)); + } + + // 친구 요청 목록 조회(받은 요청) + public ResponseEntity> getReceivedRequests(@AuthenticationPrincipal Long userId) { + return ResponseEntity.ok(friendRequestService.getReceivedFriendRequests(userId)); + } + + // 친구 요청 목록 조회(보낸 요청) + @Override + public ResponseEntity> getSentRequests(@AuthenticationPrincipal Long userId) { + return ResponseEntity.ok(friendRequestService.getSentFriendRequests(userId)); + } +} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friendRequest/dto/FriendRequestCreateRequest.java b/src/main/java/com/splanet/splanet/friendRequest/dto/FriendRequestCreateRequest.java new file mode 100644 index 00000000..3b1c5260 --- /dev/null +++ b/src/main/java/com/splanet/splanet/friendRequest/dto/FriendRequestCreateRequest.java @@ -0,0 +1,3 @@ +package com.splanet.splanet.friendRequest.dto; + +public record FriendRequestCreateRequest(Long receiverId) {} diff --git a/src/main/java/com/splanet/splanet/friendRequest/dto/ReceivedFriendRequestResponse.java b/src/main/java/com/splanet/splanet/friendRequest/dto/ReceivedFriendRequestResponse.java new file mode 100644 index 00000000..913507f0 --- /dev/null +++ b/src/main/java/com/splanet/splanet/friendRequest/dto/ReceivedFriendRequestResponse.java @@ -0,0 +1,10 @@ +package com.splanet.splanet.friendRequest.dto; + +// 받은 요청 정보담는 dto +public record ReceivedFriendRequestResponse( + Long id, + Long requesterId, + String requesterName, + String status, + String profileImage +) {} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friendRequest/dto/SentFriendRequestResponse.java b/src/main/java/com/splanet/splanet/friendRequest/dto/SentFriendRequestResponse.java new file mode 100644 index 00000000..9f8b0aa8 --- /dev/null +++ b/src/main/java/com/splanet/splanet/friendRequest/dto/SentFriendRequestResponse.java @@ -0,0 +1,10 @@ +package com.splanet.splanet.friendRequest.dto; + +// 보낸 요청 정보담는 dto +public record SentFriendRequestResponse( + Long id, + Long receiverId, + String receiverName, + String status, + String profileImage +) {} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friendRequest/dto/SuccessResponse.java b/src/main/java/com/splanet/splanet/friendRequest/dto/SuccessResponse.java new file mode 100644 index 00000000..36e959e9 --- /dev/null +++ b/src/main/java/com/splanet/splanet/friendRequest/dto/SuccessResponse.java @@ -0,0 +1,3 @@ +package com.splanet.splanet.friendRequest.dto; + +public record SuccessResponse(String message) {} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friendRequest/entity/FriendRequest.java b/src/main/java/com/splanet/splanet/friendRequest/entity/FriendRequest.java index c89544ca..f4bb62a5 100644 --- a/src/main/java/com/splanet/splanet/friendRequest/entity/FriendRequest.java +++ b/src/main/java/com/splanet/splanet/friendRequest/entity/FriendRequest.java @@ -1,6 +1,8 @@ package com.splanet.splanet.friendRequest.entity; import com.splanet.splanet.core.BaseEntity; +import com.splanet.splanet.friend.entity.Friend; +import com.splanet.splanet.user.entity.User; import jakarta.persistence.*; import lombok.*; import lombok.experimental.SuperBuilder; @@ -13,11 +15,13 @@ @Entity public class FriendRequest extends BaseEntity { - @Column(name = "requester_id", nullable = false) - private Long requesterId; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "requester_id", nullable = false) + private User requester; - @Column(name = "receiver_id", nullable = false) - private Long receiverId; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "receiver_id", nullable = false) + private User receiver; @Enumerated(EnumType.STRING) @Column(name = "status") @@ -34,4 +38,11 @@ public enum Status { ACCEPTED, REJECTED } + + public Friend accept() { + return Friend.builder() + .user(requester) + .friend(receiver) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friendRequest/repository/FriendRequestRepository.java b/src/main/java/com/splanet/splanet/friendRequest/repository/FriendRequestRepository.java index 26560222..96ff90e3 100644 --- a/src/main/java/com/splanet/splanet/friendRequest/repository/FriendRequestRepository.java +++ b/src/main/java/com/splanet/splanet/friendRequest/repository/FriendRequestRepository.java @@ -2,8 +2,27 @@ import com.splanet.splanet.friendRequest.entity.FriendRequest; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface FriendRequestRepository extends JpaRepository { + + @Query("SELECT fr FROM FriendRequest fr JOIN FETCH fr.requester WHERE fr.receiver.id = :userId") + List findByReceiverIdWithRequester(@Param("userId") Long userId); + + @Query("SELECT fr FROM FriendRequest fr JOIN FETCH fr.receiver WHERE fr.requester.id = :userId") + List findByRequesterIdWithReceiver(@Param("userId") Long userId); + + @Query("SELECT fr FROM FriendRequest fr WHERE fr.receiver.id = :receiverId AND fr.requester.id = :requesterId AND fr.status = :status") + List findPendingRequestsByReceiverId(@Param("receiverId") Long receiverId, @Param("requesterId") Long requesterId, @Param("status") FriendRequest.Status status); + + @Query("SELECT fr FROM FriendRequest fr WHERE fr.receiver.id = :userId") + List findByReceiverId(@Param("userId") Long userId); + + @Query("SELECT fr FROM FriendRequest fr WHERE fr.requester.id = :userId") + List findByRequesterId(@Param("userId") Long userId); } \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/friendRequest/service/FriendRequestService.java b/src/main/java/com/splanet/splanet/friendRequest/service/FriendRequestService.java new file mode 100644 index 00000000..19f27554 --- /dev/null +++ b/src/main/java/com/splanet/splanet/friendRequest/service/FriendRequestService.java @@ -0,0 +1,213 @@ +package com.splanet.splanet.friendRequest.service; + +import com.splanet.splanet.core.exception.BusinessException; +import com.splanet.splanet.core.exception.ErrorCode; +import com.splanet.splanet.friend.entity.Friend; +import com.splanet.splanet.friend.repository.FriendRepository; +import com.splanet.splanet.friendRequest.dto.ReceivedFriendRequestResponse; +import com.splanet.splanet.friendRequest.dto.SentFriendRequestResponse; +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 org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class FriendRequestService { + + private final FriendRequestRepository friendRequestRepository; + private final FriendRepository friendRepository; + private final UserRepository userRepository; + + public FriendRequestService(FriendRequestRepository friendRequestRepository, UserRepository userRepository, FriendRepository friendRepository) { + this.friendRequestRepository = friendRequestRepository; + this.userRepository = userRepository; + this.friendRepository = friendRepository; + } + + // 친구 요청 전송 + public void sendFriendRequest(Long userId, Long receiverId) { + // 본인에게 요청 보낼 수 없음 + if (userId.equals(receiverId)) { + throw new BusinessException(ErrorCode.SELF_FRIEND_REQUEST_NOT_ALLOWED); + } + + // 요청자가 이미 친구 목록에 있는지 확인 + if (friendRepository.existsByUserIdAndFriendId(userId, receiverId)) { + throw new BusinessException(ErrorCode.FRIEND_ALREADY_EXISTS); + } + + // 이미 보낸 요청이 있는지 확인 + List existingRequests = friendRequestRepository.findPendingRequestsByReceiverId(receiverId, userId, FriendRequest.Status.PENDING); + if (!existingRequests.isEmpty()) { + throw new BusinessException(ErrorCode.FRIEND_REQUEST_ALREADY_SENT); + } + + User receiver = userRepository.findById(receiverId) + .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); + User requester = userRepository.findById(userId) + .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); + + FriendRequest friendRequest = FriendRequest.builder() + .requester(requester) + .receiver(receiver) + .status(FriendRequest.Status.PENDING) + .build(); + + friendRequestRepository.save(friendRequest); + } + + // 친구 요청 수락 + public ReceivedFriendRequestResponse acceptFriendRequest(Long requestId, Long userId) { + FriendRequest friendRequest = friendRequestRepository.findById(requestId) + .orElseThrow(() -> new BusinessException(ErrorCode.FRIEND_REQUEST_NOT_FOUND)); + + if (friendRequest.getStatus() != FriendRequest.Status.PENDING) { + throw new BusinessException(ErrorCode.FRIEND_REQUEST_ALREADY_ACCEPTED_OR_REJECTED); + } + + // 요청자 본인이 수락을 시도할 경우, 내가 보낸 요청 처리 할 수 없음 + if (friendRequest.getRequester().getId().equals(userId)) { + throw new BusinessException(ErrorCode.FRIEND_REQUEST_NOT_RECEIVER); + } + + // 내가 받은 요청이 아닐 경우, 예외처리 + if (!friendRequest.getReceiver().getId().equals(userId)) { + throw new BusinessException(ErrorCode.FRIEND_REQUEST_NOT_FOUND_IN_RECEIVED_LIST); + } + + List receivedRequests = getReceivedFriendRequests(userId); + + boolean isRequestPresent = receivedRequests.stream() + .anyMatch(request -> request.requesterId().equals(friendRequest.getRequester().getId())); + + // 받은 요청 목록에 없어도 예외처리 + if (!isRequestPresent) { + throw new BusinessException(ErrorCode.FRIEND_REQUEST_NOT_FOUND_IN_RECEIVED_LIST); + } + + FriendRequest updatedFriendRequest = FriendRequest.builder() + .id(friendRequest.getId()) + .requester(friendRequest.getRequester()) + .receiver(friendRequest.getReceiver()) + .status(FriendRequest.Status.ACCEPTED) + .createdAt(friendRequest.getCreatedAt()) + .updatedAt(LocalDateTime.now()) + .build(); + + friendRequestRepository.save(updatedFriendRequest); + + User requester = friendRequest.getRequester(); + User receiver = friendRequest.getReceiver(); + + Friend friend1 = Friend.builder() + .user(requester) + .friend(receiver) + .build(); + + Friend friend2 = Friend.builder() + .user(receiver) + .friend(requester) + .build(); + + friendRepository.save(friend1); + friendRepository.save(friend2); + + return new ReceivedFriendRequestResponse( + friendRequest.getId(), + requester.getId(), + requester.getNickname(), + friendRequest.getStatus().name(), + requester.getProfileImage() + ); + } + + // 친구 요청 거절 + public ReceivedFriendRequestResponse rejectFriendRequest(Long requestId, Long userId) { + FriendRequest friendRequest = friendRequestRepository.findById(requestId) + .orElseThrow(() -> new BusinessException(ErrorCode.FRIEND_REQUEST_NOT_FOUND)); + + if (friendRequest.getStatus() != FriendRequest.Status.PENDING) { + throw new BusinessException(ErrorCode.FRIEND_REQUEST_ALREADY_ACCEPTED_OR_REJECTED); + } + + // 요청자 본인이 거절을 시도할 경우, 내가 보낸 요청 처리 할 수 없음 + if (friendRequest.getRequester().getId().equals(userId)) { + throw new BusinessException(ErrorCode.FRIEND_REQUEST_NOT_RECEIVER); + } + + // 내가 받은 요청이 아닐 경우, 예외처리 + if (!friendRequest.getReceiver().getId().equals(userId)) { + throw new BusinessException(ErrorCode.FRIEND_REQUEST_NOT_FOUND_IN_RECEIVED_LIST); + } + + List receivedRequests = getReceivedFriendRequests(userId); + + boolean isRequestPresent = receivedRequests.stream() + .anyMatch(request -> request.requesterId().equals(friendRequest.getRequester().getId())); + + // 받은 요청 목록에 없어도 예외처리 + if (!isRequestPresent) { + throw new BusinessException(ErrorCode.FRIEND_REQUEST_NOT_FOUND_IN_RECEIVED_LIST); + } + + FriendRequest updatedFriendRequest = FriendRequest.builder() + .id(friendRequest.getId()) + .requester(friendRequest.getRequester()) + .receiver(friendRequest.getReceiver()) + .status(FriendRequest.Status.REJECTED) + .createdAt(friendRequest.getCreatedAt()) + .updatedAt(LocalDateTime.now()) + .build(); + + friendRequestRepository.save(updatedFriendRequest); + + User requester = friendRequest.getRequester(); + + return new ReceivedFriendRequestResponse( + friendRequest.getId(), + requester.getId(), + requester.getNickname(), + updatedFriendRequest.getStatus().name(), + requester.getProfileImage() + ); + } + + // 친구 요청 목록 조회(받은 요청) + public List getReceivedFriendRequests(Long userId) { + List requests = friendRequestRepository.findByReceiverIdWithRequester(userId); + + // PENDING인 요청만 + return requests.stream() + .filter(request -> request.getStatus() == FriendRequest.Status.PENDING) + .map(request -> new ReceivedFriendRequestResponse( + request.getId(), + request.getRequester().getId(), + request.getRequester().getNickname(), + request.getStatus().name(), + request.getRequester().getProfileImage() + )) + .collect(Collectors.toList()); + } + + // 친구 요청 목록 조회(보낸 요청) + public List getSentFriendRequests(Long userId) { + List requests = friendRequestRepository.findByRequesterIdWithReceiver(userId); + + // PENDING인 요청만 + return requests.stream() + .filter(request -> request.getStatus() == FriendRequest.Status.PENDING) + .map(request -> new SentFriendRequestResponse( + request.getId(), + request.getReceiver().getId(), + request.getReceiver().getNickname(), + request.getStatus().name(), + request.getRequester().getProfileImage() + )) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/com/splanet/splanet/plan/repository/PlanRepository.java b/src/main/java/com/splanet/splanet/plan/repository/PlanRepository.java index a3f67b6b..41e4604f 100644 --- a/src/main/java/com/splanet/splanet/plan/repository/PlanRepository.java +++ b/src/main/java/com/splanet/splanet/plan/repository/PlanRepository.java @@ -9,4 +9,5 @@ @Repository public interface PlanRepository extends JpaRepository { List findAllByUserId(Long userId); + List findAllByUserIdAndAccessibility(Long userId, Boolean accessibility); } diff --git a/src/test/java/com/splanet/splanet/friend/service/FriendServiceTest.java b/src/test/java/com/splanet/splanet/friend/service/FriendServiceTest.java new file mode 100644 index 00000000..e5b0f11f --- /dev/null +++ b/src/test/java/com/splanet/splanet/friend/service/FriendServiceTest.java @@ -0,0 +1,172 @@ +package com.splanet.splanet.friend.service; + +import com.splanet.splanet.core.exception.BusinessException; +import com.splanet.splanet.core.exception.ErrorCode; +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.plan.dto.PlanResponseDto; +import com.splanet.splanet.plan.entity.Plan; +import com.splanet.splanet.plan.repository.PlanRepository; +import com.splanet.splanet.user.entity.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.ResponseEntity; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class FriendServiceTest { + + @InjectMocks + private FriendService friendService; + + @Mock + private FriendRepository friendRepository; + + @Mock + private PlanRepository planRepository; + + @Mock + private User mockUser; + + @Mock + private Friend mockFriend; + + @Mock + private Plan mockPlan; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void 친구목록조회_성공() { + // Arrange + when(mockFriend.getFriend()).thenReturn(mockUser); + when(mockUser.getNickname()).thenReturn("testUser"); + when(mockUser.getProfileImage()).thenReturn("testProfileImageUrl"); + when(friendRepository.findByUserId(1L)).thenReturn(Collections.singletonList(mockFriend)); + + // Act + List friends = friendService.getFriends(1L); + + // Assert + assertNotNull(friends); + assertEquals(1, friends.size()); + assertEquals("testUser", friends.get(0).nickname()); + assertEquals("testProfileImageUrl", friends.get(0).profileImage()); + } + + @Test + void 친구플랜조회_성공() { + // Arrange + LocalDateTime startDate = LocalDateTime.of(2024, 1, 1, 0, 0); + LocalDateTime endDate = LocalDateTime.of(2024, 12, 31, 0, 0); + + // 친구 확인 모킹 + when(friendRepository.existsByUserIdAndFriendId(1L, 1L)).thenReturn(true); + + when(mockPlan.getId()).thenReturn(1L); + when(mockPlan.getTitle()).thenReturn("Test Plan"); + when(mockPlan.getDescription()).thenReturn("This is a test plan."); + when(mockPlan.getStartDate()).thenReturn(startDate); + when(mockPlan.getEndDate()).thenReturn(endDate); + when(mockPlan.getAccessibility()).thenReturn(true); + when(mockPlan.getIsCompleted()).thenReturn(false); + when(mockPlan.getCreatedAt()).thenReturn(null); + when(mockPlan.getUpdatedAt()).thenReturn(null); + when(planRepository.findAllByUserIdAndAccessibility(1L, true)).thenReturn(Collections.singletonList(mockPlan)); + + // Act + ResponseEntity> response = friendService.getFriendPlan(1L, 1L); + + // Assert + assertNotNull(response); + assertEquals(200, response.getStatusCodeValue()); + assertEquals(1, response.getBody().size()); + assertEquals("Test Plan", response.getBody().get(0).getTitle()); + } + + @Test + void 친구플랜조회_성공_플랜없음() { + // Arrange + when(friendRepository.existsByUserIdAndFriendId(1L, 1L)).thenReturn(true); // 친구 확인 성공 + when(planRepository.findAllByUserIdAndAccessibility(1L, true)).thenReturn(Collections.emptyList()); // 플랜 없음 + + // Act + ResponseEntity> response = friendService.getFriendPlan(1L, 1L); + + // Assert + assertNotNull(response); + assertEquals(200, response.getStatusCodeValue()); // 성공적으로 빈 목록을 반환해야 함 + assertTrue(response.getBody().isEmpty()); // 빈 목록인지 확인 + } + + @Test + void 친구플랜조회_성공_여러플랜() { + // Arrange + Plan mockPlan1 = mock(Plan.class); + Plan mockPlan2 = mock(Plan.class); + + when(friendRepository.existsByUserIdAndFriendId(1L, 1L)).thenReturn(true); + when(mockPlan1.getTitle()).thenReturn("Plan 1"); + when(mockPlan2.getTitle()).thenReturn("Plan 2"); + when(planRepository.findAllByUserIdAndAccessibility(1L, true)).thenReturn(List.of(mockPlan1, mockPlan2)); + + // Act + ResponseEntity> response = friendService.getFriendPlan(1L, 1L); + + // Assert + assertNotNull(response); + assertEquals(200, response.getStatusCodeValue()); + assertEquals(2, response.getBody().size()); + assertEquals("Plan 1", response.getBody().get(0).getTitle()); + assertEquals("Plan 2", response.getBody().get(1).getTitle()); + } + + @Test + void 친구플랜조회_실패_친구아님() { + // Arrange + when(friendRepository.existsByUserIdAndFriendId(1L, 2L)).thenReturn(false); // 친구가 아닌 경우 + + // Act & Assert + BusinessException exception = assertThrows(BusinessException.class, () -> { + friendService.getFriendPlan(2L, 1L); // 다른 userId로 조회 + }); + assertEquals(ErrorCode.FRIEND_NOT_FOUND, exception.getErrorCode()); + } + + @Test + void 친구목록조회_실패_사용자없음() { + // Arrange + when(friendRepository.findByUserId(99L)).thenReturn(Collections.emptyList()); // 존재하지 않는 사용자 + + // Act + List friends = friendService.getFriends(99L); + + // Assert + assertNotNull(friends); + assertTrue(friends.isEmpty()); // 빈 목록이 반환되어야 함 + } + + @Test + void 친구플랜조회_실패_친구관계없음() { + // Arrange + when(friendRepository.existsByUserIdAndFriendId(1L, 2L)).thenReturn(false); // 친구 관계가 없음 + + // Act & Assert + BusinessException exception = assertThrows(BusinessException.class, () -> { + friendService.getFriendPlan(1L, 2L); + }); + assertEquals(ErrorCode.FRIEND_NOT_FOUND, exception.getErrorCode()); + } +} \ No newline at end of file diff --git a/src/test/java/com/splanet/splanet/friendRequest/service/FriendRequestServiceTest.java b/src/test/java/com/splanet/splanet/friendRequest/service/FriendRequestServiceTest.java new file mode 100644 index 00000000..e07e84f8 --- /dev/null +++ b/src/test/java/com/splanet/splanet/friendRequest/service/FriendRequestServiceTest.java @@ -0,0 +1,234 @@ +package com.splanet.splanet.friendRequest.service; + +import com.splanet.splanet.core.exception.BusinessException; +import com.splanet.splanet.core.exception.ErrorCode; +import com.splanet.splanet.friend.repository.FriendRepository; +import com.splanet.splanet.friendRequest.dto.ReceivedFriendRequestResponse; +import com.splanet.splanet.friendRequest.dto.SentFriendRequestResponse; +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 org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class FriendRequestServiceTest { + + @Mock + private FriendRequestRepository friendRequestRepository; + + @Mock + private FriendRepository friendRepository; + + @Mock + private UserRepository userRepository; + + @InjectMocks + private FriendRequestService friendRequestService; + + private User requester; + private User receiver; + private FriendRequest friendRequest; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + + requester = User.builder() + .id(1L) + .nickname("요청자") + .profileImage("requester.png") + .build(); + + receiver = User.builder() + .id(2L) + .nickname("수락자") + .profileImage("receiver.png") + .build(); + + friendRequest = FriendRequest.builder() + .id(1L) + .requester(requester) + .receiver(receiver) + .status(FriendRequest.Status.PENDING) + .build(); + } + + @Test + void 친구요청전송_성공() { + Long userId = requester.getId(); + Long receiverId = receiver.getId(); + + when(userRepository.findById(receiverId)).thenReturn(Optional.of(receiver)); + when(userRepository.findById(userId)).thenReturn(Optional.of(requester)); + when(friendRepository.existsByUserIdAndFriendId(userId, receiverId)).thenReturn(false); + + friendRequestService.sendFriendRequest(userId, receiverId); + + verify(friendRequestRepository, times(1)).save(any(FriendRequest.class)); + } + + @Test + void 친구요청전송_본인에게요청() { + Long userId = requester.getId(); + + BusinessException exception = assertThrows(BusinessException.class, () -> + friendRequestService.sendFriendRequest(userId, userId) + ); + + assertEquals(ErrorCode.SELF_FRIEND_REQUEST_NOT_ALLOWED, exception.getErrorCode()); + } + + @Test + void 친구요청수락_요청없음() { + Long requestId = 1L; + + when(friendRequestRepository.findById(requestId)).thenReturn(Optional.empty()); + + BusinessException exception = assertThrows(BusinessException.class, () -> + friendRequestService.acceptFriendRequest(requestId, receiver.getId()) + ); + + assertEquals(ErrorCode.FRIEND_REQUEST_NOT_FOUND, exception.getErrorCode()); + } + + @Test + void 친구요청수락_요청자와수락자가동일() { + Long requestId = friendRequest.getId(); + when(friendRequestRepository.findById(requestId)).thenReturn(Optional.of(friendRequest)); + + BusinessException exception = assertThrows(BusinessException.class, () -> + friendRequestService.acceptFriendRequest(requestId, requester.getId()) + ); + + assertEquals(ErrorCode.FRIEND_REQUEST_NOT_RECEIVER, exception.getErrorCode()); + } + + @Test + void 친구요청거절_요청자와수락자가동일() { + Long requestId = friendRequest.getId(); + when(friendRequestRepository.findById(requestId)).thenReturn(Optional.of(friendRequest)); + + BusinessException exception = assertThrows(BusinessException.class, () -> + friendRequestService.rejectFriendRequest(requestId, requester.getId()) + ); + + assertEquals(ErrorCode.FRIEND_REQUEST_NOT_RECEIVER, exception.getErrorCode()); + } + + @Test + void 친구요청목록조회_받은요청() { + Long userId = receiver.getId(); + + when(friendRequestRepository.findByReceiverIdWithRequester(userId)).thenReturn(Arrays.asList(friendRequest)); + + List responses = friendRequestService.getReceivedFriendRequests(userId); + + assertEquals(1, responses.size()); + assertEquals(requester.getId(), responses.get(0).requesterId()); + } + + @Test + void 친구요청목록조회_보낸요청() { + Long userId = requester.getId(); + + when(friendRequestRepository.findByRequesterIdWithReceiver(userId)).thenReturn(Arrays.asList(friendRequest)); + + List responses = friendRequestService.getSentFriendRequests(userId); + + assertEquals(1, responses.size()); + assertEquals(receiver.getId(), responses.get(0).receiverId()); + } + + @Test + void 친구요청목록조회_받은요청없음() { + Long userId = receiver.getId(); + + when(friendRequestRepository.findByReceiverIdWithRequester(userId)).thenReturn(Collections.emptyList()); + + List responses = friendRequestService.getReceivedFriendRequests(userId); + + assertEquals(0, responses.size()); // 빈 목록인지 확인 + } + + @Test + void 친구요청목록조회_보낸요청없음() { + Long userId = requester.getId(); + + when(friendRequestRepository.findByRequesterId(userId)).thenReturn(Collections.emptyList()); + + List responses = friendRequestService.getSentFriendRequests(userId); + + assertEquals(0, responses.size()); // 빈 목록인지 확인 + } + + @Test + void 친구요청전송_수신자존재하지않음() { + Long userId = requester.getId(); + Long receiverId = 999L; + + when(userRepository.findById(receiverId)).thenReturn(Optional.empty()); + + BusinessException exception = assertThrows(BusinessException.class, () -> + friendRequestService.sendFriendRequest(userId, receiverId) + ); + + assertEquals(ErrorCode.USER_NOT_FOUND, exception.getErrorCode()); + } + + @Test + void 친구요청전송_이미친구인경우() { + Long userId = requester.getId(); + Long receiverId = receiver.getId(); + + when(userRepository.findById(receiverId)).thenReturn(Optional.of(receiver)); + when(userRepository.findById(userId)).thenReturn(Optional.of(requester)); + when(friendRepository.existsByUserIdAndFriendId(userId, receiverId)).thenReturn(true); // 이미 친구인 경우 + + BusinessException exception = assertThrows(BusinessException.class, () -> + friendRequestService.sendFriendRequest(userId, receiverId) + ); + + assertEquals(ErrorCode.FRIEND_ALREADY_EXISTS, exception.getErrorCode()); + } + + @Test + void 친구요청수락_이미수락된요청() { + Long requestId = friendRequest.getId(); + friendRequest.setStatus(FriendRequest.Status.ACCEPTED); + + when(friendRequestRepository.findById(requestId)).thenReturn(Optional.of(friendRequest)); + + BusinessException exception = assertThrows(BusinessException.class, () -> + friendRequestService.acceptFriendRequest(requestId, receiver.getId()) + ); + + assertEquals(ErrorCode.FRIEND_REQUEST_ALREADY_ACCEPTED_OR_REJECTED, exception.getErrorCode()); + } + + @Test + void 친구요청수락_이미거절된요청() { + Long requestId = friendRequest.getId(); + friendRequest.setStatus(FriendRequest.Status.REJECTED); + + when(friendRequestRepository.findById(requestId)).thenReturn(Optional.of(friendRequest)); + + BusinessException exception = assertThrows(BusinessException.class, () -> + friendRequestService.acceptFriendRequest(requestId, receiver.getId()) + ); + + assertEquals(ErrorCode.FRIEND_REQUEST_ALREADY_ACCEPTED_OR_REJECTED, exception.getErrorCode()); + } +} \ No newline at end of file