diff --git a/src/test/kotlin/roomescape/schedule/ScheduleApiTest.kt b/src/test/kotlin/roomescape/schedule/ScheduleApiTest.kt index e8164fee..72573521 100644 --- a/src/test/kotlin/roomescape/schedule/ScheduleApiTest.kt +++ b/src/test/kotlin/roomescape/schedule/ScheduleApiTest.kt @@ -4,22 +4,17 @@ import io.kotest.matchers.date.shouldBeAfter import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe -import io.restassured.module.kotlin.extensions.Extract -import io.restassured.module.kotlin.extensions.Given -import io.restassured.module.kotlin.extensions.When -import io.restassured.response.ValidatableResponse import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.notNullValue import org.springframework.data.repository.findByIdOrNull +import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import org.springframework.http.MediaType +import roomescape.admin.infrastructure.persistence.AdminPermissionLevel import roomescape.auth.exception.AuthErrorCode -import roomescape.member.infrastructure.persistence.Role import roomescape.schedule.exception.ScheduleErrorCode import roomescape.schedule.infrastructure.persistence.ScheduleEntity import roomescape.schedule.infrastructure.persistence.ScheduleRepository import roomescape.schedule.infrastructure.persistence.ScheduleStatus -import roomescape.schedule.web.ScheduleCreateRequest import roomescape.schedule.web.ScheduleUpdateRequest import roomescape.util.* import roomescape.util.ScheduleFixture.createRequest @@ -31,162 +26,47 @@ class ScheduleApiTest( ) : FunSpecSpringbootTest() { init { - context("관리자가 아니면 접근할 수 없다.") { - lateinit var token: String - - beforeTest { - token = authUtil.loginAsUser() - } - - val commonAssertion: ValidatableResponse.() -> Unit = { - statusCode(HttpStatus.FORBIDDEN.value()) - body( - "code", - equalTo(AuthErrorCode.ACCESS_DENIED.errorCode) - ) - } - - test("일정 상세: GET /schedules/{id}") { - runTest( - token = token, - on = { - get("/schedules/$INVALID_PK") - }, - expect = commonAssertion - ) - } - - test("일정 생성: POST /schedules") { - runTest( - token = token, - using = { - body(createRequest) - }, - on = { - get("/schedules/$INVALID_PK") - }, - expect = commonAssertion - ) - } - - test("일정 수정: PATCH /schedules/{id}") { - val createdSchedule: ScheduleEntity = - createDummySchedule( - createRequest - ) - - runTest( - token = token, - using = { - body(createRequest) - }, - on = { - patch("/schedules/${createdSchedule.id}") - }, - expect = commonAssertion - ) - } - - test("일정 삭제: DELETE /schedules/{id}") { - val createdSchedule: ScheduleEntity = - createDummySchedule( - createRequest - ) - - runTest( - token = token, - on = { - delete("/schedules/${createdSchedule.id}") - }, - expect = commonAssertion - ) - } - } - - context("일반 회원도 접근할 수 있다.") { - lateinit var token: String - - beforeTest { - token = authUtil.loginAsUser() - } - - test("예약 가능 테마 조회: GET /schedules/themes?date={date}") { - val date = LocalDate.now().plusDays(1) - val time = LocalTime.now() - val createdSchedule: ScheduleEntity = - createDummySchedule( - createRequest.copy( - date = date, - time = time - ) - ) - - runTest( - token = token, - on = { - get("/schedules/themes?date=$date") - }, - expect = { - statusCode(HttpStatus.OK.value()) - body("data.themeIds.size()", equalTo(1)) - body("data.themeIds[0]", equalTo(createdSchedule.themeId)) - } - ) - } - - test("동일한 날짜, 테마에 대한 시간 조회: GET /schedules?date={date}&themeId={themeId}") { - val date = LocalDate.now().plusDays(1) - val time = LocalTime.now() - - val createdSchedule: ScheduleEntity = - createDummySchedule( - createRequest.copy( - date = date, - time = time - ) - ) - createDummySchedule( - createRequest.copy( - date = date.plusDays(1L), - time = time, - themeId = createdSchedule.themeId - ) - ) - - runTest( - token = token, - on = { - get("/schedules?date=$date&themeId=${createdSchedule.themeId}") - }, - expect = { - statusCode(HttpStatus.OK.value()) - body("data.schedules.size()", equalTo(1)) - body("data.schedules[0].id", equalTo(createdSchedule.id)) - assertProperties( - props = setOf("id", "time", "status"), - propsNameIfList = "schedules" - ) - } - ) - } - } context("특정 날짜에 예약 가능한 테마 목록을 조회한다.") { + val date = LocalDate.now().plusDays(1) + val endpoint = "/schedules/themes?date=$date" + + context("권한이 없으면 접근할 수 없다.") { + test("관리자") { + runExceptionTest( + token = authUtil.defaultAdminLogin(), + method = HttpMethod.GET, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + + test("비회원") { + runExceptionTest( + method = HttpMethod.GET, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND + ) + } + } + test("정상 응답") { - val date = LocalDate.now().plusDays(1) + val adminToken = authUtil.defaultAdminLogin() + for (i in 1..10) { - createDummySchedule( - createRequest.copy( - date = date, - time = LocalTime.now().plusMinutes(i.toLong()) + dummyInitializer.createSchedule( + adminToken = adminToken, + request = createRequest.copy( + date = date, + time = LocalTime.now().plusMinutes(i.toLong()) ) ) } runTest( - token = authUtil.loginAsUser(), + token = authUtil.defaultUserLogin(), on = { - get("/schedules/themes?date=$date") + get(endpoint) }, expect = { statusCode(HttpStatus.OK.value()) @@ -197,26 +77,49 @@ class ScheduleApiTest( } context("동일한 날짜, 테마에 대한 모든 시간을 조회한다.") { + context("권한이 없으면 접근할 수 없다.") { + val endpoint = "/schedules?date=2025-09-12&themeId=12345" + + test("관리자") { + runExceptionTest( + token = authUtil.defaultAdminLogin(), + method = HttpMethod.GET, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + + test("비회원") { + runExceptionTest( + method = HttpMethod.GET, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND + ) + } + + } + test("정상 응답") { val date = LocalDate.now().plusDays(1) - val createdSchedule = createDummySchedule( - createRequest.copy( - date = date, - time = LocalTime.now() - ) + val adminToken = authUtil.defaultAdminLogin() + val createdSchedule = dummyInitializer.createSchedule( + adminToken = adminToken, + request = createRequest.copy(date = date, time = LocalTime.now()) ) + for (i in 1..10) { - createDummySchedule( - createRequest.copy( - date = date, - time = LocalTime.now().plusMinutes(i.toLong()), - themeId = createdSchedule.themeId + dummyInitializer.createSchedule( + adminToken = adminToken, + request = createRequest.copy( + date = date, + time = LocalTime.now().plusMinutes(i.toLong()), + themeId = createdSchedule.themeId ) ) } runTest( - token = authUtil.loginAsUser(), + token = authUtil.defaultUserLogin(), on = { get("/schedules?date=$date&themeId=${createdSchedule.themeId}") }, @@ -233,11 +136,48 @@ class ScheduleApiTest( } context("관리자 페이지에서 특정 일정의 감사 정보를 조회한다.") { + context("권한이 없으면 접근할 수 없다.") { + val endpoint = "/schedules/${INVALID_PK}" + + test("비회원") { + runExceptionTest( + method = HttpMethod.GET, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND + ) + } + + test("회원") { + runExceptionTest( + token = authUtil.defaultUserLogin(), + method = HttpMethod.GET, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + + test("권한이 ${AdminPermissionLevel.READ_SUMMARY}인 관리자") { + val admin = AdminFixture.create(permissionLevel = AdminPermissionLevel.READ_SUMMARY) + + runExceptionTest( + token = authUtil.adminLogin(admin), + method = HttpMethod.GET, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + } + test("정상 응답") { - val createdSchedule = createDummySchedule(createRequest) + val token = authUtil.defaultAdminLogin() + + val createdSchedule = dummyInitializer.createSchedule( + adminToken = token, + request = createRequest + ) runTest( - token = authUtil.loginAsAdmin(), + token = token, on = { get("/schedules/${createdSchedule.id}") }, @@ -253,49 +193,67 @@ class ScheduleApiTest( } ).also { it.extract().path("data.createdAt") shouldNotBeNull {} - it.extract().path("data.createdBy") shouldNotBeNull {} - it.extract().path("data.createdAt") shouldNotBeNull {} - it.extract().path("data.createdAt") shouldNotBeNull {} + it.extract().path>("data.createdBy") shouldNotBeNull {} + it.extract().path("data.updatedAt") shouldNotBeNull {} + it.extract().path>("data.updatedBy") shouldNotBeNull {} } } test("일정이 없으면 실패한다.") { - runTest( - token = authUtil.loginAsAdmin(), - on = { - get("/schedules/$INVALID_PK") - }, - expect = { - statusCode(HttpStatus.NOT_FOUND.value()) - body( - "code", - equalTo(ScheduleErrorCode.SCHEDULE_NOT_FOUND.errorCode) - ) - } + runExceptionTest( + token = authUtil.defaultAdminLogin(), + method = HttpMethod.GET, + endpoint = "/schedules/$INVALID_PK", + expectedErrorCode = ScheduleErrorCode.SCHEDULE_NOT_FOUND ) } } context("일정을 생성한다.") { - lateinit var token: String + context("권한이 없으면 접근할 수 없다.") { + val endpoint = "/schedules" - beforeTest { - token = authUtil.loginAsAdmin() + test("비회원") { + runExceptionTest( + method = HttpMethod.POST, + requestBody = createRequest, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND + ) + } + + test("회원") { + runExceptionTest( + token = authUtil.defaultUserLogin(), + method = HttpMethod.POST, + requestBody = createRequest, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + + listOf(AdminPermissionLevel.READ_SUMMARY, AdminPermissionLevel.READ_ALL).forEach { + test("권한이 ${it}인 관리자") { + val admin = AdminFixture.create(permissionLevel = it) + + runExceptionTest( + token = authUtil.adminLogin(admin), + method = HttpMethod.POST, + requestBody = createRequest, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + } } test("정상 생성 및 감사 정보 확인") { - /** - * FK 제약조건 해소를 위한 테마 생성 API 호출 및 ID 획득 - */ - val themeId: Long = Given { - contentType(MediaType.APPLICATION_JSON_VALUE) - header("Authorization", "Bearer $token") - body(ThemeFixture.createRequest.copy(name = "theme-${System.currentTimeMillis()}")) - } When { - post("/admin/themes") - } Extract { - path("data.id") - } + val token = authUtil.defaultAdminLogin() + + val themeId: Long = dummyInitializer.createTheme( + adminToken = token, + request = ThemeFixture.createRequest.copy(name = "theme-${System.currentTimeMillis()}") + ).id /** * 생성 테스트 @@ -328,62 +286,70 @@ class ScheduleApiTest( } test("이미 동일한 날짜, 시간, 테마인 일정이 있으면 실패한다.") { + val token = authUtil.defaultAdminLogin() val date = LocalDate.now().plusDays(1) val time = LocalTime.of(10, 0) - val alreadyCreated: ScheduleEntity = createDummySchedule( - createRequest.copy( - date = date, time = time - ) + val alreadyCreated: ScheduleEntity = dummyInitializer.createSchedule( + adminToken = token, + request = createRequest.copy(date = date, time = time) ) - runTest( + val body = createRequest.copy(date = date, time = time, themeId = alreadyCreated.themeId) + + runExceptionTest( token = token, - using = { - body( - createRequest.copy( - date = date, time = time, themeId = alreadyCreated.themeId - ) - ) - }, - on = { - post("/schedules") - }, - expect = { - statusCode(HttpStatus.CONFLICT.value()) - body("code", equalTo(ScheduleErrorCode.SCHEDULE_ALREADY_EXISTS.errorCode)) - } + method = HttpMethod.POST, + endpoint = "/schedules", + requestBody = body, + expectedErrorCode = ScheduleErrorCode.SCHEDULE_ALREADY_EXISTS ) } test("입력된 날짜 + 시간이 현재 시간 이전이면 실패한다.") { - runTest( + val token = authUtil.defaultAdminLogin() + val body = createRequest.copy(LocalDate.now(), LocalTime.now().minusMinutes(1)) + + runExceptionTest( token = token, - using = { - body( - createRequest.copy( - date = LocalDate.now(), - time = LocalTime.now().minusMinutes(1) - ) - ) - }, - on = { - post("/schedules") - }, - expect = { - statusCode(HttpStatus.BAD_REQUEST.value()) - body("code", equalTo(ScheduleErrorCode.PAST_DATE_TIME.errorCode)) - } + method = HttpMethod.POST, + endpoint = "/schedules", + requestBody = body, + expectedErrorCode = ScheduleErrorCode.PAST_DATE_TIME ) } } context("일정을 잠시 Hold 상태로 변경하여 중복 예약을 방지한다.") { + context("권한이 없으면 접근할 수 없다.") { + val endpoint = "/schedules/${INVALID_PK}/hold" + + test("비회원") { + runExceptionTest( + method = HttpMethod.PATCH, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND + ) + } + + test("관리자") { + runExceptionTest( + token = authUtil.defaultAdminLogin(), + method = HttpMethod.PATCH, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + } + test("해당 일정이 ${ScheduleStatus.AVAILABLE} 상태이면 정상 응답") { - val createdSchedule: ScheduleEntity = createDummySchedule(createRequest) + val createdSchedule: ScheduleEntity = dummyInitializer.createSchedule( + adminToken = authUtil.defaultAdminLogin(), + request = createRequest + ) runTest( - token = authUtil.loginAsUser(), + token = authUtil.defaultUserLogin(), on = { patch("/schedules/${createdSchedule.id}/hold") }, @@ -399,32 +365,31 @@ class ScheduleApiTest( } test("예약이 없으면 실패한다.") { - runTest( - token = authUtil.loginAsUser(), - on = { - patch("/schedules/$INVALID_PK/hold") - }, - expect = { - statusCode(HttpStatus.NOT_FOUND.value()) - body("code", equalTo(ScheduleErrorCode.SCHEDULE_NOT_FOUND.errorCode)) - } + runExceptionTest( + token = authUtil.defaultUserLogin(), + method = HttpMethod.PATCH, + endpoint = "/schedules/$INVALID_PK/hold", + expectedErrorCode = ScheduleErrorCode.SCHEDULE_NOT_FOUND ) } test("해당 일정이 ${ScheduleStatus.AVAILABLE} 상태가 아니면 실패한다.") { - val createdSchedule: ScheduleEntity = createDummySchedule(createRequest) + val adminToken = authUtil.defaultAdminLogin() + + val createdSchedule: ScheduleEntity = dummyInitializer.createSchedule( + adminToken = adminToken, + request = createRequest + ) /* * 테스트를 위해 수정 API를 호출하여 상태를 HOLD 상태로 변경 * 생성 API에서는 일정 생성 시 AVAILABLE을 기본 상태로 지정하기 때문. */ runTest( - token = authUtil.loginAsAdmin(), + token = adminToken, using = { body( - ScheduleUpdateRequest( - status = ScheduleStatus.HOLD - ) + ScheduleUpdateRequest(status = ScheduleStatus.HOLD) ) }, on = { @@ -435,15 +400,11 @@ class ScheduleApiTest( } ) - runTest( - token = authUtil.loginAsUser(), - on = { - patch("/schedules/${createdSchedule.id}/hold") - }, - expect = { - statusCode(HttpStatus.CONFLICT.value()) - body("code", equalTo(ScheduleErrorCode.SCHEDULE_NOT_AVAILABLE.errorCode)) - } + runExceptionTest( + token = authUtil.defaultUserLogin(), + method = HttpMethod.PATCH, + endpoint = "/schedules/${createdSchedule.id}/hold", + expectedErrorCode = ScheduleErrorCode.SCHEDULE_NOT_AVAILABLE ) } } @@ -454,14 +415,55 @@ class ScheduleApiTest( status = ScheduleStatus.BLOCKED ) + context("권한이 없으면 접근할 수 없다.") { + val endpoint = "/schedules/${INVALID_PK}" + + test("비회원") { + runExceptionTest( + method = HttpMethod.PATCH, + requestBody = updateRequest, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND + ) + } + + test("회원") { + runExceptionTest( + token = authUtil.defaultUserLogin(), + method = HttpMethod.PATCH, + requestBody = updateRequest, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + + listOf(AdminPermissionLevel.READ_SUMMARY, AdminPermissionLevel.READ_ALL).forEach { + test("권한이 ${it}인 관리자") { + val admin = AdminFixture.create(permissionLevel = it) + + runExceptionTest( + token = authUtil.adminLogin(admin), + method = HttpMethod.PATCH, + requestBody = updateRequest, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + } + } + test("정상 수정 및 감사 정보 변경 확인") { - val createdSchedule: ScheduleEntity = createDummySchedule( - createRequest.copy( + val createdSchedule: ScheduleEntity = dummyInitializer.createSchedule( + adminToken = authUtil.defaultAdminLogin(), + request = createRequest.copy( date = LocalDate.now().plusDays(1), time = LocalTime.now().plusMinutes(1), ) ) - val otherAdminToken = authUtil.login("admin1@admin.com", "admin1", Role.ADMIN) + + val otherAdminToken = authUtil.adminLogin( + AdminFixture.create(account = "otherAdmin", phone = "01099999999") + ) runTest( token = otherAdminToken, @@ -487,10 +489,15 @@ class ScheduleApiTest( } test("입력값이 없으면 수정하지 않는다.") { - val createdSchedule: ScheduleEntity = createDummySchedule(createRequest) + val token = authUtil.defaultAdminLogin() + + val createdSchedule: ScheduleEntity = dummyInitializer.createSchedule( + adminToken = token, + request = createRequest + ) runTest( - token = authUtil.loginAsAdmin(), + token = token, using = { body(ScheduleUpdateRequest()) }, @@ -509,52 +516,78 @@ class ScheduleApiTest( } test("일정이 없으면 실패한다.") { - runTest( - token = authUtil.loginAsAdmin(), - using = { - body(updateRequest) - }, - on = { - patch("/schedules/$INVALID_PK") - }, - expect = { - statusCode(HttpStatus.NOT_FOUND.value()) - body("code", equalTo(ScheduleErrorCode.SCHEDULE_NOT_FOUND.errorCode)) - } + runExceptionTest( + token = authUtil.defaultAdminLogin(), + method = HttpMethod.PATCH, + requestBody = updateRequest, + endpoint = "/schedules/${INVALID_PK}", + expectedErrorCode = ScheduleErrorCode.SCHEDULE_NOT_FOUND ) } test("입력된 날짜 + 시간이 현재 시간 이전이면 실패한다.") { - val createdSchedule: ScheduleEntity = createDummySchedule( - createRequest.copy(date = LocalDate.now(), time = LocalTime.now().plusMinutes(1)) + val token = authUtil.defaultAdminLogin() + val createdSchedule: ScheduleEntity = dummyInitializer.createSchedule( + adminToken = token, + request = + + createRequest.copy(date = LocalDate.now(), time = LocalTime.now().plusMinutes(1)) ) - runTest( - token = authUtil.loginAsAdmin(), - using = { - body( - updateRequest.copy( - time = LocalTime.now().minusMinutes(1) - ) - ) - }, - on = { - patch("/schedules/${createdSchedule.id}") - }, - expect = { - statusCode(HttpStatus.BAD_REQUEST.value()) - body("code", equalTo(ScheduleErrorCode.PAST_DATE_TIME.errorCode)) - } + runExceptionTest( + token = token, + method = HttpMethod.PATCH, + requestBody = updateRequest.copy(time = LocalTime.now().minusMinutes(1)), + endpoint = "/schedules/${createdSchedule.id}", + expectedErrorCode = ScheduleErrorCode.PAST_DATE_TIME ) } } context("일정을 삭제한다.") { + context("권한이 없으면 접근할 수 없다.") { + val endpoint = "/schedules/${INVALID_PK}" + + test("비회원") { + runExceptionTest( + method = HttpMethod.DELETE, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.TOKEN_NOT_FOUND + ) + } + + test("회원") { + runExceptionTest( + token = authUtil.defaultUserLogin(), + method = HttpMethod.DELETE, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + + listOf(AdminPermissionLevel.READ_SUMMARY, AdminPermissionLevel.READ_ALL).forEach { + test("권한이 ${it}인 관리자") { + val admin = AdminFixture.create(permissionLevel = it) + + runExceptionTest( + token = authUtil.adminLogin(admin), + method = HttpMethod.DELETE, + endpoint = endpoint, + expectedErrorCode = AuthErrorCode.ACCESS_DENIED + ) + } + } + } + test("정상 삭제") { - val createdSchedule: ScheduleEntity = createDummySchedule(createRequest) + val token = authUtil.defaultAdminLogin() + val createdSchedule: ScheduleEntity = dummyInitializer.createSchedule( + adminToken = token, + request = createRequest + ) runTest( - token = authUtil.loginAsAdmin(), + token = token, on = { delete("/schedules/${createdSchedule.id}") }, @@ -567,19 +600,22 @@ class ScheduleApiTest( } test("예약 중이거나 예약이 완료된 일정이면 실패한다.") { - val createdSchedule: ScheduleEntity = createDummySchedule(createRequest) + val token = authUtil.defaultAdminLogin() + + val createdSchedule: ScheduleEntity = dummyInitializer.createSchedule( + adminToken = token, + request = createRequest + ) /* * 테스트를 위해 수정 API를 호출하여 상태를 예약 중 상태로 변경 * 생성 API에서는 일정 생성 시 AVAILABLE을 기본 상태로 지정하기 때문. */ runTest( - token = authUtil.loginAsAdmin(), + token = token, using = { body( - ScheduleUpdateRequest( - status = ScheduleStatus.RESERVED - ) + ScheduleUpdateRequest(status = ScheduleStatus.RESERVED) ) }, on = { @@ -593,48 +629,13 @@ class ScheduleApiTest( /** * 삭제 테스트 */ - runTest( - token = authUtil.loginAsAdmin(), - on = { - delete("/schedules/${createdSchedule.id}") - }, - expect = { - statusCode(HttpStatus.CONFLICT.value()) - body("code", equalTo(ScheduleErrorCode.SCHEDULE_IN_USE.errorCode)) - } + runExceptionTest( + token = token, + method = HttpMethod.DELETE, + endpoint = "/schedules/${createdSchedule.id}", + expectedErrorCode = ScheduleErrorCode.SCHEDULE_IN_USE ) } } } - - fun createDummySchedule(request: ScheduleCreateRequest): ScheduleEntity { - val token = authUtil.loginAsAdmin() - - val themeId: Long = if (request.themeId > 1L) { - request.themeId - } else { - Given { - contentType(MediaType.APPLICATION_JSON_VALUE) - header("Authorization", "Bearer $token") - body(ThemeFixture.createRequest.copy(name = "theme-${System.currentTimeMillis()}")) - } When { - post("/admin/themes") - } Extract { - path("data.id") - } - } - - val createdScheduleId: Long = Given { - contentType(MediaType.APPLICATION_JSON_VALUE) - header("Authorization", "Bearer $token") - body(request.copy(themeId = themeId)) - } When { - post("/schedules") - } Extract { - path("data.id") - } - - return scheduleRepository.findByIdOrNull(createdScheduleId) - ?: throw RuntimeException("unreachable line") - } -} \ No newline at end of file +}