generated from pricelees/issue-pr-template
test: ReservationTimeController의 테스트 코드 코틀린 전환
This commit is contained in:
parent
b15b8b7ae8
commit
5c6c52cf41
@ -1,254 +1,306 @@
|
||||
package roomescape.reservation.web;
|
||||
package roomescape.reservation.web
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import com.ninjasquad.springmockk.MockkBean
|
||||
import com.ninjasquad.springmockk.SpykBean
|
||||
import io.kotest.assertions.assertSoftly
|
||||
import io.kotest.matchers.collections.shouldHaveSize
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.mockk.every
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import roomescape.common.config.JacksonConfig
|
||||
import roomescape.common.exception.ErrorType
|
||||
import roomescape.reservation.business.ReservationTimeService
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationRepository
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeRepository
|
||||
import roomescape.util.ReservationFixture
|
||||
import roomescape.util.ReservationTimeFixture
|
||||
import roomescape.util.RoomescapeApiTest
|
||||
import roomescape.util.ThemeFixture
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
@WebMvcTest(ReservationTimeController::class)
|
||||
@Import(JacksonConfig::class)
|
||||
class ReservationTimeControllerTest(
|
||||
val mockMvc: MockMvc,
|
||||
) : RoomescapeApiTest() {
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
|
||||
@SpykBean
|
||||
private lateinit var reservationTimeService: ReservationTimeService
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.http.Header;
|
||||
import roomescape.member.infrastructure.persistence.MemberEntity;
|
||||
import roomescape.member.infrastructure.persistence.MemberRepository;
|
||||
import roomescape.member.infrastructure.persistence.Role;
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity;
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationRepository;
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus;
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity;
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeRepository;
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity;
|
||||
import roomescape.theme.infrastructure.persistence.ThemeRepository;
|
||||
@MockkBean
|
||||
private lateinit var reservationTimeRepository: ReservationTimeRepository
|
||||
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@Sql(scripts = "/truncate.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD)
|
||||
public class ReservationTimeControllerTest {
|
||||
@MockkBean
|
||||
private lateinit var reservationRepository: ReservationRepository
|
||||
|
||||
@Autowired
|
||||
private ReservationTimeRepository reservationTimeRepository;
|
||||
init {
|
||||
Given("등록된 모든 시간을 조회할 때") {
|
||||
val endpoint = "/times"
|
||||
|
||||
@Autowired
|
||||
private ThemeRepository themeRepository;
|
||||
|
||||
@Autowired
|
||||
private ReservationRepository reservationRepository;
|
||||
|
||||
@Autowired
|
||||
private MemberRepository memberRepository;
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
private final Map<String, String> params = Map.of(
|
||||
"startAt", "17:00"
|
||||
);
|
||||
|
||||
@Test
|
||||
@DisplayName("처음으로 등록하는 시간의 id는 1이다.")
|
||||
void firstPost() {
|
||||
String adminAccessTokenCookie = getAdminAccessTokenCookieByLogin("email@email.com", "password");
|
||||
|
||||
RestAssured.given().log().all()
|
||||
.contentType(ContentType.JSON)
|
||||
.port(port)
|
||||
.header(new Header("Cookie", adminAccessTokenCookie))
|
||||
.body(params)
|
||||
.when().post("/times")
|
||||
.then().log().all()
|
||||
.statusCode(201)
|
||||
.body("data.id", is(1))
|
||||
.header("Location", "/times/1");
|
||||
When("관리자인 경우") {
|
||||
beforeTest {
|
||||
loginAsAdmin()
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("아무 시간도 등록 하지 않은 경우, 시간 목록 조회 결과 개수는 0개이다.")
|
||||
void readEmptyTimes() {
|
||||
String adminAccessTokenCookie = getAdminAccessTokenCookieByLogin("email@email.com", "password");
|
||||
|
||||
RestAssured.given().log().all()
|
||||
.port(port)
|
||||
.header(new Header("Cookie", adminAccessTokenCookie))
|
||||
.when().get("/times")
|
||||
.then().log().all()
|
||||
.statusCode(200)
|
||||
.body("data.times.size()", is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("하나의 시간만 등록한 경우, 시간 목록 조회 결과 개수는 1개이다.")
|
||||
void readTimesSizeAfterFirstPost() {
|
||||
String adminAccessTokenCookie = getAdminAccessTokenCookieByLogin("email@email.com", "password");
|
||||
|
||||
RestAssured.given().log().all()
|
||||
.contentType(ContentType.JSON)
|
||||
.port(port)
|
||||
.header(new Header("Cookie", adminAccessTokenCookie))
|
||||
.body(params)
|
||||
.when().post("/times")
|
||||
.then().log().all()
|
||||
.statusCode(201)
|
||||
.body("data.id", is(1))
|
||||
.header("Location", "/times/1");
|
||||
|
||||
RestAssured.given().log().all()
|
||||
.port(port)
|
||||
.header(new Header("Cookie", adminAccessTokenCookie))
|
||||
.when().get("/times")
|
||||
.then().log().all()
|
||||
.statusCode(200)
|
||||
.body("data.times.size()", is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("하나의 시간만 등록한 경우, 시간 삭제 뒤 시간 목록 조회 결과 개수는 0개이다.")
|
||||
void readTimesSizeAfterPostAndDelete() {
|
||||
String adminAccessTokenCookie = getAdminAccessTokenCookieByLogin("email@email.com", "password");
|
||||
|
||||
RestAssured.given().log().all()
|
||||
.contentType(ContentType.JSON)
|
||||
.port(port)
|
||||
.header(new Header("Cookie", adminAccessTokenCookie))
|
||||
.body(params)
|
||||
.when().post("/times")
|
||||
.then().log().all()
|
||||
.statusCode(201)
|
||||
.body("data.id", is(1))
|
||||
.header("Location", "/times/1");
|
||||
|
||||
RestAssured.given().log().all()
|
||||
.port(port)
|
||||
.header(new Header("Cookie", adminAccessTokenCookie))
|
||||
.when().delete("/times/1")
|
||||
.then().log().all()
|
||||
.statusCode(204);
|
||||
|
||||
RestAssured.given().log().all()
|
||||
.port(port)
|
||||
.header(new Header("Cookie", adminAccessTokenCookie))
|
||||
.when().get("/times")
|
||||
.then().log().all()
|
||||
.statusCode(200)
|
||||
.body("data.times.size()", is(0));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("validateRequestDataFormatSource")
|
||||
@DisplayName("예약 시간 생성 시, 시간 요청 데이터에 시간 포맷이 아닌 값이 입력되어오면 400 에러를 발생한다.")
|
||||
void validateRequestDataFormat(Map<String, String> request) {
|
||||
String adminAccessTokenCookie = getAdminAccessTokenCookieByLogin("email@email.com", "password");
|
||||
|
||||
RestAssured.given().log().all()
|
||||
.contentType(ContentType.JSON)
|
||||
.header(new Header("Cookie", adminAccessTokenCookie))
|
||||
.port(port)
|
||||
.body(request)
|
||||
.when().post("/times")
|
||||
.then().log().all()
|
||||
.statusCode(400);
|
||||
}
|
||||
|
||||
static Stream<Map<String, String>> validateRequestDataFormatSource() {
|
||||
return Stream.of(
|
||||
Map.of(
|
||||
"startAt", "24:59"
|
||||
),
|
||||
Map.of(
|
||||
"startAt", "hihi")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("validateBlankRequestSource")
|
||||
@DisplayName("예약 시간 생성 시, 요청 값에 공백 또는 null이 포함되어 있으면 400 에러를 발생한다.")
|
||||
void validateBlankRequest(Map<String, String> request) {
|
||||
String adminAccessTokenCookie = getAdminAccessTokenCookieByLogin("email@email.com", "password");
|
||||
|
||||
RestAssured.given().log().all()
|
||||
.contentType(ContentType.JSON)
|
||||
.header(new Header("Cookie", adminAccessTokenCookie))
|
||||
.port(port)
|
||||
.body(request)
|
||||
.when().post("/times")
|
||||
.then().log().all()
|
||||
.statusCode(400);
|
||||
}
|
||||
|
||||
static Stream<Map<String, String>> validateBlankRequestSource() {
|
||||
return Stream.of(
|
||||
Map.of(
|
||||
),
|
||||
Map.of(
|
||||
"startAt", ""
|
||||
),
|
||||
Map.of(
|
||||
"startAt", " "
|
||||
Then("정상 응답") {
|
||||
every {
|
||||
reservationTimeRepository.findAll()
|
||||
} returns listOf(
|
||||
ReservationTimeFixture.create(id = 1L),
|
||||
ReservationTimeFixture.create(id = 2L)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private String getAdminAccessTokenCookieByLogin(String email, String password) {
|
||||
memberRepository.save(new MemberEntity(null, "이름", email, password, Role.ADMIN));
|
||||
|
||||
Map<String, String> loginParams = Map.of(
|
||||
"email", email,
|
||||
"password", password
|
||||
);
|
||||
|
||||
String accessToken = RestAssured.given().log().all()
|
||||
.contentType(ContentType.JSON)
|
||||
.port(port)
|
||||
.body(loginParams)
|
||||
.when().post("/login")
|
||||
.then().log().all().extract().cookie("accessToken");
|
||||
|
||||
return "accessToken=" + accessToken;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("특정 날짜의 특정 테마 예약 현황을 조회한다.")
|
||||
void readReservationByDateAndThemeId() {
|
||||
// given
|
||||
LocalDate today = LocalDate.now();
|
||||
ReservationTimeEntity reservationTime1 = reservationTimeRepository.save(
|
||||
new ReservationTimeEntity(null, LocalTime.of(17, 0)));
|
||||
ReservationTimeEntity reservationTime2 = reservationTimeRepository.save(
|
||||
new ReservationTimeEntity(null, LocalTime.of(17, 30)));
|
||||
ReservationTimeEntity reservationTime3 = reservationTimeRepository.save(
|
||||
new ReservationTimeEntity(null, LocalTime.of(18, 30)));
|
||||
ThemeEntity theme = themeRepository.save(new ThemeEntity(null, "테마명1", "설명", "썸네일URL"));
|
||||
MemberEntity member = memberRepository.save(
|
||||
new MemberEntity(null, "name", "email@email.com", "password", Role.MEMBER));
|
||||
|
||||
reservationRepository.save(
|
||||
new ReservationEntity(null, today.plusDays(1), reservationTime1, theme, member,
|
||||
ReservationStatus.CONFIRMED));
|
||||
reservationRepository.save(
|
||||
new ReservationEntity(null, today.plusDays(1), reservationTime2, theme, member,
|
||||
ReservationStatus.CONFIRMED));
|
||||
reservationRepository.save(
|
||||
new ReservationEntity(null, today.plusDays(1), reservationTime3, theme, member,
|
||||
ReservationStatus.CONFIRMED));
|
||||
|
||||
// when & then
|
||||
RestAssured.given().log().all()
|
||||
.contentType(ContentType.JSON)
|
||||
.port(port)
|
||||
.header("Cookie", getAdminAccessTokenCookieByLogin("a@a.a", "a"))
|
||||
.when().get("/times/filter?date={date}&themeId={themeId}", today.plusDays(1).toString(), theme.getId())
|
||||
.then().log().all()
|
||||
.statusCode(200)
|
||||
.body("data.reservationTimes.size()", is(3));
|
||||
runGetTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
log = true
|
||||
) {
|
||||
status { isOk() }
|
||||
content {
|
||||
contentType(MediaType.APPLICATION_JSON)
|
||||
jsonPath("$.data.times[0].id") { value(1) }
|
||||
jsonPath("$.data.times[1].id") { value(2) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
When("관리자가 아닌 경우") {
|
||||
loginAsUser()
|
||||
|
||||
Then("로그인 페이지로 이동") {
|
||||
runGetTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
log = true
|
||||
) {
|
||||
status { is3xxRedirection() }
|
||||
header { string("Location", "/login") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Given("시간을 추가할 때") {
|
||||
val endpoint = "/times"
|
||||
|
||||
When("관리자인 경우") {
|
||||
beforeTest {
|
||||
loginAsAdmin()
|
||||
}
|
||||
val time = LocalTime.of(10, 0)
|
||||
val request = ReservationTimeRequest(startAt = time)
|
||||
|
||||
Then("시간 형식이 HH:mm이 아니거나, 범위를 벗어나면 400 응답") {
|
||||
listOf(
|
||||
"{\"startAt\": \"23:30:30\"}",
|
||||
"{\"startAt\": \"24:59\"}",
|
||||
).forEach {
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = it,
|
||||
log = true
|
||||
) {
|
||||
status { isBadRequest() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Then("정상 응답") {
|
||||
every {
|
||||
reservationTimeService.addTime(request)
|
||||
} returns ReservationTimeResponse(id = 1, startAt = time)
|
||||
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = request,
|
||||
log = true
|
||||
) {
|
||||
status { isCreated() }
|
||||
content {
|
||||
contentType(MediaType.APPLICATION_JSON)
|
||||
jsonPath("$.data.id") { value(1) }
|
||||
jsonPath("$.data.startAt") { value("10:00") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Then("동일한 시간이 존재하면 409 응답") {
|
||||
every {
|
||||
reservationTimeRepository.existsByStartAt(time)
|
||||
} returns true
|
||||
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = request,
|
||||
log = true
|
||||
) {
|
||||
status { isConflict() }
|
||||
content {
|
||||
contentType(MediaType.APPLICATION_JSON)
|
||||
jsonPath("$.errorType") { value(ErrorType.TIME_DUPLICATED.name) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
When("관리자가 아닌 경우") {
|
||||
loginAsUser()
|
||||
|
||||
Then("로그인 페이지로 이동") {
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = ReservationTimeFixture.create(),
|
||||
log = true
|
||||
) {
|
||||
status { is3xxRedirection() }
|
||||
header { string("Location", "/login") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Given("시간을 삭제할 때") {
|
||||
val endpoint = "/times/1"
|
||||
|
||||
When("관리자인 경우") {
|
||||
beforeTest {
|
||||
loginAsAdmin()
|
||||
}
|
||||
|
||||
Then("정상 응답") {
|
||||
every {
|
||||
reservationTimeService.removeTimeById(1L)
|
||||
} returns Unit
|
||||
|
||||
runDeleteTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
log = true
|
||||
) {
|
||||
status { isNoContent() }
|
||||
}
|
||||
}
|
||||
|
||||
Then("없는 시간을 조회하면 400 응답") {
|
||||
val id = 1L
|
||||
every {
|
||||
reservationTimeRepository.findByIdOrNull(id)
|
||||
} returns null
|
||||
|
||||
runDeleteTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = "/times/$id",
|
||||
log = true
|
||||
) {
|
||||
status { isBadRequest() }
|
||||
content {
|
||||
contentType(MediaType.APPLICATION_JSON)
|
||||
jsonPath("$.errorType") { value(ErrorType.RESERVATION_TIME_NOT_FOUND.name) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Then("예약이 있는 시간을 삭제하면 409 응답") {
|
||||
val id = 1L
|
||||
every {
|
||||
reservationTimeRepository.findByIdOrNull(id)
|
||||
} returns ReservationTimeFixture.create(id = id)
|
||||
|
||||
every {
|
||||
reservationRepository.findByReservationTime(any())
|
||||
} returns listOf(ReservationFixture.create())
|
||||
|
||||
runDeleteTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = "/times/$id",
|
||||
log = true
|
||||
) {
|
||||
status { isConflict() }
|
||||
content {
|
||||
contentType(MediaType.APPLICATION_JSON)
|
||||
jsonPath("$.errorType") { value(ErrorType.TIME_IS_USED_CONFLICT.name) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
When("관리자가 아닌 경우") {
|
||||
loginAsUser()
|
||||
|
||||
Then("로그인 페이지로 이동") {
|
||||
runDeleteTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
log = true
|
||||
) {
|
||||
status { is3xxRedirection() }
|
||||
header { string("Location", "/login") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Given("날짜, 테마가 주어졌을 때") {
|
||||
loginAsUser()
|
||||
|
||||
val date: LocalDate = LocalDate.now()
|
||||
val themeId = 1L
|
||||
|
||||
When("저장된 예약 시간이 있으면") {
|
||||
val times: List<ReservationTimeEntity> = listOf(
|
||||
ReservationTimeFixture.create(id = 1L, startAt = LocalTime.of(10, 0)),
|
||||
ReservationTimeFixture.create(id = 2L, startAt = LocalTime.of(11, 0))
|
||||
)
|
||||
|
||||
every {
|
||||
reservationTimeRepository.findAll()
|
||||
} returns times
|
||||
|
||||
Then("그 시간과, 해당 날짜와 테마에 대한 예약 여부가 담긴 목록을 응답") {
|
||||
|
||||
every {
|
||||
reservationRepository.findByDateAndThemeId(date, themeId)
|
||||
} returns listOf(
|
||||
ReservationFixture.create(
|
||||
id = 1L,
|
||||
date = date,
|
||||
theme = ThemeFixture.create(id = themeId),
|
||||
reservationTime = times[0]
|
||||
)
|
||||
)
|
||||
|
||||
val response = runGetTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = "/times/filter?date=$date&themeId=$themeId",
|
||||
log = true
|
||||
) {
|
||||
status { isOk() }
|
||||
content {
|
||||
contentType(MediaType.APPLICATION_JSON)
|
||||
}
|
||||
}.andReturn().readValue(ReservationTimeInfosResponse::class.java)
|
||||
|
||||
assertSoftly(response.times) {
|
||||
this shouldHaveSize times.size
|
||||
this[0].id shouldBe times[0].id
|
||||
this[0].alreadyBooked shouldBe true
|
||||
|
||||
this[1].id shouldBe times[1].id
|
||||
this[1].alreadyBooked shouldBe false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user