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