diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/controller/MentoringController.java b/back/src/main/java/com/back/domain/mentoring/mentoring/controller/MentoringController.java index 3c72bdad..3bd0964d 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/controller/MentoringController.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/controller/MentoringController.java @@ -17,6 +17,8 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @RequestMapping("/mentorings") @RequiredArgsConstructor @@ -43,6 +45,20 @@ public RsData getMentorings( ); } + @GetMapping("/my") + @PreAuthorize("hasRole('MENTOR')") + @Operation(summary = "나의 멘토링 목록 조회", description = "나의 멘토링 목록을 조회합니다. 로그인한 멘토만 접근할 수 있습니다.") + public RsData> getMyMentorings() { + Mentor mentor = memberStorage.findMentorByMember(rq.getActor()); + List resDto = mentoringService.getMyMentorings(mentor); + + return new RsData<>( + "200", + "나의 멘토링 목록을 조회하였습니다.", + resDto + ); + } + @GetMapping("/{mentoringId}") @Operation(summary = "멘토링 상세 조회", description = "특정 멘토링을 상세 조회합니다.") public RsData getMentoring( diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/repository/MentoringRepository.java b/back/src/main/java/com/back/domain/mentoring/mentoring/repository/MentoringRepository.java index bc4d2a7a..13424f69 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/repository/MentoringRepository.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/repository/MentoringRepository.java @@ -8,6 +8,7 @@ public interface MentoringRepository extends JpaRepository, MentoringRepositoryCustom { List findByMentorId(Long mentorId); + List findByMentorIdOrderByIdDesc(Long mentorId); Optional findTopByOrderByIdDesc(); boolean existsByMentorIdAndTitle(Long mentorId, String title); boolean existsByMentorIdAndTitleAndIdNot(Long mentorId, String title, Long MentoringId); diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/service/MentoringService.java b/back/src/main/java/com/back/domain/mentoring/mentoring/service/MentoringService.java index 7a5a4984..545a00d0 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/service/MentoringService.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/service/MentoringService.java @@ -39,6 +39,14 @@ public Page getMentorings(String keyword, int page, int si .map(MentoringWithTagsDto::from); } + @Transactional(readOnly = true) + public List getMyMentorings(Mentor mentor) { + return mentoringRepository.findByMentorIdOrderByIdDesc(mentor.getId()) + .stream() + .map(MentoringWithTagsDto::from) + .toList(); + } + @Transactional(readOnly = true) public MentoringResponse getMentoring(Long mentoringId) { Mentoring mentoring = mentoringStorage.findMentoring(mentoringId); diff --git a/back/src/main/java/com/back/domain/mentoring/session/controller/MentoringSessionController.java b/back/src/main/java/com/back/domain/mentoring/session/controller/MentoringSessionController.java index 157d7dea..4dfd085d 100644 --- a/back/src/main/java/com/back/domain/mentoring/session/controller/MentoringSessionController.java +++ b/back/src/main/java/com/back/domain/mentoring/session/controller/MentoringSessionController.java @@ -19,9 +19,10 @@ public class MentoringSessionController { private final MentoringSessionManager mentoringSessionManager; private final Rq rq; - @GetMapping("/{sessionId}/url") + @GetMapping("/url") @Operation(summary = "세션참여 URL 발급", description = "세션 참여를 위한 URL을 발급합니다.") - public RsData getSessionUrl(@PathVariable Long sessionId) { + public RsData getSessionUrl(@RequestParam Long sessionId) { + GetSessionUrlResponse response = mentoringSessionManager.getSessionUrl(sessionId); return new RsData<>("200", "요청완료", response); } diff --git a/back/src/main/java/com/back/domain/mentoring/session/controller/WebRtcController.java b/back/src/main/java/com/back/domain/mentoring/session/controller/WebRtcController.java new file mode 100644 index 00000000..8b8fecfd --- /dev/null +++ b/back/src/main/java/com/back/domain/mentoring/session/controller/WebRtcController.java @@ -0,0 +1,36 @@ +package com.back.domain.mentoring.session.controller; + +import com.back.domain.mentoring.session.dto.WebRtcSignalingMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.simp.SimpMessageSendingOperations; +import org.springframework.stereotype.Controller; + +import java.security.Principal; + +@Slf4j +@Controller +@RequiredArgsConstructor +public class WebRtcController { + + private final SimpMessageSendingOperations messagingTemplate; + + @MessageMapping("/signal") + public void handleSignalingMessage(WebRtcSignalingMessage message, Principal principal) { + String senderUsername = principal.getName(); + + WebRtcSignalingMessage finalMessage = new WebRtcSignalingMessage(message.type(), senderUsername, message.to(), message.sessionId(), message.payload()); + + log.debug("[Signal] type: {}, from: {}, to: {}, sessionId: {}", + finalMessage.type(), finalMessage.from(), finalMessage.to(), finalMessage.sessionId()); + + // 'to' 필드가 있으면 특정 사용자에게, 없으면 세션 전체에 브로드캐스트 + if (finalMessage.to() != null && !finalMessage.to().isEmpty()) { + String userSpecificTopic = "/topic/signal/user/" + finalMessage.to(); + messagingTemplate.convertAndSend(userSpecificTopic, finalMessage); + } else { + messagingTemplate.convertAndSend("/topic/signal/room/" + finalMessage.sessionId(), finalMessage); + } + } +} diff --git a/back/src/main/java/com/back/domain/mentoring/session/dto/WebRtcSignalingMessage.java b/back/src/main/java/com/back/domain/mentoring/session/dto/WebRtcSignalingMessage.java new file mode 100644 index 00000000..fa4ef283 --- /dev/null +++ b/back/src/main/java/com/back/domain/mentoring/session/dto/WebRtcSignalingMessage.java @@ -0,0 +1,10 @@ +package com.back.domain.mentoring.session.dto; + +public record WebRtcSignalingMessage( + String type, + String from, + String to, + String sessionId, + Object payload +) { +} diff --git a/back/src/main/java/com/back/domain/mentoring/session/entity/ChatMessage.java b/back/src/main/java/com/back/domain/mentoring/session/entity/ChatMessage.java index 5268b369..7c05269e 100644 --- a/back/src/main/java/com/back/domain/mentoring/session/entity/ChatMessage.java +++ b/back/src/main/java/com/back/domain/mentoring/session/entity/ChatMessage.java @@ -46,6 +46,39 @@ private ChatMessage(MentoringSession mentoringSession, Member sender, SenderRole } public static ChatMessage create(MentoringSession mentoringSession, Member sender, SenderRole senderRole, String content, MessageType type) { + if (mentoringSession == null) { + throw new IllegalArgumentException("MentoringSession은 null일 수 없습니다."); + } + if (content == null || content.isBlank()) { + throw new IllegalArgumentException("Content는 null이거나 비어 있을 수 없습니다."); + } + if (type == null) { + throw new IllegalArgumentException("MessageType은 null일 수 없습니다."); + } + + if (type == MessageType.SYSTEM) { + if (sender != null) { + throw new IllegalArgumentException("시스템 메시지의 sender는 null이어야 합니다."); + } + if (senderRole != SenderRole.SYSTEM) { + throw new IllegalArgumentException("시스템 메시지의 senderRole은 SYSTEM이어야 합니다."); + } + } else { // TEXT, IMAGE, FILE + if (sender == null) { + throw new IllegalArgumentException("일반 메시지의 sender는 null일 수 없습니다."); + } + if (senderRole != SenderRole.MENTOR && senderRole != SenderRole.MENTEE) { + throw new IllegalArgumentException("일반 메시지의 senderRole은 MENTOR 또는 MENTEE여야 합니다."); + } + + boolean isParticipant = (mentoringSession.getReservation().getMentor().isMember(sender) || + mentoringSession.getReservation().getMentee().isMember(sender)); + + if (!isParticipant) { + throw new IllegalArgumentException("메시지 발신자는 해당 멘토링 세션의 참여자가 아닙니다."); + } + } + return ChatMessage.builder() .mentoringSession(mentoringSession) .sender(sender) diff --git a/back/src/main/java/com/back/domain/mentoring/session/entity/MentoringSession.java b/back/src/main/java/com/back/domain/mentoring/session/entity/MentoringSession.java index 1dafe206..91a04604 100644 --- a/back/src/main/java/com/back/domain/mentoring/session/entity/MentoringSession.java +++ b/back/src/main/java/com/back/domain/mentoring/session/entity/MentoringSession.java @@ -34,8 +34,6 @@ public class MentoringSession extends BaseEntity { @OneToMany(mappedBy = "mentoringSession", cascade = CascadeType.ALL) private List chatMessages = new ArrayList<>(); - // 화면 공유, WebRTC 관련 필드 등 추가 가능 - @Builder(access = AccessLevel.PRIVATE) private MentoringSession(Reservation reservation) { this.sessionUrl = java.util.UUID.randomUUID().toString(); diff --git a/back/src/main/java/com/back/domain/mentoring/slot/dto/request/MentorSlotRepetitionRequest.java b/back/src/main/java/com/back/domain/mentoring/slot/dto/request/MentorSlotRepetitionRequest.java index bb7ee76a..4ba24fae 100644 --- a/back/src/main/java/com/back/domain/mentoring/slot/dto/request/MentorSlotRepetitionRequest.java +++ b/back/src/main/java/com/back/domain/mentoring/slot/dto/request/MentorSlotRepetitionRequest.java @@ -23,14 +23,14 @@ public record MentorSlotRepetitionRequest( @NotEmpty List daysOfWeek, - @Schema(description = "시작 시간", example = "HH:mm:ss") + @Schema(description = "시작 시간", example = "HH:mm") @NotNull - @JsonFormat(pattern = "HH:mm:ss") + @JsonFormat(pattern = "HH:mm") LocalTime startTime, - @Schema(description = "종료 시간", example = "HH:mm:ss") + @Schema(description = "종료 시간", example = "HH:mm") @NotNull - @JsonFormat(pattern = "HH:mm:ss") + @JsonFormat(pattern = "HH:mm") LocalTime endTime ){ } diff --git a/back/src/main/java/com/back/domain/mentoring/slot/dto/request/MentorSlotRequest.java b/back/src/main/java/com/back/domain/mentoring/slot/dto/request/MentorSlotRequest.java index 109ffa28..3245797d 100644 --- a/back/src/main/java/com/back/domain/mentoring/slot/dto/request/MentorSlotRequest.java +++ b/back/src/main/java/com/back/domain/mentoring/slot/dto/request/MentorSlotRequest.java @@ -10,11 +10,11 @@ public record MentorSlotRequest( @NotNull Long mentorId, - @Schema(description = "시작 일시", example = "yyyy-MM-ddTHH:mm:ss") + @Schema(description = "시작 일시", example = "yyyy-MM-ddTHH:mm") @NotNull LocalDateTime startDateTime, - @Schema(description = "종료 일시", example = "yyyy-MM-ddTHH:mm:ss") + @Schema(description = "종료 일시", example = "yyyy-MM-ddTHH:mm") @NotNull LocalDateTime endDateTime ) { diff --git a/back/src/main/java/com/back/domain/news/comment/controller/CommentController.java b/back/src/main/java/com/back/domain/news/comment/controller/NewsCommentController.java similarity index 99% rename from back/src/main/java/com/back/domain/news/comment/controller/CommentController.java rename to back/src/main/java/com/back/domain/news/comment/controller/NewsCommentController.java index d4ac7d7b..1f8e6938 100644 --- a/back/src/main/java/com/back/domain/news/comment/controller/CommentController.java +++ b/back/src/main/java/com/back/domain/news/comment/controller/NewsCommentController.java @@ -21,7 +21,7 @@ @RestController @RequestMapping("/news/{newsId}/comment") @RequiredArgsConstructor -public class CommentController { +public class NewsCommentController { private final NewsService newsService; private final NewsCommentService newsCommentService; private final Rq rq; diff --git a/back/src/test/java/com/back/domain/mentoring/mentoring/service/MentoringServiceTest.java b/back/src/test/java/com/back/domain/mentoring/mentoring/service/MentoringServiceTest.java index fbde5117..c416080f 100644 --- a/back/src/test/java/com/back/domain/mentoring/mentoring/service/MentoringServiceTest.java +++ b/back/src/test/java/com/back/domain/mentoring/mentoring/service/MentoringServiceTest.java @@ -143,6 +143,25 @@ void returnEmptyPage() { } } + @Test + @DisplayName("나의 멘토링 목록 조회") + void getMentorings() { + // given + Mentoring mentoring2 = MentoringFixture.create(2L, mentor2); + + List mentorings = List.of(mentoring1); + + when(mentoringRepository.findByMentorIdOrderByIdDesc(mentor1.getId())) + .thenReturn(mentorings); + + // when + List result = mentoringService.getMyMentorings(mentor1); + + // then + assertThat(result).hasSize(1); + verify(mentoringRepository).findByMentorIdOrderByIdDesc(mentor1.getId()); + } + @Nested @DisplayName("멘토링 조회") class Describe_getMentoring { diff --git a/back/src/test/java/com/back/domain/mentoring/session/entity/ChatMessageTest.java b/back/src/test/java/com/back/domain/mentoring/session/entity/ChatMessageTest.java new file mode 100644 index 00000000..81bd15d1 --- /dev/null +++ b/back/src/test/java/com/back/domain/mentoring/session/entity/ChatMessageTest.java @@ -0,0 +1,137 @@ +package com.back.domain.mentoring.session.entity; + + +import com.back.domain.member.member.entity.Member; +import com.back.fixture.MemberFixture; +import com.back.fixture.mentoring.MentoringSessionFixture; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ChatMessageTest { + @Test + @DisplayName("ChatMessage 생성 테스트") + void createChatMessageTest() { + // given + MentoringSession session = MentoringSessionFixture.createDefault(); + Member sender = session.getReservation().getMentor().getMember(); + String content = "Hello world"; + SenderRole senderRole = SenderRole.MENTOR; + MessageType type = MessageType.TEXT; + + // when + ChatMessage message = ChatMessage.create(session, sender, senderRole, content, type); + + // then + assertThat(message.getMentoringSession()).isEqualTo(session); + assertThat(message.getSender()).isEqualTo(sender); + assertThat(message.getSenderRole()).isEqualTo(senderRole); + assertThat(message.getContent()).isEqualTo(content); + assertThat(message.getType()).isEqualTo(type); + assertThat(message.getTimestamp()).isNotNull(); + assertThat(message.getTimestamp()).isBeforeOrEqualTo(LocalDateTime.now()); + } + + @Test + @DisplayName("시스템 메시지 생성 테스트") + void createSystemMessageTest() { + // given + MentoringSession session = MentoringSessionFixture.createDefault(); + String content = "멘토링 세션이 시작되었습니다."; + MessageType type = MessageType.SYSTEM; + SenderRole senderRole = SenderRole.SYSTEM; + Member sender = null; // 시스템 메시지는 발신자가 없을 수 있습니다. + + // when + ChatMessage message = ChatMessage.create(session, sender, senderRole, content, type); + + // then + assertThat(message.getMentoringSession()).isEqualTo(session); + assertThat(message.getSender()).isNull(); + assertThat(message.getSenderRole()).isEqualTo(senderRole); + assertThat(message.getContent()).isEqualTo(content); + assertThat(message.getType()).isEqualTo(type); + assertThat(message.getTimestamp()).isNotNull(); + assertThat(message.getTimestamp()).isBeforeOrEqualTo(LocalDateTime.now()); + } + + @Nested + @DisplayName("ChatMessage 생성 유효성 검증") + class ValidationTests { + + @Test + @DisplayName("MentoringSession이 null이면 예외가 발생한다") + void createWithNullSession_shouldThrowException() { + assertThrows(IllegalArgumentException.class, () -> + ChatMessage.create(null, MemberFixture.createDefault(), SenderRole.MENTOR, "message", MessageType.TEXT)); + } + + @Test + @DisplayName("Content가 null이면 예외가 발생한다") + void createWithNullContent_shouldThrowException() { + assertThrows(IllegalArgumentException.class, () -> + ChatMessage.create(MentoringSessionFixture.createDefault(), MemberFixture.createDefault(), SenderRole.MENTOR, null, MessageType.TEXT)); + } + + @Test + @DisplayName("Content가 비어있으면 예외가 발생한다") + void createWithBlankContent_shouldThrowException() { + assertThrows(IllegalArgumentException.class, () -> + ChatMessage.create(MentoringSessionFixture.createDefault(), MemberFixture.createDefault(), SenderRole.MENTOR, " ", MessageType.TEXT)); + } + + @Test + @DisplayName("MessageType이 null이면 예외가 발생한다") + void createWithNullType_shouldThrowException() { + assertThrows(IllegalArgumentException.class, () -> + ChatMessage.create(MentoringSessionFixture.createDefault(), MemberFixture.createDefault(), SenderRole.MENTOR, "message", null)); + } + + @Test + @DisplayName("시스템 메시지에 sender가 있으면 예외가 발생한다") + void createSystemMessageWithSender_shouldThrowException() { + assertThrows(IllegalArgumentException.class, () -> + ChatMessage.create(MentoringSessionFixture.createDefault(), MemberFixture.createDefault(), SenderRole.SYSTEM, "system message", MessageType.SYSTEM)); + } + + @Test + @DisplayName("시스템 메시지의 senderRole이 SYSTEM이 아니면 예외가 발생한다") + void createSystemMessageWithInvalidRole_shouldThrowException() { + assertThrows(IllegalArgumentException.class, () -> + ChatMessage.create(MentoringSessionFixture.createDefault(), null, SenderRole.MENTOR, "system message", MessageType.SYSTEM)); + } + + @Test + @DisplayName("일반 메시지에 sender가 없으면 예외가 발생한다") + void createUserMessageWithNullSender_shouldThrowException() { + assertThrows(IllegalArgumentException.class, () -> + ChatMessage.create(MentoringSessionFixture.createDefault(), null, SenderRole.MENTOR, "user message", MessageType.TEXT)); + } + + @Test + @DisplayName("일반 메시지의 senderRole이 MENTOR나 MENTEE가 아니면 예외가 발생한다") + void createUserMessageWithInvalidRole_shouldThrowException() { + assertThrows(IllegalArgumentException.class, () -> + ChatMessage.create(MentoringSessionFixture.createDefault(), MemberFixture.createDefault(), SenderRole.SYSTEM, "user message", MessageType.TEXT)); + } + + @Test + @DisplayName("메시지 발신자가 멘토링 참여자가 아니면 예외가 발생한다") + void createWithNonParticipantSender_shouldThrowException() { + // given + MentoringSession session = MentoringSessionFixture.createDefault(); + Member nonParticipant = MemberFixture.create(99L, "non@participant.com", "외부인", "password", Member.Role.MENTEE); + + // when & then + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> + ChatMessage.create(session, nonParticipant, SenderRole.MENTOR, "some message", MessageType.TEXT)); + + assertThat(exception.getMessage()).isEqualTo("메시지 발신자는 해당 멘토링 세션의 참여자가 아닙니다."); + } + } +} \ No newline at end of file diff --git a/back/src/test/java/com/back/domain/mentoring/session/service/ChatMessageServiceTest.java b/back/src/test/java/com/back/domain/mentoring/session/service/ChatMessageServiceTest.java new file mode 100644 index 00000000..d325341a --- /dev/null +++ b/back/src/test/java/com/back/domain/mentoring/session/service/ChatMessageServiceTest.java @@ -0,0 +1,86 @@ +package com.back.domain.mentoring.session.service; + +import com.back.domain.member.member.entity.Member; +import com.back.domain.member.mentee.entity.Mentee; +import com.back.domain.mentoring.reservation.entity.Reservation; +import com.back.domain.mentoring.session.dto.ChatMessageRequest; +import com.back.domain.mentoring.session.dto.ChatMessageResponse; +import com.back.domain.mentoring.session.entity.ChatMessage; +import com.back.domain.mentoring.session.entity.MentoringSession; +import com.back.domain.mentoring.session.entity.MessageType; +import com.back.domain.mentoring.session.repository.ChatMessageRepository; +import com.back.fixture.MemberFixture; +import com.back.fixture.MenteeFixture; +import com.back.fixture.mentoring.MentoringSessionFixture; +import com.back.fixture.mentoring.ReservationFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class ChatMessageServiceTest { + + @Mock + private ChatMessageRepository chatMessageRepository; + + @InjectMocks + private ChatMessageService chatMessageService; + + private Member menteeMember; + private MentoringSession mentoringSession; + + @BeforeEach + void setUp() { + // Reservation에 포함된 실제 멘티 멤버를 가져와서 사용 + Reservation reservation = ReservationFixture.createDefault(); + menteeMember = reservation.getMentee().getMember(); + mentoringSession = MentoringSessionFixture.create(reservation); + } + + @Test + @DisplayName("채팅 메시지를 생성하고 저장한다.") + void createAndSaveChatMessage() { + // given + String content = "안녕하세요!"; + MessageType messageType = MessageType.TEXT; + ChatMessage chatMessage = ChatMessage.create(mentoringSession, menteeMember, menteeMember.getRole() == Member.Role.MENTOR ? com.back.domain.mentoring.session.entity.SenderRole.MENTOR : com.back.domain.mentoring.session.entity.SenderRole.MENTEE, content, messageType); + given(chatMessageRepository.save(any(ChatMessage.class))).willReturn(chatMessage); + + // when + ChatMessage result = chatMessageService.create(mentoringSession, menteeMember, content, messageType); + + // then + assertThat(result).isNotNull(); + assertThat(result.getContent()).isEqualTo(content); + assertThat(result.getSender()).isEqualTo(menteeMember); + verify(chatMessageRepository).save(any(ChatMessage.class)); + } + + @Test + @DisplayName("채팅 메시지를 저장하고 처리하여 응답 DTO를 반환한다.") + void saveAndProcessMessage_returnsResponseDTO() { + // given + ChatMessageRequest request = new ChatMessageRequest(MessageType.TEXT, "테스트 메시지"); + ChatMessage chatMessage = ChatMessage.create(mentoringSession, menteeMember, menteeMember.getRole() == Member.Role.MENTOR ? com.back.domain.mentoring.session.entity.SenderRole.MENTOR : com.back.domain.mentoring.session.entity.SenderRole.MENTEE, request.content(), request.type()); + + given(chatMessageRepository.save(any(ChatMessage.class))).willReturn(chatMessage); + + // when + ChatMessageResponse response = chatMessageService.saveAndProcessMessage(menteeMember, mentoringSession, request); + + // then + assertThat(response).isNotNull(); + assertThat(response.senderName()).isEqualTo(menteeMember.getNickname()); + assertThat(response.content()).isEqualTo(request.content()); + assertThat(response.createdAt()).isEqualTo(chatMessage.getTimestamp()); + } +} \ No newline at end of file diff --git a/back/src/test/java/com/back/domain/mentoring/session/service/MentoringSessionManagerTest.java b/back/src/test/java/com/back/domain/mentoring/session/service/MentoringSessionManagerTest.java new file mode 100644 index 00000000..813cdfed --- /dev/null +++ b/back/src/test/java/com/back/domain/mentoring/session/service/MentoringSessionManagerTest.java @@ -0,0 +1,152 @@ +package com.back.domain.mentoring.session.service; + +import com.back.domain.member.member.entity.Member; +import com.back.domain.member.member.service.MemberStorage; +import com.back.domain.member.mentee.entity.Mentee; +import com.back.domain.member.mentor.entity.Mentor; +import com.back.domain.mentoring.mentoring.entity.Mentoring; +import com.back.domain.mentoring.reservation.constant.ReservationStatus; +import com.back.domain.mentoring.reservation.entity.Reservation; +import com.back.domain.mentoring.session.dto.*; +import com.back.domain.mentoring.session.entity.MentoringSession; +import com.back.domain.mentoring.slot.constant.MentorSlotStatus; +import com.back.domain.mentoring.slot.entity.MentorSlot; +import com.back.fixture.MemberFixture; +import com.back.fixture.MenteeFixture; +import com.back.fixture.MentorFixture; +import com.back.fixture.mentoring.MentoringFixture; +import com.back.fixture.mentoring.MentoringSessionFixture; +import com.back.fixture.mentoring.MentorSlotFixture; +import com.back.fixture.mentoring.ReservationFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class MentoringSessionManagerTest { + + @Mock + private MentoringSessionService mentoringSessionService; + + @Mock + private MemberStorage memberStorage; + + @InjectMocks + private MentoringSessionManager mentoringSessionManager; + + private Member mentorMember; + private Mentor mentor; + private Mentoring mentoring; + private MentorSlot mentorSlot; + private Reservation reservation; + private MentoringSession mentoringSession; + + @BeforeEach + void setUp() { + // 테스트에 사용될 객체들을 فxture를 통해 생성 + reservation = ReservationFixture.createDefault(); + mentor = reservation.getMentor(); + mentorMember = mentor.getMember(); + mentoring = reservation.getMentoring(); + mentorSlot = reservation.getMentorSlot(); + mentoringSession = MentoringSessionFixture.create(reservation); + } + + @Nested + @DisplayName("세션 조회 테스트") + class GetSessionTest { + + @Test + @DisplayName("세션 URL을 성공적으로 조회한다.") + void getSessionUrl_success() { + // given + Long sessionId = 1L; + given(mentoringSessionService.getMentoringSession(sessionId)).willReturn(mentoringSession); + + // when + GetSessionUrlResponse response = mentoringSessionManager.getSessionUrl(sessionId); + + // then + assertThat(response.sessionUrl()).isEqualTo(mentoringSession.getSessionUrl()); + } + + @Test + @DisplayName("세션 상세 정보를 성공적으로 조회한다.") + void getSessionDetail_success() { + // given + Long sessionId = 1L; + given(mentoringSessionService.getMentoringSession(sessionId)).willReturn(mentoringSession); + + // when + GetSessionInfoResponse response = mentoringSessionManager.getSessionDetail(sessionId); + + // then + assertThat(response.mentoringTitle()).isEqualTo(mentoring.getTitle()); + assertThat(response.mentorName()).isEqualTo(mentor.getMember().getNickname()); + assertThat(response.menteeName()).isEqualTo(reservation.getMentee().getMember().getNickname()); + assertThat(response.sessionStatus()).isEqualTo(mentoringSession.getStatus().toString()); + } + } + + @Nested + @DisplayName("세션 관리 테스트") + class ManageSessionTest { + + @Test + @DisplayName("멘토가 세션을 성공적으로 연다.") + void openSession_success() { + // given + Long sessionId = 1L; + OpenSessionRequest request = new OpenSessionRequest(sessionId); + + given(memberStorage.findMentorByMember(mentorMember)).willReturn(mentor); + given(mentoringSessionService.getMentoringSession(sessionId)).willReturn(mentoringSession); + // openSession은 상태만 변경하므로, 변경된 자기 자신을 반환하도록 설정 + given(mentoringSessionService.save(any(MentoringSession.class))).willAnswer(invocation -> invocation.getArgument(0)); + + // when + OpenSessionResponse response = mentoringSessionManager.openSession(mentorMember, request); + + // then + // openSession 후의 상태를 검증 + assertThat(response.status()).isEqualTo("OPEN"); + verify(mentoringSessionService).save(any(MentoringSession.class)); + } + + @Test + @DisplayName("멘토가 세션을 성공적으로 닫는다.") + void closeSession_success() { + // given + Long sessionId = 1L; + DeleteSessionRequest request = new DeleteSessionRequest(sessionId); + // 세션을 미리 OPEN 상태로 만듦 + mentoringSession.openSession(mentor); + + given(memberStorage.findMentorByMember(mentorMember)).willReturn(mentor); + given(mentoringSessionService.getMentoringSession(sessionId)).willReturn(mentoringSession); + // closeSession은 상태만 변경하므로, 변경된 자기 자신을 반환하도록 설정 + given(mentoringSessionService.save(any(MentoringSession.class))).willAnswer(invocation -> invocation.getArgument(0)); + + // when + CloseSessionResponse response = mentoringSessionManager.closeSession(mentorMember, request); + + // then + // closeSession 후의 상태를 검증 + assertThat(response.status()).isEqualTo("CLOSED"); + // reservation과 mentorSlot의 상태가 COMPLETED로 변경되었는지 검증 + assertThat(reservation.getStatus()).isEqualTo(ReservationStatus.COMPLETED); + assertThat(mentorSlot.getStatus()).isEqualTo(MentorSlotStatus.COMPLETED); + verify(mentoringSessionService).save(any(MentoringSession.class)); + } + } +} \ No newline at end of file diff --git a/back/src/test/java/com/back/domain/mentoring/slot/controller/MentorSlotControllerTest.java b/back/src/test/java/com/back/domain/mentoring/slot/controller/MentorSlotControllerTest.java index d3baa83a..74896f4a 100644 --- a/back/src/test/java/com/back/domain/mentoring/slot/controller/MentorSlotControllerTest.java +++ b/back/src/test/java/com/back/domain/mentoring/slot/controller/MentorSlotControllerTest.java @@ -247,8 +247,8 @@ void createMentorSlotRepetitionSuccess() throws Exception { "repeatStartDate": "%s", "repeatEndDate": "%s", "daysOfWeek": ["MONDAY", "WEDNESDAY", "FRIDAY"], - "startTime": "10:00:00", - "endTime": "11:00:00" + "startTime": "10:00", + "endTime": "11:00" } """.formatted( startDate.format(DateTimeFormatter.ISO_LOCAL_DATE),