Skip to content

Commit 413be7f

Browse files
committed
Test: 예약 단위 테스트 추가
1 parent f877b40 commit 413be7f

File tree

2 files changed

+212
-24
lines changed

2 files changed

+212
-24
lines changed

back/src/test/java/com/back/domain/mentoring/reservation/controller/ReservationControllerTest.java

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -91,30 +91,6 @@ void createReservationSuccess() throws Exception {
9191
.andExpect(jsonPath("$.data.reservation.endDateTime").value(mentorSlot.getEndDateTime().format(formatter)));
9292
}
9393

94-
@Test
95-
@DisplayName("멘티가 멘토에게 예약 신청 실패 - 예약 가능한 상태가 아닌 경우")
96-
void createReservationFailNotAvailable() throws Exception {
97-
Member menteeMember = memberFixture.createMenteeMember();
98-
Mentee mentee2 = memberFixture.createMentee(menteeMember);
99-
mentoringFixture.createReservation(mentoring, mentee2, mentorSlot);
100-
101-
performCreateReservation()
102-
.andExpect(status().isConflict())
103-
.andExpect(jsonPath("$.resultCode").value("409-1"))
104-
.andExpect(jsonPath("$.msg").value("이미 예약이 완료된 시간대입니다."));
105-
}
106-
107-
@Test
108-
@DisplayName("멘티가 멘토에게 예약 신청 실패 - 이미 예약한 경우")
109-
void createReservationFailAlreadyReservation() throws Exception {
110-
mentoringFixture.createReservation(mentoring, mentee, mentorSlot);
111-
112-
performCreateReservation()
113-
.andExpect(status().isConflict())
114-
.andExpect(jsonPath("$.resultCode").value("409-2"))
115-
.andExpect(jsonPath("$.msg").value("이미 예약한 시간대입니다. 예약 목록을 확인해 주세요."));
116-
}
117-
11894

11995
// ===== perform =====
12096

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
package com.back.domain.mentoring.reservation.service;
2+
3+
import com.back.domain.member.member.entity.Member;
4+
import com.back.domain.member.mentee.entity.Mentee;
5+
import com.back.domain.member.mentor.entity.Mentor;
6+
import com.back.domain.mentoring.mentoring.entity.Mentoring;
7+
import com.back.domain.mentoring.mentoring.service.MentoringStorage;
8+
import com.back.domain.mentoring.reservation.constant.ReservationStatus;
9+
import com.back.domain.mentoring.reservation.dto.request.ReservationRequest;
10+
import com.back.domain.mentoring.reservation.dto.response.ReservationResponse;
11+
import com.back.domain.mentoring.reservation.entity.Reservation;
12+
import com.back.domain.mentoring.reservation.error.ReservationErrorCode;
13+
import com.back.domain.mentoring.reservation.repository.ReservationRepository;
14+
import com.back.domain.mentoring.slot.entity.MentorSlot;
15+
import com.back.domain.mentoring.slot.error.MentorSlotErrorCode;
16+
import com.back.fixture.MemberFixture;
17+
import com.back.fixture.MenteeFixture;
18+
import com.back.fixture.MentorFixture;
19+
import com.back.fixture.mentoring.MentorSlotFixture;
20+
import com.back.fixture.mentoring.MentoringFixture;
21+
import com.back.global.exception.ServiceException;
22+
import jakarta.persistence.OptimisticLockException;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.DisplayName;
25+
import org.junit.jupiter.api.Nested;
26+
import org.junit.jupiter.api.Test;
27+
import org.junit.jupiter.api.extension.ExtendWith;
28+
import org.mockito.InjectMocks;
29+
import org.mockito.Mock;
30+
import org.mockito.junit.jupiter.MockitoExtension;
31+
32+
import java.time.LocalDateTime;
33+
import java.util.List;
34+
import java.util.Optional;
35+
36+
import static org.assertj.core.api.Assertions.assertThat;
37+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
38+
import static org.mockito.ArgumentMatchers.any;
39+
import static org.mockito.Mockito.*;
40+
41+
@ExtendWith(MockitoExtension.class)
42+
class ReservationServiceTest {
43+
44+
@InjectMocks
45+
private ReservationService reservationService;
46+
47+
@Mock
48+
private ReservationRepository reservationRepository;
49+
50+
@Mock
51+
private MentoringStorage mentoringStorage;
52+
53+
private Mentor mentor;
54+
private Mentee mentee, mentee2;
55+
private Mentoring mentoring;
56+
private MentorSlot mentorSlot;
57+
58+
@BeforeEach
59+
void setUp() {
60+
Member mentorMember = MemberFixture.create("[email protected]", "Mentor", "pass123");
61+
mentor = MentorFixture.create(1L, mentorMember);
62+
63+
Member menteeMember = MemberFixture.create("[email protected]", "Mentee", "pass123");
64+
mentee = MenteeFixture.create(1L, menteeMember);
65+
66+
Member menteeMember2 = MemberFixture.create("[email protected]", "Mentee2", "pass123");
67+
mentee2 = MenteeFixture.create(2L, menteeMember2);
68+
69+
mentoring = MentoringFixture.create(1L, mentor);
70+
mentorSlot = MentorSlotFixture.create(1L, mentor);
71+
}
72+
73+
@Nested
74+
@DisplayName("멘토링 예약 생성")
75+
class Describe_createReservation {
76+
77+
private ReservationRequest request;
78+
79+
@BeforeEach
80+
void setUp() {
81+
request = new ReservationRequest(
82+
mentor.getId(),
83+
mentorSlot.getId(),
84+
mentoring.getId(),
85+
"사전 질문입니다."
86+
);
87+
}
88+
89+
@Test
90+
@DisplayName("생성 성공")
91+
void createReservation() {
92+
// given
93+
when(mentoringStorage.findMentoring(request.mentoringId()))
94+
.thenReturn(mentoring);
95+
when(mentoringStorage.findMentorSlot(request.mentorSlotId()))
96+
.thenReturn(mentorSlot);
97+
when(reservationRepository.findByMentorSlotIdAndStatusIn(mentorSlot.getId(),
98+
List.of(ReservationStatus.PENDING, ReservationStatus.APPROVED, ReservationStatus.COMPLETED)))
99+
.thenReturn(Optional.empty());
100+
101+
// when
102+
ReservationResponse response = reservationService.createReservation(mentee, request);
103+
104+
// then
105+
assertThat(response.mentoring().mentoringId()).isEqualTo(mentoring.getId());
106+
assertThat(response.mentee().menteeId()).isEqualTo(mentee.getId());
107+
assertThat(response.mentor().mentorId()).isEqualTo(mentor.getId());
108+
assertThat(response.reservation().mentorSlotId()).isEqualTo(mentorSlot.getId());
109+
assertThat(response.reservation().preQuestion()).isEqualTo(request.preQuestion());
110+
assertThat(response.reservation().status()).isEqualTo(ReservationStatus.PENDING);
111+
112+
verify(reservationRepository).save(any(Reservation.class));
113+
}
114+
115+
@Test
116+
@DisplayName("이미 해당 멘티가 예약한 슬롯이면 예외")
117+
void throwExceptionWhenAlreadyReservedBySameMentee() {
118+
// given
119+
Reservation existingReservation = Reservation.builder()
120+
.mentee(mentee)
121+
.mentorSlot(mentorSlot)
122+
.mentoring(mentoring)
123+
.build();
124+
125+
when(mentoringStorage.findMentoring(request.mentoringId()))
126+
.thenReturn(mentoring);
127+
when(mentoringStorage.findMentorSlot(request.mentorSlotId()))
128+
.thenReturn(mentorSlot);
129+
when(reservationRepository.findByMentorSlotIdAndStatusIn(mentorSlot.getId(),
130+
List.of(ReservationStatus.PENDING, ReservationStatus.APPROVED, ReservationStatus.COMPLETED)))
131+
.thenReturn(Optional.of(existingReservation));
132+
133+
// when & then
134+
assertThatThrownBy(() -> reservationService.createReservation(mentee, request))
135+
.isInstanceOf(ServiceException.class)
136+
.hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.ALREADY_RESERVED_SLOT.getCode());
137+
}
138+
139+
@Test
140+
@DisplayName("다른 멘티가 이미 예약한 슬롯이면 예외")
141+
void throwExceptionWhenSlotNotAvailable() {
142+
// given
143+
Reservation existingReservation = Reservation.builder()
144+
.mentee(mentee2) // 다른 멘티
145+
.mentorSlot(mentorSlot)
146+
.mentoring(mentoring)
147+
.build();
148+
149+
when(mentoringStorage.findMentoring(request.mentoringId()))
150+
.thenReturn(mentoring);
151+
when(mentoringStorage.findMentorSlot(request.mentorSlotId()))
152+
.thenReturn(mentorSlot);
153+
when(reservationRepository.findByMentorSlotIdAndStatusIn(mentorSlot.getId(),
154+
List.of(ReservationStatus.PENDING, ReservationStatus.APPROVED, ReservationStatus.COMPLETED)))
155+
.thenReturn(Optional.of(existingReservation));
156+
157+
// when & then
158+
assertThatThrownBy(() -> reservationService.createReservation(mentee, request))
159+
.isInstanceOf(ServiceException.class)
160+
.hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.NOT_AVAILABLE_SLOT.getCode());
161+
}
162+
163+
@Test
164+
@DisplayName("예약 시간이 과거이면 예외")
165+
void throwExceptionWhenStartTimeInPast() {
166+
// given
167+
MentorSlot pastSlot = MentorSlotFixture.create(2L, mentor,
168+
LocalDateTime.now().minusDays(1), LocalDateTime.now().minusDays(1).plusHours(1));
169+
170+
ReservationRequest pastRequest = new ReservationRequest(
171+
mentor.getId(),
172+
pastSlot.getId(),
173+
mentoring.getId(),
174+
"사전 질문입니다."
175+
);
176+
177+
when(mentoringStorage.findMentoring(pastRequest.mentoringId()))
178+
.thenReturn(mentoring);
179+
when(mentoringStorage.findMentorSlot(pastRequest.mentorSlotId()))
180+
.thenReturn(pastSlot);
181+
when(reservationRepository.findByMentorSlotIdAndStatusIn(pastSlot.getId(),
182+
List.of(ReservationStatus.PENDING, ReservationStatus.APPROVED, ReservationStatus.COMPLETED)))
183+
.thenReturn(Optional.empty());
184+
185+
// when & then
186+
assertThatThrownBy(() -> reservationService.createReservation(mentee, pastRequest))
187+
.isInstanceOf(ServiceException.class)
188+
.hasFieldOrPropertyWithValue("resultCode", MentorSlotErrorCode.START_TIME_IN_PAST.getCode());
189+
}
190+
191+
@Test
192+
@DisplayName("동시성 충돌 발생 시 예외")
193+
void throwExceptionOnConcurrentReservation() {
194+
// given
195+
when(mentoringStorage.findMentoring(request.mentoringId()))
196+
.thenReturn(mentoring);
197+
when(mentoringStorage.findMentorSlot(request.mentorSlotId()))
198+
.thenReturn(mentorSlot);
199+
when(reservationRepository.findByMentorSlotIdAndStatusIn(mentorSlot.getId(),
200+
List.of(ReservationStatus.PENDING, ReservationStatus.APPROVED, ReservationStatus.COMPLETED)))
201+
.thenReturn(Optional.empty());
202+
203+
// OptimisticLockException 테스트 위해 save 호출 시 예외 설정
204+
doThrow(new OptimisticLockException()).when(reservationRepository).save(any(Reservation.class));
205+
206+
// when & then
207+
assertThatThrownBy(() -> reservationService.createReservation(mentee, request))
208+
.isInstanceOf(ServiceException.class)
209+
.hasFieldOrPropertyWithValue("resultCode", ReservationErrorCode.CONCURRENT_RESERVATION_CONFLICT.getCode());
210+
}
211+
}
212+
}

0 commit comments

Comments
 (0)