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/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