test: 예약 테스트에 새로 추가된 회원 및 인증 권한 테스트 추가

This commit is contained in:
이상진 2025-09-13 12:56:09 +09:00
parent 45039b8e7c
commit 97a84f1c61
2 changed files with 207 additions and 151 deletions

View File

@ -3,9 +3,10 @@ package roomescape.reservation
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.equalTo
import org.springframework.data.repository.findByIdOrNull import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
import roomescape.auth.exception.AuthErrorCode
import roomescape.common.exception.CommonErrorCode import roomescape.common.exception.CommonErrorCode
import roomescape.member.infrastructure.persistence.Role
import roomescape.payment.exception.PaymentErrorCode import roomescape.payment.exception.PaymentErrorCode
import roomescape.payment.infrastructure.common.BankCode import roomescape.payment.infrastructure.common.BankCode
import roomescape.payment.infrastructure.common.CardIssuerCode import roomescape.payment.infrastructure.common.CardIssuerCode
@ -35,21 +36,42 @@ class ReservationApiTest(
init { init {
context("결제 전 임시 예약을 생성한다.") { context("결제 전 임시 예약을 생성한다.") {
val commonRequest = ReservationFixture.pendingCreateRequest val commonRequest = ReservationFixture.pendingCreateRequest
val endpoint = "/reservations/pending"
context("권한이 없으면 접근할 수 없다.") {
test("비회원") {
runExceptionTest(
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND
)
}
test("관리자") {
runExceptionTest(
token = authUtil.defaultAdminLogin(),
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
}
test("정상 생성") { test("정상 생성") {
val schedule: ScheduleEntity = dummyInitializer.createSchedule( val schedule: ScheduleEntity = dummyInitializer.createSchedule(
adminToken = authUtil.loginAsAdmin(), adminToken = authUtil.defaultAdminLogin(),
request = ScheduleFixture.createRequest, request = ScheduleFixture.createRequest,
status = ScheduleStatus.HOLD status = ScheduleStatus.HOLD
) )
runTest( runTest(
token = authUtil.loginAsUser(), token = authUtil.defaultUserLogin(),
using = { using = {
body(commonRequest.copy(scheduleId = schedule.id)) body(commonRequest.copy(scheduleId = schedule.id))
}, },
on = { on = {
post("/reservations/pending") post(endpoint)
}, },
expect = { expect = {
statusCode(HttpStatus.OK.value()) statusCode(HttpStatus.OK.value())
@ -67,18 +89,18 @@ class ReservationApiTest(
test("예약을 생성할 때 해당 일정이 ${ScheduleStatus.HOLD} 상태가 아니면 실패한다.") { test("예약을 생성할 때 해당 일정이 ${ScheduleStatus.HOLD} 상태가 아니면 실패한다.") {
val schedule: ScheduleEntity = dummyInitializer.createSchedule( val schedule: ScheduleEntity = dummyInitializer.createSchedule(
adminToken = authUtil.loginAsAdmin(), adminToken = authUtil.defaultAdminLogin(),
request = ScheduleFixture.createRequest, request = ScheduleFixture.createRequest,
status = ScheduleStatus.AVAILABLE status = ScheduleStatus.AVAILABLE
) )
runTest( runTest(
token = authUtil.loginAsUser(), token = authUtil.defaultUserLogin(),
using = { using = {
body(commonRequest.copy(scheduleId = schedule.id)) body(commonRequest.copy(scheduleId = schedule.id))
}, },
on = { on = {
post("/reservations/pending") post(endpoint)
}, },
expect = { expect = {
statusCode(HttpStatus.BAD_REQUEST.value()) statusCode(HttpStatus.BAD_REQUEST.value())
@ -88,7 +110,7 @@ class ReservationApiTest(
} }
test("예약 인원이 테마의 최소 인원보다 작거나 최대 인원보다 많으면 실패한다.") { test("예약 인원이 테마의 최소 인원보다 작거나 최대 인원보다 많으면 실패한다.") {
val adminToken = authUtil.loginAsAdmin() val adminToken = authUtil.defaultAdminLogin()
val theme: ThemeEntity = dummyInitializer.createTheme( val theme: ThemeEntity = dummyInitializer.createTheme(
adminToken = adminToken, adminToken = adminToken,
request = ThemeFixture.createRequest request = ThemeFixture.createRequest
@ -100,86 +122,81 @@ class ReservationApiTest(
status = ScheduleStatus.HOLD status = ScheduleStatus.HOLD
) )
runTest( val userToken = authUtil.defaultUserLogin()
token = authUtil.loginAsUser(),
using = { runExceptionTest(
body( token = userToken,
commonRequest.copy( method = HttpMethod.POST,
scheduleId = schedule.id, endpoint = endpoint,
participantCount = ((theme.minParticipants - 1).toShort()) requestBody = commonRequest.copy(
) schedule.id,
) participantCount = ((theme.minParticipants - 1).toShort())
}, ),
on = { expectedErrorCode = ReservationErrorCode.INVALID_PARTICIPANT_COUNT
post("/reservations/pending")
},
expect = {
statusCode(HttpStatus.BAD_REQUEST.value())
body("code", equalTo(ReservationErrorCode.INVALID_PARTICIPANT_COUNT.errorCode))
}
) )
runTest( runExceptionTest(
token = authUtil.loginAsUser(), token = userToken,
using = { method = HttpMethod.POST,
body( endpoint = endpoint,
commonRequest.copy( requestBody = commonRequest.copy(
scheduleId = schedule.id, scheduleId = schedule.id,
participantCount = ((theme.maxParticipants + 1).toShort()) participantCount = ((theme.maxParticipants + 1).toShort())
) ),
) expectedErrorCode = ReservationErrorCode.INVALID_PARTICIPANT_COUNT
},
on = {
post("/reservations/pending")
},
expect = {
statusCode(HttpStatus.BAD_REQUEST.value())
body("code", equalTo(ReservationErrorCode.INVALID_PARTICIPANT_COUNT.errorCode))
}
) )
} }
context("필수 입력값이 입력되지 않으면 실패한다.") { context("필수 입력값이 입력되지 않으면 실패한다.") {
test("예약자명") { test("예약자명") {
runTest( runExceptionTest(
token = authUtil.loginAsUser(), token = authUtil.defaultUserLogin(),
using = { method = HttpMethod.POST,
body(commonRequest.copy(reserverName = "")) endpoint = endpoint,
}, requestBody = commonRequest.copy(reserverName = ""),
on = { expectedErrorCode = CommonErrorCode.INVALID_INPUT_VALUE
post("/reservations/pending")
},
expect = {
statusCode(HttpStatus.BAD_REQUEST.value())
body("code", equalTo(CommonErrorCode.INVALID_INPUT_VALUE.errorCode))
}
) )
} }
test("예약자 연락처") { test("예약자 연락처") {
runTest( runExceptionTest(
token = authUtil.loginAsUser(), token = authUtil.defaultUserLogin(),
using = { method = HttpMethod.POST,
body(commonRequest.copy(reserverContact = "")) endpoint = endpoint,
}, requestBody = commonRequest.copy(reserverContact = ""),
on = { expectedErrorCode = CommonErrorCode.INVALID_INPUT_VALUE
post("/reservations/pending")
},
expect = {
statusCode(HttpStatus.BAD_REQUEST.value())
body("code", equalTo(CommonErrorCode.INVALID_INPUT_VALUE.errorCode))
}
) )
} }
} }
} }
context("예약을 확정한다.") { context("예약을 확정한다.") {
context("권한이 없으면 접근할 수 없다.") {
val endpoint = "/reservations/$INVALID_PK/confirm"
test("비회원") {
runExceptionTest(
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND
)
}
test("관리자") {
runExceptionTest(
token = authUtil.defaultAdminLogin(),
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
}
test("정상 응답") { test("정상 응답") {
val userToken = authUtil.loginAsUser() val userToken = authUtil.defaultUserLogin()
val reservation: ReservationEntity = dummyInitializer.createPendingReservation( val reservation: ReservationEntity = dummyInitializer.createPendingReservation(
adminToken = authUtil.loginAsAdmin(), adminToken = authUtil.defaultAdminLogin(),
reserverToken = userToken, reserverToken = userToken,
) )
@ -204,25 +221,42 @@ class ReservationApiTest(
} }
test("예약이 없으면 실패한다.") { test("예약이 없으면 실패한다.") {
runTest( runExceptionTest(
token = authUtil.loginAsUser(), token = authUtil.defaultUserLogin(),
on = { method = HttpMethod.POST,
post("/reservations/$INVALID_PK/confirm") endpoint = "/reservations/$INVALID_PK/confirm",
}, expectedErrorCode = ReservationErrorCode.RESERVATION_NOT_FOUND
expect = {
statusCode(HttpStatus.NOT_FOUND.value())
body("code", equalTo(ReservationErrorCode.RESERVATION_NOT_FOUND.errorCode))
}
) )
} }
} }
context("예약을 취소한다.") { context("예약을 취소한다.") {
context("권한이 없으면 접근할 수 없다.") {
val endpoint = "/reservations/$INVALID_PK/confirm"
test("비회원") {
runExceptionTest(
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND
)
}
test("관리자") {
runExceptionTest(
token = authUtil.defaultAdminLogin(),
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
}
test("정상 응답") { test("정상 응답") {
val userToken = authUtil.loginAsUser() val userToken = authUtil.defaultUserLogin()
val reservation: ReservationEntity = dummyInitializer.createConfirmReservation( val reservation: ReservationEntity = dummyInitializer.createConfirmReservation(
adminToken = authUtil.loginAsAdmin(), adminToken = authUtil.defaultAdminLogin(),
reserverToken = userToken, reserverToken = userToken,
) )
@ -250,54 +284,42 @@ class ReservationApiTest(
} }
test("예약이 없으면 실패한다.") { test("예약이 없으면 실패한다.") {
runTest( runExceptionTest(
token = authUtil.loginAsUser(), token = authUtil.defaultUserLogin(),
using = { method = HttpMethod.POST,
body(ReservationCancelRequest(cancelReason = "test")) endpoint = "/reservations/$INVALID_PK/cancel",
}, requestBody = ReservationCancelRequest(cancelReason = "test"),
on = { expectedErrorCode = ReservationErrorCode.RESERVATION_NOT_FOUND
post("/reservations/$INVALID_PK/cancel")
},
expect = {
statusCode(HttpStatus.NOT_FOUND.value())
body("code", equalTo(ReservationErrorCode.RESERVATION_NOT_FOUND.errorCode))
}
) )
} }
test("관리자가 아닌 회원은 다른 회원의 예약을 취소할 수 없다.") { test("관리자가 아닌 회원은 다른 회원의 예약을 취소할 수 없다.") {
val reservation: ReservationEntity = dummyInitializer.createConfirmReservation( val reservation: ReservationEntity = dummyInitializer.createConfirmReservation(
adminToken = authUtil.loginAsAdmin(), adminToken = authUtil.defaultAdminLogin(),
reserverToken = authUtil.loginAsUser(), reserverToken = authUtil.defaultUserLogin(),
) )
val otherUserToken = authUtil.login("other@example.com", "other", role = Role.MEMBER) val otherUserToken = authUtil.userLogin(UserFixture.createUser(email = "test@test.com", phone="01011111111"))
runTest( runExceptionTest(
token = otherUserToken, token = otherUserToken,
using = { method = HttpMethod.POST,
body(ReservationCancelRequest(cancelReason = "test")) endpoint = "/reservations/${reservation.id}/cancel",
}, requestBody = ReservationCancelRequest(cancelReason = "test"),
on = { expectedErrorCode = ReservationErrorCode.NO_PERMISSION_TO_CANCEL_RESERVATION
post("/reservations/${reservation.id}/cancel")
},
expect = {
statusCode(HttpStatus.FORBIDDEN.value())
body("code", equalTo(ReservationErrorCode.NO_PERMISSION_TO_CANCEL_RESERVATION.errorCode))
}
) )
} }
test("관리자는 다른 회원의 예약을 취소할 수 있다.") { test("관리자는 다른 회원의 예약을 취소할 수 있다.") {
val adminToken = authUtil.defaultAdminLogin()
val reservation: ReservationEntity = dummyInitializer.createConfirmReservation( val reservation: ReservationEntity = dummyInitializer.createConfirmReservation(
adminToken = authUtil.loginAsAdmin(), adminToken = adminToken,
reserverToken = authUtil.loginAsAdmin(), reserverToken = authUtil.defaultUserLogin(),
) )
val otherAdminToken = authUtil.login("admin1@example.com", "admin1", role = Role.ADMIN)
runTest( runTest(
token = otherAdminToken, token = adminToken,
using = { using = {
body(ReservationCancelRequest(cancelReason = "test")) body(ReservationCancelRequest(cancelReason = "test"))
}, },
@ -321,9 +343,30 @@ class ReservationApiTest(
} }
context("나의 예약 목록을 조회한다.") { context("나의 예약 목록을 조회한다.") {
context("권한이 없으면 접근할 수 없다.") {
val endpoint = "/reservations/$INVALID_PK/confirm"
test("비회원") {
runExceptionTest(
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND
)
}
test("관리자") {
runExceptionTest(
token = authUtil.defaultAdminLogin(),
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
}
test("정상 응답") { test("정상 응답") {
val userToken = authUtil.loginAsUser() val userToken = authUtil.defaultUserLogin()
val adminToken = authUtil.loginAsAdmin() val adminToken = authUtil.defaultAdminLogin()
for (i in 1..3) { for (i in 1..3) {
dummyInitializer.createConfirmReservation( dummyInitializer.createConfirmReservation(
@ -355,6 +398,27 @@ class ReservationApiTest(
} }
context("예약 상세 정보를 조회한다.") { context("예약 상세 정보를 조회한다.") {
context("권한이 없으면 접근할 수 없다.") {
val endpoint = "/reservations/$INVALID_PK/confirm"
test("비회원") {
runExceptionTest(
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND
)
}
test("관리자") {
runExceptionTest(
token = authUtil.defaultAdminLogin(),
method = HttpMethod.POST,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
}
context("정상 응답") { context("정상 응답") {
val commonPaymentRequest = PaymentFixture.confirmRequest val commonPaymentRequest = PaymentFixture.confirmRequest
@ -362,8 +426,8 @@ class ReservationApiTest(
beforeTest { beforeTest {
reservation = dummyInitializer.createConfirmReservation( reservation = dummyInitializer.createConfirmReservation(
adminToken = authUtil.loginAsAdmin(), adminToken = authUtil.defaultAdminLogin(),
reserverToken = authUtil.loginAsUser(), reserverToken = authUtil.defaultUserLogin(),
) )
} }
@ -428,10 +492,11 @@ class ReservationApiTest(
) )
val cancelReason = "테스트입니다." val cancelReason = "테스트입니다."
val memberId = authUtil.getUser().id!!
val user = authUtil.defaultUser()
dummyInitializer.cancelPayment( dummyInitializer.cancelPayment(
memberId = memberId, userId = user.id,
reservationId = reservation.id, reservationId = reservation.id,
cancelReason = cancelReason, cancelReason = cancelReason,
) )
@ -448,7 +513,7 @@ class ReservationApiTest(
with((it.get("cancel") as LinkedHashMap<*, *>)) { with((it.get("cancel") as LinkedHashMap<*, *>)) {
this["cancelReason"] shouldBe cancelReason this["cancelReason"] shouldBe cancelReason
this["canceledBy"] shouldBe memberId this["canceledBy"] shouldBe user.id
} }
} }
} }
@ -494,40 +559,32 @@ class ReservationApiTest(
} }
test("예약이 없으면 실패한다.") { test("예약이 없으면 실패한다.") {
runTest( runExceptionTest(
token = authUtil.loginAsUser(), token = authUtil.defaultUserLogin(),
on = { method = HttpMethod.GET,
get("/reservations/$INVALID_PK/detail") endpoint = "/reservations/$INVALID_PK/detail",
}, expectedErrorCode = ReservationErrorCode.RESERVATION_NOT_FOUND
expect = {
statusCode(HttpStatus.NOT_FOUND.value())
body("code", equalTo(ReservationErrorCode.RESERVATION_NOT_FOUND.errorCode))
}
) )
} }
test("예약은 있지만, 결제 정보가 없으면 실패한다.") { test("예약은 있지만, 결제 정보가 없으면 실패한다.") {
val reservation = dummyInitializer.createConfirmReservation( val reservation = dummyInitializer.createConfirmReservation(
adminToken = authUtil.loginAsAdmin(), adminToken = authUtil.defaultAdminLogin(),
reserverToken = authUtil.loginAsUser(), reserverToken = authUtil.defaultUserLogin(),
) )
runTest( runExceptionTest(
token = authUtil.loginAsUser(), token = authUtil.defaultUserLogin(),
on = { method = HttpMethod.GET,
get("/reservations/${reservation.id}/detail") endpoint = "/reservations/${reservation.id}/detail",
}, expectedErrorCode = PaymentErrorCode.PAYMENT_NOT_FOUND
expect = {
statusCode(HttpStatus.NOT_FOUND.value())
body("code", equalTo(PaymentErrorCode.PAYMENT_NOT_FOUND.errorCode))
}
) )
} }
test("예약과 결제는 있지만, 결제 세부 내역이 없으면 실패한다.") { test("예약과 결제는 있지만, 결제 세부 내역이 없으면 실패한다.") {
val reservation = dummyInitializer.createConfirmReservation( val reservation = dummyInitializer.createConfirmReservation(
adminToken = authUtil.loginAsAdmin(), adminToken = authUtil.defaultAdminLogin(),
reserverToken = authUtil.loginAsUser(), reserverToken = authUtil.defaultUserLogin(),
) )
dummyInitializer.createPayment( dummyInitializer.createPayment(
@ -537,15 +594,11 @@ class ReservationApiTest(
paymentDetailRepository.deleteAll() paymentDetailRepository.deleteAll()
} }
runTest( runExceptionTest(
token = authUtil.loginAsUser(), token = authUtil.defaultUserLogin(),
on = { method = HttpMethod.GET,
get("/reservations/${reservation.id}/detail") endpoint = "/reservations/${reservation.id}/detail",
}, expectedErrorCode = PaymentErrorCode.PAYMENT_DETAIL_NOT_FOUND
expect = {
statusCode(HttpStatus.NOT_FOUND.value())
body("code", equalTo(PaymentErrorCode.PAYMENT_DETAIL_NOT_FOUND.errorCode))
}
) )
} }
} }
@ -555,17 +608,17 @@ class ReservationApiTest(
reservation: ReservationEntity reservation: ReservationEntity
): LinkedHashMap<String, Any> { ): LinkedHashMap<String, Any> {
return runTest( return runTest(
token = authUtil.loginAsUser(), token = authUtil.defaultUserLogin(),
on = { on = {
get("/reservations/${reservation.id}/detail") get("/reservations/${reservation.id}/detail")
}, },
expect = { expect = {
statusCode(HttpStatus.OK.value()) statusCode(HttpStatus.OK.value())
assertProperties(props = setOf("id", "member", "applicationDateTime", "payment")) assertProperties(props = setOf("id", "user", "applicationDateTime", "payment"))
} }
).also { ).also {
it.extract().path<Long>("data.id") shouldBe reservation.id it.extract().path<Long>("data.id") shouldBe reservation.id
it.extract().path<Long>("data.member.id") shouldBe reservation.memberId it.extract().path<Long>("data.user.id") shouldBe reservation.userId
}.extract().path("data.payment") }.extract().path("data.payment")
} }
} }

View File

@ -123,6 +123,9 @@ class AuthUtil(
} }
fun defaultUserLogin(): String = userLogin(UserFixture.default) fun defaultUserLogin(): String = userLogin(UserFixture.default)
fun defaultUser(): UserEntity = userRepository.findByEmail(UserFixture.default.email)
?: throw AssertionError("Unexpected Exception Occurred.")
} }
fun runTest( fun runTest(