refactor: Interceptor에서 redirect 처리 제거 및 테스트 수정

This commit is contained in:
이상진 2025-07-25 18:09:13 +09:00
parent 41def25709
commit ede2e7f624
6 changed files with 272 additions and 259 deletions

View File

@ -13,8 +13,8 @@ import roomescape.member.infrastructure.persistence.MemberEntity
@Component @Component
class AuthInterceptor( class AuthInterceptor(
private val memberService: MemberService, private val memberService: MemberService,
private val jwtHandler: JwtHandler private val jwtHandler: JwtHandler
) : HandlerInterceptor { ) : HandlerInterceptor {
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
if (handler !is HandlerMethod) { if (handler !is HandlerMethod) {
@ -31,7 +31,6 @@ class AuthInterceptor(
val member: MemberEntity = findMember(request, response) val member: MemberEntity = findMember(request, response)
if (admin != null && !member.isAdmin()) { if (admin != null && !member.isAdmin()) {
response.sendRedirect("/login")
throw AuthException(AuthErrorCode.ACCESS_DENIED) throw AuthException(AuthErrorCode.ACCESS_DENIED)
} }
@ -45,7 +44,6 @@ class AuthInterceptor(
return memberService.findById(memberId) return memberService.findById(memberId)
} catch (e: Exception) { } catch (e: Exception) {
response.sendRedirect("/login")
throw e throw e
} }
} }

View File

@ -16,7 +16,7 @@ import roomescape.util.RoomescapeApiTest
@WebMvcTest(controllers = [AuthController::class]) @WebMvcTest(controllers = [AuthController::class])
class AuthControllerTest( class AuthControllerTest(
val mockMvc: MockMvc val mockMvc: MockMvc
) : RoomescapeApiTest() { ) : RoomescapeApiTest() {
@SpykBean @SpykBean
@ -41,9 +41,9 @@ class AuthControllerTest(
Then("토큰을 쿠키에 담아 응답한다") { Then("토큰을 쿠키에 담아 응답한다") {
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = userRequest, body = userRequest,
) { ) {
status { isOk() } status { isOk() }
header { header {
@ -61,12 +61,12 @@ class AuthControllerTest(
memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password) memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password)
} returns null } returns null
Then("에러 응답") { Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.LOGIN_FAILED val expectedError = AuthErrorCode.LOGIN_FAILED
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = userRequest, body = userRequest,
) { ) {
status { isEqualTo(expectedError.httpStatus.value()) } status { isEqualTo(expectedError.httpStatus.value()) }
jsonPath("$.code", equalTo(expectedError.errorCode)) jsonPath("$.code", equalTo(expectedError.errorCode))
@ -78,14 +78,14 @@ class AuthControllerTest(
Then("400 에러를 응답한다") { Then("400 에러를 응답한다") {
listOf( listOf(
userRequest.copy(email = "invalid"), userRequest.copy(email = "invalid"),
userRequest.copy(password = " "), userRequest.copy(password = " "),
"{\"email\": \"null\", \"password\": \"null\"}" "{\"email\": \"null\", \"password\": \"null\"}"
).forEach { ).forEach {
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = it, body = it,
) { ) {
status { isEqualTo(expectedErrorCode.httpStatus.value()) } status { isEqualTo(expectedErrorCode.httpStatus.value()) }
jsonPath("$.code", equalTo(expectedErrorCode.errorCode)) jsonPath("$.code", equalTo(expectedErrorCode.errorCode))
@ -103,8 +103,8 @@ class AuthControllerTest(
Then("회원의 이름을 응답한다") { Then("회원의 이름을 응답한다") {
runGetTest( runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { isOk() } status { isOk() }
jsonPath("$.data.name", equalTo(user.name)) jsonPath("$.data.name", equalTo(user.name))
@ -118,11 +118,11 @@ class AuthControllerTest(
every { jwtHandler.getMemberIdFromToken(any()) } returns invalidMemberId every { jwtHandler.getMemberIdFromToken(any()) } returns invalidMemberId
every { memberRepository.findByIdOrNull(invalidMemberId) } returns null every { memberRepository.findByIdOrNull(invalidMemberId) } returns null
Then("에러 응답.") { Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.UNIDENTIFIABLE_MEMBER val expectedError = AuthErrorCode.UNIDENTIFIABLE_MEMBER
runGetTest( runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { isEqualTo(expectedError.httpStatus.value()) } status { isEqualTo(expectedError.httpStatus.value()) }
jsonPath("$.code", equalTo(expectedError.errorCode)) jsonPath("$.code", equalTo(expectedError.errorCode))
@ -137,15 +137,14 @@ class AuthControllerTest(
When("로그인 상태가 아니라면") { When("로그인 상태가 아니라면") {
doNotLogin() doNotLogin()
Then("로그인 페이지로 이동한다.") { Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.INVALID_TOKEN
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
header { jsonPath("$.code", equalTo(expectedError.errorCode))
string("Location", "/login")
}
} }
} }
} }
@ -155,8 +154,8 @@ class AuthControllerTest(
Then("토큰의 존재 여부와 무관하게 토큰을 만료시킨다.") { Then("토큰의 존재 여부와 무관하게 토큰을 만료시킨다.") {
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { isOk() } status { isOk() }
header { header {

View File

@ -7,6 +7,7 @@ import io.mockk.every
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.MockMvc
import roomescape.auth.exception.AuthErrorCode
import roomescape.member.web.MemberController import roomescape.member.web.MemberController
import roomescape.member.web.MemberRetrieveListResponse import roomescape.member.web.MemberRetrieveListResponse
import roomescape.util.MemberFixture import roomescape.util.MemberFixture
@ -15,7 +16,7 @@ import kotlin.random.Random
@WebMvcTest(controllers = [MemberController::class]) @WebMvcTest(controllers = [MemberController::class])
class MemberControllerTest( class MemberControllerTest(
@Autowired private val mockMvc: MockMvc @Autowired private val mockMvc: MockMvc
) : RoomescapeApiTest() { ) : RoomescapeApiTest() {
init { init {
@ -23,9 +24,9 @@ class MemberControllerTest(
val endpoint = "/members" val endpoint = "/members"
every { memberRepository.findAll() } returns listOf( every { memberRepository.findAll() } returns listOf(
MemberFixture.create(id = Random.nextLong(), name = "name1"), MemberFixture.create(id = Random.nextLong(), name = "name1"),
MemberFixture.create(id = Random.nextLong(), name = "name2"), MemberFixture.create(id = Random.nextLong(), name = "name2"),
MemberFixture.create(id = Random.nextLong(), name = "name3"), MemberFixture.create(id = Random.nextLong(), name = "name3"),
) )
`when`("관리자가 보내면") { `when`("관리자가 보내면") {
@ -33,15 +34,15 @@ class MemberControllerTest(
then("성공한다.") { then("성공한다.") {
val result: String = runGetTest( val result: String = runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { isOk() } status { isOk() }
}.andReturn().response.contentAsString }.andReturn().response.contentAsString
val response: MemberRetrieveListResponse = readValue( val response: MemberRetrieveListResponse = readValue(
responseJson = result, responseJson = result,
valueType = MemberRetrieveListResponse::class.java valueType = MemberRetrieveListResponse::class.java
) )
assertSoftly(response.members) { assertSoftly(response.members) {
@ -51,32 +52,32 @@ class MemberControllerTest(
} }
} }
`when`("관리자가 아니면 로그인 페이지로 이동한다.") { `when`("관리자가 아니면 에러 응답을 받는다.") {
then("비회원") { then("비회원") {
doNotLogin() doNotLogin()
val expectedError = AuthErrorCode.INVALID_TOKEN
runGetTest( runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
header { }.andExpect {
string("Location", "/login") jsonPath("$.code") { value(expectedError.errorCode) }
}
} }
} }
then("일반 회원") { then("일반 회원") {
loginAsUser() loginAsUser()
val expectedError = AuthErrorCode.ACCESS_DENIED
runGetTest( runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
header { }.andExpect {
string("Location", "/login") jsonPath("$.code") { value(expectedError.errorCode) }
}
} }
} }
} }

View File

@ -9,14 +9,13 @@ import io.restassured.module.kotlin.extensions.Given
import io.restassured.module.kotlin.extensions.Then import io.restassured.module.kotlin.extensions.Then
import io.restassured.module.kotlin.extensions.When import io.restassured.module.kotlin.extensions.When
import jakarta.persistence.EntityManager import jakarta.persistence.EntityManager
import org.hamcrest.Matchers.containsString
import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.equalTo
import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.server.LocalServerPort import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.transaction.support.TransactionTemplate import org.springframework.transaction.support.TransactionTemplate
import roomescape.auth.exception.AuthErrorCode
import roomescape.auth.infrastructure.jwt.JwtHandler import roomescape.auth.infrastructure.jwt.JwtHandler
import roomescape.auth.web.support.MemberIdResolver import roomescape.auth.web.support.MemberIdResolver
import roomescape.member.business.MemberService import roomescape.member.business.MemberService
@ -38,9 +37,9 @@ import java.time.LocalTime
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ReservationControllerTest( class ReservationControllerTest(
@LocalServerPort val port: Int, @LocalServerPort val port: Int,
val entityManager: EntityManager, val entityManager: EntityManager,
val transactionTemplate: TransactionTemplate val transactionTemplate: TransactionTemplate
) : FunSpec({ ) : FunSpec({
extension(DatabaseCleanerExtension(mode = CleanerMode.AFTER_EACH_TEST)) extension(DatabaseCleanerExtension(mode = CleanerMode.AFTER_EACH_TEST))
}) { }) {
@ -66,9 +65,9 @@ class ReservationControllerTest(
test("정상 응답") { test("정상 응답") {
val reservationRequest = createRequest() val reservationRequest = createRequest()
val paymentApproveResponse = PaymentFixture.createApproveResponse().copy( val paymentApproveResponse = PaymentFixture.createApproveResponse().copy(
paymentKey = reservationRequest.paymentKey, paymentKey = reservationRequest.paymentKey,
orderId = reservationRequest.orderId, orderId = reservationRequest.orderId,
totalAmount = reservationRequest.amount, totalAmount = reservationRequest.amount,
) )
every { every {
@ -108,12 +107,12 @@ class ReservationControllerTest(
} }
} }
test("결제 완료 후 예약 / 결제 정보 저장 과정에서 에러 발생시 결제 취소 후 에러 응답") { test("결제 완료 후 예약 / 결제 정보 저장 과정에서 에러 발생시 결제 취소 후 에러 응답을 받는다.") {
val reservationRequest = createRequest() val reservationRequest = createRequest()
val paymentApproveResponse = PaymentFixture.createApproveResponse().copy( val paymentApproveResponse = PaymentFixture.createApproveResponse().copy(
paymentKey = reservationRequest.paymentKey, paymentKey = reservationRequest.paymentKey,
orderId = reservationRequest.orderId, orderId = reservationRequest.orderId,
totalAmount = reservationRequest.amount, totalAmount = reservationRequest.amount,
) )
every { every {
@ -129,8 +128,8 @@ class ReservationControllerTest(
} returns PaymentFixture.createCancelResponse() } returns PaymentFixture.createCancelResponse()
val canceledPaymentSizeBeforeApiCall: Long = entityManager.createQuery( val canceledPaymentSizeBeforeApiCall: Long = entityManager.createQuery(
"SELECT COUNT(c) FROM CanceledPaymentEntity c", "SELECT COUNT(c) FROM CanceledPaymentEntity c",
Long::class.java Long::class.java
).singleResult ).singleResult
Given { Given {
@ -145,8 +144,8 @@ class ReservationControllerTest(
} }
val canceledPaymentSizeAfterApiCall: Long = entityManager.createQuery( val canceledPaymentSizeAfterApiCall: Long = entityManager.createQuery(
"SELECT COUNT(c) FROM CanceledPaymentEntity c", "SELECT COUNT(c) FROM CanceledPaymentEntity c",
Long::class.java Long::class.java
).singleResult ).singleResult
canceledPaymentSizeAfterApiCall shouldBe canceledPaymentSizeBeforeApiCall + 1L canceledPaymentSizeAfterApiCall shouldBe canceledPaymentSizeBeforeApiCall + 1L
@ -203,6 +202,7 @@ class ReservationControllerTest(
test("관리자만 검색할 수 있다.") { test("관리자만 검색할 수 있다.") {
login(reservations.keys.first()) login(reservations.keys.first())
val expectedError = AuthErrorCode.ACCESS_DENIED
Given { Given {
port(port) port(port)
@ -210,7 +210,8 @@ class ReservationControllerTest(
}.When { }.When {
get("/reservations/search") get("/reservations/search")
}.Then { }.Then {
header(HttpHeaders.CONTENT_TYPE, containsString(MediaType.TEXT_HTML_VALUE)) statusCode(expectedError.httpStatus.value())
body("code", equalTo(expectedError.errorCode))
} }
} }
@ -309,14 +310,15 @@ class ReservationControllerTest(
test("관리자만 예약을 삭제할 수 있다.") { test("관리자만 예약을 삭제할 수 있다.") {
login(MemberFixture.create(role = Role.MEMBER)) login(MemberFixture.create(role = Role.MEMBER))
val reservation: ReservationEntity = reservations.values.flatten().first() val reservation: ReservationEntity = reservations.values.flatten().first()
val expectedError = AuthErrorCode.ACCESS_DENIED
Given { Given {
port(port) port(port)
}.When { }.When {
delete("/reservations/${reservation.id}") delete("/reservations/${reservation.id}")
}.Then { }.Then {
statusCode(302) statusCode(expectedError.httpStatus.value())
header(HttpHeaders.LOCATION, containsString("/login")) body("code", equalTo(expectedError.errorCode))
} }
} }
@ -326,8 +328,8 @@ class ReservationControllerTest(
transactionTemplate.execute { transactionTemplate.execute {
val reservation: ReservationEntity = entityManager.find( val reservation: ReservationEntity = entityManager.find(
ReservationEntity::class.java, ReservationEntity::class.java,
reservationId reservationId
) )
reservation.reservationStatus = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED reservation.reservationStatus = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED
entityManager.persist(reservation) entityManager.persist(reservation)
@ -346,8 +348,8 @@ class ReservationControllerTest(
// 예약이 삭제되었는지 확인 // 예약이 삭제되었는지 확인
transactionTemplate.executeWithoutResult { transactionTemplate.executeWithoutResult {
val deletedReservation = entityManager.find( val deletedReservation = entityManager.find(
ReservationEntity::class.java, ReservationEntity::class.java,
reservationId reservationId
) )
deletedReservation shouldBe null deletedReservation shouldBe null
} }
@ -371,8 +373,8 @@ class ReservationControllerTest(
} returns PaymentFixture.createCancelResponse() } returns PaymentFixture.createCancelResponse()
val canceledPaymentSizeBeforeApiCall: Long = entityManager.createQuery( val canceledPaymentSizeBeforeApiCall: Long = entityManager.createQuery(
"SELECT COUNT(c) FROM CanceledPaymentEntity c", "SELECT COUNT(c) FROM CanceledPaymentEntity c",
Long::class.java Long::class.java
).singleResult ).singleResult
Given { Given {
@ -384,8 +386,8 @@ class ReservationControllerTest(
} }
val canceledPaymentSizeAfterApiCall: Long = entityManager.createQuery( val canceledPaymentSizeAfterApiCall: Long = entityManager.createQuery(
"SELECT COUNT(c) FROM CanceledPaymentEntity c", "SELECT COUNT(c) FROM CanceledPaymentEntity c",
Long::class.java Long::class.java
).singleResult ).singleResult
canceledPaymentSizeAfterApiCall shouldBe canceledPaymentSizeBeforeApiCall + 1L canceledPaymentSizeAfterApiCall shouldBe canceledPaymentSizeBeforeApiCall + 1L
@ -397,10 +399,10 @@ class ReservationControllerTest(
val member = login(MemberFixture.create(role = Role.ADMIN)) val member = login(MemberFixture.create(role = Role.ADMIN))
val adminRequest: AdminReservationCreateRequest = createRequest().let { val adminRequest: AdminReservationCreateRequest = createRequest().let {
AdminReservationCreateRequest( AdminReservationCreateRequest(
date = it.date, date = it.date,
themeId = it.themeId, themeId = it.themeId,
timeId = it.timeId, timeId = it.timeId,
memberId = member.id!!, memberId = member.id!!,
) )
} }
@ -425,6 +427,7 @@ class ReservationControllerTest(
test("관리자가 아니면 조회할 수 없다.") { test("관리자가 아니면 조회할 수 없다.") {
login(MemberFixture.create(role = Role.MEMBER)) login(MemberFixture.create(role = Role.MEMBER))
val expectedError = AuthErrorCode.ACCESS_DENIED
Given { Given {
port(port) port(port)
@ -432,14 +435,15 @@ class ReservationControllerTest(
}.When { }.When {
get("/reservations/waiting") get("/reservations/waiting")
}.Then { }.Then {
header(HttpHeaders.CONTENT_TYPE, containsString(MediaType.TEXT_HTML_VALUE)) statusCode(expectedError.httpStatus.value())
body("code", equalTo(expectedError.errorCode))
} }
} }
test("대기 중인 예약 목록을 조회한다.") { test("대기 중인 예약 목록을 조회한다.") {
login(MemberFixture.create(role = Role.ADMIN)) login(MemberFixture.create(role = Role.ADMIN))
val expected = reservations.values.flatten() val expected = reservations.values.flatten()
.count { it.reservationStatus == ReservationStatus.WAITING } .count { it.reservationStatus == ReservationStatus.WAITING }
Given { Given {
port(port) port(port)
@ -458,9 +462,9 @@ class ReservationControllerTest(
val member = login(MemberFixture.create(role = Role.MEMBER)) val member = login(MemberFixture.create(role = Role.MEMBER))
val waitingCreateRequest: WaitingCreateRequest = createRequest().let { val waitingCreateRequest: WaitingCreateRequest = createRequest().let {
WaitingCreateRequest( WaitingCreateRequest(
date = it.date, date = it.date,
themeId = it.themeId, themeId = it.themeId,
timeId = it.timeId timeId = it.timeId
) )
} }
@ -483,11 +487,11 @@ class ReservationControllerTest(
transactionTemplate.executeWithoutResult { transactionTemplate.executeWithoutResult {
val reservation = ReservationFixture.create( val reservation = ReservationFixture.create(
date = reservationRequest.date, date = reservationRequest.date,
theme = entityManager.find(ThemeEntity::class.java, reservationRequest.themeId), theme = entityManager.find(ThemeEntity::class.java, reservationRequest.themeId),
time = entityManager.find(TimeEntity::class.java, reservationRequest.timeId), time = entityManager.find(TimeEntity::class.java, reservationRequest.timeId),
member = member, member = member,
status = ReservationStatus.WAITING status = ReservationStatus.WAITING
) )
entityManager.persist(reservation) entityManager.persist(reservation)
entityManager.flush() entityManager.flush()
@ -496,9 +500,9 @@ class ReservationControllerTest(
// 이미 예약된 시간, 테마로 대기 예약 요청 // 이미 예약된 시간, 테마로 대기 예약 요청
val waitingCreateRequest = WaitingCreateRequest( val waitingCreateRequest = WaitingCreateRequest(
date = reservationRequest.date, date = reservationRequest.date,
themeId = reservationRequest.themeId, themeId = reservationRequest.themeId,
timeId = reservationRequest.timeId timeId = reservationRequest.timeId
) )
val expectedError = ReservationErrorCode.ALREADY_RESERVE val expectedError = ReservationErrorCode.ALREADY_RESERVE
@ -524,8 +528,8 @@ class ReservationControllerTest(
test("대기 중인 예약을 취소한다.") { test("대기 중인 예약을 취소한다.") {
val member = login(MemberFixture.create(role = Role.MEMBER)) val member = login(MemberFixture.create(role = Role.MEMBER))
val waiting: ReservationEntity = createSingleReservation( val waiting: ReservationEntity = createSingleReservation(
member = member, member = member,
status = ReservationStatus.WAITING status = ReservationStatus.WAITING
) )
Given { Given {
@ -538,8 +542,8 @@ class ReservationControllerTest(
transactionTemplate.executeWithoutResult { _ -> transactionTemplate.executeWithoutResult { _ ->
entityManager.find( entityManager.find(
ReservationEntity::class.java, ReservationEntity::class.java,
waiting.id waiting.id
) shouldBe null ) shouldBe null
} }
} }
@ -547,8 +551,8 @@ class ReservationControllerTest(
test("이미 확정된 예약을 삭제하면 예외 응답") { test("이미 확정된 예약을 삭제하면 예외 응답") {
val member = login(MemberFixture.create(role = Role.MEMBER)) val member = login(MemberFixture.create(role = Role.MEMBER))
val reservation: ReservationEntity = createSingleReservation( val reservation: ReservationEntity = createSingleReservation(
member = member, member = member,
status = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED status = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED
) )
val expectedError = ReservationErrorCode.ALREADY_CONFIRMED val expectedError = ReservationErrorCode.ALREADY_CONFIRMED
@ -566,22 +570,22 @@ class ReservationControllerTest(
context("POST /reservations/waiting/{id}/confirm") { context("POST /reservations/waiting/{id}/confirm") {
test("관리자만 승인할 수 있다.") { test("관리자만 승인할 수 있다.") {
login(MemberFixture.create(role = Role.MEMBER)) login(MemberFixture.create(role = Role.MEMBER))
val expectedError = AuthErrorCode.ACCESS_DENIED
Given { Given {
port(port) port(port)
}.When { }.When {
post("/reservations/waiting/1/confirm") post("/reservations/waiting/1/confirm")
}.Then { }.Then {
statusCode(302) statusCode(expectedError.httpStatus.value())
header(HttpHeaders.LOCATION, containsString("/login")) body("code", equalTo(expectedError.errorCode))
} }
} }
test("대기 예약을 승인하면 결제 대기 상태로 변경") { test("대기 예약을 승인하면 결제 대기 상태로 변경") {
val member = login(MemberFixture.create(role = Role.ADMIN)) val member = login(MemberFixture.create(role = Role.ADMIN))
val reservation = createSingleReservation( val reservation = createSingleReservation(
member = member, member = member,
status = ReservationStatus.WAITING status = ReservationStatus.WAITING
) )
Given { Given {
@ -594,8 +598,8 @@ class ReservationControllerTest(
transactionTemplate.executeWithoutResult { _ -> transactionTemplate.executeWithoutResult { _ ->
entityManager.find( entityManager.find(
ReservationEntity::class.java, ReservationEntity::class.java,
reservation.id reservation.id
)?.also { )?.also {
it.reservationStatus shouldBe ReservationStatus.CONFIRMED_PAYMENT_REQUIRED it.reservationStatus shouldBe ReservationStatus.CONFIRMED_PAYMENT_REQUIRED
} ?: throw AssertionError("Reservation not found") } ?: throw AssertionError("Reservation not found")
@ -605,8 +609,8 @@ class ReservationControllerTest(
test("다른 확정된 예약을 승인하면 예외 응답") { test("다른 확정된 예약을 승인하면 예외 응답") {
val admin = login(MemberFixture.create(role = Role.ADMIN)) val admin = login(MemberFixture.create(role = Role.ADMIN))
val alreadyReserved = createSingleReservation( val alreadyReserved = createSingleReservation(
member = admin, member = admin,
status = ReservationStatus.CONFIRMED status = ReservationStatus.CONFIRMED
) )
val member = MemberFixture.create(account = "account", role = Role.MEMBER).also { it -> val member = MemberFixture.create(account = "account", role = Role.MEMBER).also { it ->
@ -615,11 +619,11 @@ class ReservationControllerTest(
} }
} }
val waiting = ReservationFixture.create( val waiting = ReservationFixture.create(
date = alreadyReserved.date, date = alreadyReserved.date,
time = alreadyReserved.time, time = alreadyReserved.time,
theme = alreadyReserved.theme, theme = alreadyReserved.theme,
member = member, member = member,
status = ReservationStatus.WAITING status = ReservationStatus.WAITING
).also { ).also {
transactionTemplate.executeWithoutResult { _ -> transactionTemplate.executeWithoutResult { _ ->
entityManager.persist(it) entityManager.persist(it)
@ -642,22 +646,23 @@ class ReservationControllerTest(
context("POST /reservations/waiting/{id}/reject") { context("POST /reservations/waiting/{id}/reject") {
test("관리자만 거절할 수 있다.") { test("관리자만 거절할 수 있다.") {
login(MemberFixture.create(role = Role.MEMBER)) login(MemberFixture.create(role = Role.MEMBER))
val expectedError = AuthErrorCode.ACCESS_DENIED
Given { Given {
port(port) port(port)
}.When { }.When {
post("/reservations/waiting/1/reject") post("/reservations/waiting/1/reject")
}.Then { }.Then {
statusCode(302) statusCode(expectedError.httpStatus.value())
header(HttpHeaders.LOCATION, containsString("/login")) body("code", equalTo(expectedError.errorCode))
} }
} }
test("거절된 예약은 삭제된다.") { test("거절된 예약은 삭제된다.") {
val member = login(MemberFixture.create(role = Role.ADMIN)) val member = login(MemberFixture.create(role = Role.ADMIN))
val reservation = createSingleReservation( val reservation = createSingleReservation(
member = member, member = member,
status = ReservationStatus.WAITING status = ReservationStatus.WAITING
) )
Given { Given {
@ -670,8 +675,8 @@ class ReservationControllerTest(
transactionTemplate.executeWithoutResult { _ -> transactionTemplate.executeWithoutResult { _ ->
entityManager.find( entityManager.find(
ReservationEntity::class.java, ReservationEntity::class.java,
reservation.id reservation.id
) shouldBe null ) shouldBe null
} }
} }
@ -679,18 +684,18 @@ class ReservationControllerTest(
} }
fun createSingleReservation( fun createSingleReservation(
date: LocalDate = LocalDate.now().plusDays(1), date: LocalDate = LocalDate.now().plusDays(1),
time: LocalTime = LocalTime.now(), time: LocalTime = LocalTime.now(),
themeName: String = "Default Theme", themeName: String = "Default Theme",
member: MemberEntity = MemberFixture.create(role = Role.MEMBER), member: MemberEntity = MemberFixture.create(role = Role.MEMBER),
status: ReservationStatus = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED status: ReservationStatus = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED
): ReservationEntity { ): ReservationEntity {
return ReservationFixture.create( return ReservationFixture.create(
date = date, date = date,
theme = ThemeFixture.create(name = themeName), theme = ThemeFixture.create(name = themeName),
time = TimeFixture.create(startAt = time), time = TimeFixture.create(startAt = time),
member = member, member = member,
status = status status = status
).also { it -> ).also { it ->
transactionTemplate.execute { _ -> transactionTemplate.execute { _ ->
if (member.id == null) { if (member.id == null) {
@ -708,8 +713,8 @@ class ReservationControllerTest(
fun createDummyReservations(): MutableMap<MemberEntity, MutableList<ReservationEntity>> { fun createDummyReservations(): MutableMap<MemberEntity, MutableList<ReservationEntity>> {
val reservations: MutableMap<MemberEntity, MutableList<ReservationEntity>> = mutableMapOf() val reservations: MutableMap<MemberEntity, MutableList<ReservationEntity>> = mutableMapOf()
val members: List<MemberEntity> = listOf( val members: List<MemberEntity> = listOf(
MemberFixture.create(role = Role.MEMBER), MemberFixture.create(role = Role.MEMBER),
MemberFixture.create(role = Role.MEMBER) MemberFixture.create(role = Role.MEMBER)
) )
transactionTemplate.executeWithoutResult { transactionTemplate.executeWithoutResult {
@ -728,11 +733,11 @@ class ReservationControllerTest(
entityManager.persist(time) entityManager.persist(time)
val reservation = ReservationFixture.create( val reservation = ReservationFixture.create(
date = LocalDate.now().plusDays(index.toLong()), date = LocalDate.now().plusDays(index.toLong()),
theme = theme, theme = theme,
time = time, time = time,
member = members[index % members.size], member = members[index % members.size],
status = ReservationStatus.CONFIRMED status = ReservationStatus.CONFIRMED
) )
entityManager.persist(reservation) entityManager.persist(reservation)
reservations.getOrPut(reservation.member) { mutableListOf() }.add(reservation) reservations.getOrPut(reservation.member) { mutableListOf() }.add(reservation)
@ -745,8 +750,8 @@ class ReservationControllerTest(
} }
fun createRequest( fun createRequest(
theme: ThemeEntity = ThemeFixture.create(), theme: ThemeEntity = ThemeFixture.create(),
time: TimeEntity = TimeFixture.create(), time: TimeEntity = TimeFixture.create(),
): ReservationCreateWithPaymentRequest { ): ReservationCreateWithPaymentRequest {
lateinit var reservationCreateWithPaymentRequest: ReservationCreateWithPaymentRequest lateinit var reservationCreateWithPaymentRequest: ReservationCreateWithPaymentRequest
@ -755,8 +760,8 @@ class ReservationControllerTest(
entityManager.persist(time) entityManager.persist(time)
reservationCreateWithPaymentRequest = ReservationFixture.createRequest( reservationCreateWithPaymentRequest = ReservationFixture.createRequest(
themeId = theme.id!!, themeId = theme.id!!,
timeId = time.id!!, timeId = time.id!!,
) )
entityManager.flush() entityManager.flush()

View File

@ -8,6 +8,7 @@ import io.kotest.matchers.shouldBe
import io.mockk.every import io.mockk.every
import io.mockk.just import io.mockk.just
import io.mockk.runs import io.mockk.runs
import org.hamcrest.Matchers.equalTo
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.MockMvc
@ -34,15 +35,15 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
When("로그인 상태가 아니라면") { When("로그인 상태가 아니라면") {
doNotLogin() doNotLogin()
Then("로그인 페이지로 이동한다.") { Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.INVALID_TOKEN
runGetTest( runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
header { }.andExpect {
string("Location", "/login") jsonPath("$.code") { value(expectedError.errorCode) }
}
} }
} }
} }
@ -54,14 +55,14 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
every { every {
themeRepository.findAll() themeRepository.findAll()
} returns listOf( } returns listOf(
ThemeFixture.create(id = 1, name = "theme1"), ThemeFixture.create(id = 1, name = "theme1"),
ThemeFixture.create(id = 2, name = "theme2"), ThemeFixture.create(id = 2, name = "theme2"),
ThemeFixture.create(id = 3, name = "theme3") ThemeFixture.create(id = 3, name = "theme3")
) )
val response: ThemeRetrieveListResponse = runGetTest( val response: ThemeRetrieveListResponse = runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { isOk() } status { isOk() }
content { content {
@ -80,37 +81,37 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
Given("테마를 추가할 때") { Given("테마를 추가할 때") {
val endpoint = "/themes" val endpoint = "/themes"
val request = ThemeCreateRequest( val request = ThemeCreateRequest(
name = "theme1", name = "theme1",
description = "description1", description = "description1",
thumbnail = "http://example.com/thumbnail1.jpg" thumbnail = "http://example.com/thumbnail1.jpg"
) )
When("로그인 상태가 아니라면") { When("로그인 상태가 아니라면") {
doNotLogin() doNotLogin()
Then("로그인 페이지로 이동한다.") { Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.INVALID_TOKEN
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = request, body = request,
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
header { jsonPath("$.code", equalTo(expectedError.errorCode))
string("Location", "/login")
}
} }
} }
} }
When("관리자가 아닌 회원은") { When("관리자가 아닌 회원은") {
loginAsUser() loginAsUser()
Then("로그인 페이지로 이동한다.") { Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.ACCESS_DENIED
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = request, body = request,
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
jsonPath("$.code") { value(AuthErrorCode.ACCESS_DENIED.errorCode) } jsonPath("$.code") { value(expectedError.errorCode) }
} }
} }
} }
@ -120,15 +121,15 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
val expectedError = ThemeErrorCode.THEME_NAME_DUPLICATED val expectedError = ThemeErrorCode.THEME_NAME_DUPLICATED
Then("에러 응답.") { Then("에러 응답을 받는다.") {
every { every {
themeRepository.existsByName(request.name) themeRepository.existsByName(request.name)
} returns true } returns true
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = request, body = request,
) { ) {
status { isEqualTo(expectedError.httpStatus.value()) } status { isEqualTo(expectedError.httpStatus.value()) }
jsonPath("$.code") { value(expectedError.errorCode) } jsonPath("$.code") { value(expectedError.errorCode) }
@ -142,16 +143,16 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
} }
val request = ThemeCreateRequest( val request = ThemeCreateRequest(
name = "theme1", name = "theme1",
description = "description1", description = "description1",
thumbnail = "http://example.com/thumbnail1.jpg" thumbnail = "http://example.com/thumbnail1.jpg"
) )
fun runTest(request: ThemeCreateRequest) { fun runTest(request: ThemeCreateRequest) {
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = request, body = request,
) { ) {
status { isBadRequest() } status { isBadRequest() }
} }
@ -192,26 +193,26 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
loginAsAdmin() loginAsAdmin()
val theme = ThemeFixture.create( val theme = ThemeFixture.create(
id = 1, id = 1,
name = request.name, name = request.name,
description = request.description, description = request.description,
thumbnail = request.thumbnail thumbnail = request.thumbnail
) )
every { every {
themeService.createTheme(request) themeService.createTheme(request)
} returns ThemeRetrieveResponse( } returns ThemeRetrieveResponse(
id = theme.id!!, id = theme.id!!,
name = theme.name, name = theme.name,
description = theme.description, description = theme.description,
thumbnail = theme.thumbnail thumbnail = theme.thumbnail
) )
Then("201 응답을 받는다.") { Then("201 응답을 받는다.") {
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = request, body = request,
) { ) {
status { isCreated() } status { isCreated() }
header { header {
@ -232,28 +233,28 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
When("로그인 상태가 아니라면") { When("로그인 상태가 아니라면") {
doNotLogin() doNotLogin()
Then("로그인 페이지로 이동한다.") { Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.INVALID_TOKEN
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
header { jsonPath("$.code", equalTo(expectedError.errorCode))
string("Location", "/login")
}
} }
} }
} }
When("관리자가 아닌 회원은") { When("관리자가 아닌 회원은") {
loginAsUser() loginAsUser()
Then("로그인 페이지로 이동한다.") { Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.ACCESS_DENIED
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
jsonPath("$.code") { value(AuthErrorCode.ACCESS_DENIED.errorCode) } jsonPath("$.code", equalTo(expectedError.errorCode))
} }
} }
} }
@ -262,14 +263,14 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
loginAsAdmin() loginAsAdmin()
val expectedError = ThemeErrorCode.THEME_ALREADY_RESERVED val expectedError = ThemeErrorCode.THEME_ALREADY_RESERVED
Then("에러 응답") { Then("에러 응답을 받는다.") {
every { every {
themeRepository.isReservedTheme(themeId) themeRepository.isReservedTheme(themeId)
} returns true } returns true
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { isEqualTo(expectedError.httpStatus.value()) } status { isEqualTo(expectedError.httpStatus.value()) }
jsonPath("$.code") { value(expectedError.errorCode) } jsonPath("$.code") { value(expectedError.errorCode) }
@ -290,8 +291,8 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
Then("204 응답을 받는다.") { Then("204 응답을 받는다.") {
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { isNoContent() } status { isNoContent() }
} }

View File

@ -6,11 +6,13 @@ import io.kotest.assertions.assertSoftly
import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.mockk.every import io.mockk.every
import org.hamcrest.Matchers.equalTo
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.context.annotation.Import import org.springframework.context.annotation.Import
import org.springframework.data.repository.findByIdOrNull import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.MockMvc
import roomescape.auth.exception.AuthErrorCode
import roomescape.common.config.JacksonConfig import roomescape.common.config.JacksonConfig
import roomescape.reservation.infrastructure.persistence.ReservationRepository import roomescape.reservation.infrastructure.persistence.ReservationRepository
import roomescape.time.business.TimeService import roomescape.time.business.TimeService
@ -27,7 +29,7 @@ import java.time.LocalTime
@WebMvcTest(TimeController::class) @WebMvcTest(TimeController::class)
@Import(JacksonConfig::class) @Import(JacksonConfig::class)
class TimeControllerTest( class TimeControllerTest(
val mockMvc: MockMvc, val mockMvc: MockMvc,
) : RoomescapeApiTest() { ) : RoomescapeApiTest() {
@SpykBean @SpykBean
@ -52,13 +54,13 @@ class TimeControllerTest(
every { every {
timeRepository.findAll() timeRepository.findAll()
} returns listOf( } returns listOf(
TimeFixture.create(id = 1L), TimeFixture.create(id = 1L),
TimeFixture.create(id = 2L) TimeFixture.create(id = 2L)
) )
runGetTest( runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { isOk() } status { isOk() }
content { content {
@ -72,14 +74,19 @@ class TimeControllerTest(
When("관리자가 아닌 경우") { When("관리자가 아닌 경우") {
loginAsUser() loginAsUser()
val expectedError = AuthErrorCode.ACCESS_DENIED
Then("로그인 페이지로 이동") { Then("에러 응답을 받는다.") {
runGetTest( runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
header { string("Location", "/login") } }.andExpect {
content {
contentType(MediaType.APPLICATION_JSON)
jsonPath("$.code") { value(expectedError.errorCode) }
}
} }
} }
} }
@ -97,13 +104,13 @@ class TimeControllerTest(
Then("시간 형식이 HH:mm이 아니거나, 범위를 벗어나면 400 응답") { Then("시간 형식이 HH:mm이 아니거나, 범위를 벗어나면 400 응답") {
listOf( listOf(
"{\"startAt\": \"23:30:30\"}", "{\"startAt\": \"23:30:30\"}",
"{\"startAt\": \"24:59\"}", "{\"startAt\": \"24:59\"}",
).forEach { ).forEach {
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = it, body = it,
) { ) {
status { isBadRequest() } status { isBadRequest() }
} }
@ -116,9 +123,9 @@ class TimeControllerTest(
} returns TimeCreateResponse(id = 1, startAt = time) } returns TimeCreateResponse(id = 1, startAt = time)
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = request, body = request,
) { ) {
status { isCreated() } status { isCreated() }
content { content {
@ -136,9 +143,9 @@ class TimeControllerTest(
} returns true } returns true
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = request, body = request,
) { ) {
status { isEqualTo(expectedError.httpStatus.value()) } status { isEqualTo(expectedError.httpStatus.value()) }
content { content {
@ -152,14 +159,15 @@ class TimeControllerTest(
When("관리자가 아닌 경우") { When("관리자가 아닌 경우") {
loginAsUser() loginAsUser()
Then("로그인 페이지로 이동") { Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.ACCESS_DENIED
runPostTest( runPostTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
body = TimeFixture.create(), body = TimeFixture.create(),
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
header { string("Location", "/login") } jsonPath("$.code", equalTo(expectedError.errorCode))
} }
} }
} }
@ -179,8 +187,8 @@ class TimeControllerTest(
} returns Unit } returns Unit
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { isNoContent() } status { isNoContent() }
} }
@ -194,8 +202,8 @@ class TimeControllerTest(
} returns null } returns null
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = "/times/$id", endpoint = "/times/$id",
) { ) {
status { isEqualTo(expectedError.httpStatus.value()) } status { isEqualTo(expectedError.httpStatus.value()) }
content { content {
@ -217,8 +225,8 @@ class TimeControllerTest(
} returns listOf(ReservationFixture.create()) } returns listOf(ReservationFixture.create())
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = "/times/$id", endpoint = "/times/$id",
) { ) {
status { isEqualTo(expectedError.httpStatus.value()) } status { isEqualTo(expectedError.httpStatus.value()) }
content { content {
@ -232,13 +240,14 @@ class TimeControllerTest(
When("관리자가 아닌 경우") { When("관리자가 아닌 경우") {
loginAsUser() loginAsUser()
Then("로그인 페이지로 이동") { Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.ACCESS_DENIED
runDeleteTest( runDeleteTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = endpoint, endpoint = endpoint,
) { ) {
status { is3xxRedirection() } status { isEqualTo(expectedError.httpStatus.value()) }
header { string("Location", "/login") } jsonPath("$.code", equalTo(expectedError.errorCode))
} }
} }
} }
@ -252,8 +261,8 @@ class TimeControllerTest(
When("저장된 예약 시간이 있으면") { When("저장된 예약 시간이 있으면") {
val times: List<TimeEntity> = listOf( val times: List<TimeEntity> = listOf(
TimeFixture.create(id = 1L, startAt = LocalTime.of(10, 0)), TimeFixture.create(id = 1L, startAt = LocalTime.of(10, 0)),
TimeFixture.create(id = 2L, startAt = LocalTime.of(11, 0)) TimeFixture.create(id = 2L, startAt = LocalTime.of(11, 0))
) )
every { every {
@ -265,17 +274,17 @@ class TimeControllerTest(
every { every {
reservationRepository.findByDateAndThemeId(date, themeId) reservationRepository.findByDateAndThemeId(date, themeId)
} returns listOf( } returns listOf(
ReservationFixture.create( ReservationFixture.create(
id = 1L, id = 1L,
date = date, date = date,
theme = ThemeFixture.create(id = themeId), theme = ThemeFixture.create(id = themeId),
time = times[0] time = times[0]
) )
) )
val response = runGetTest( val response = runGetTest(
mockMvc = mockMvc, mockMvc = mockMvc,
endpoint = "/times/search?date=$date&themeId=$themeId", endpoint = "/times/search?date=$date&themeId=$themeId",
) { ) {
status { isOk() } status { isOk() }
content { content {