package roomescape.theme import io.kotest.matchers.collections.shouldContainInOrder import io.kotest.matchers.collections.shouldHaveSize import org.hamcrest.CoreMatchers.equalTo import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus import roomescape.common.config.next import roomescape.common.util.DateUtils import roomescape.supports.* import roomescape.theme.exception.ThemeErrorCode import roomescape.theme.infrastructure.persistence.ThemeEntity import roomescape.theme.infrastructure.persistence.ThemeRepository import roomescape.theme.web.toEntity import roomescape.user.infrastructure.persistence.UserEntity import java.time.LocalDate class ThemeApiTest( private val themeRepository: ThemeRepository ) : FunSpecSpringbootTest() { init { context("ID로 테마 정보를 조회한다.") { test("정상 응답") { val createdTheme: ThemeEntity = initialize("조회를 위한 테마 생성") { dummyInitializer.createTheme() } runTest( on = { get("/themes/${createdTheme.id}") }, expect = { body("data.id", equalTo(createdTheme.id)) body("data.name", equalTo(createdTheme.name)) assertProperties( props = setOf( "id", "name", "thumbnailUrl", "description", "difficulty", "price", "minParticipants", "maxParticipants", "availableMinutes", "expectedMinutesFrom", "expectedMinutesTo" ), ) } ) } test("테마가 없으면 실패한다.") { runExceptionTest( method = HttpMethod.GET, endpoint = "/themes/$INVALID_PK", expectedErrorCode = ThemeErrorCode.THEME_NOT_FOUND ) } } context("인기 테마를 조회한다.") { test("정상 응답") { val expectedResult: List = initializeForPopularThemeTest() runTest( on = { get("/themes/most-reserved?count=10") }, expect = { statusCode(HttpStatus.OK.value()) } ).also { res -> val response: List> = res.extract().path("data.themes") response shouldHaveSize expectedResult.size response.map { it["id"] as Long }.shouldContainInOrder(expectedResult) } } } } private fun initializeForPopularThemeTest(): List { val user: UserEntity = testAuthUtil.defaultUser() val themeIds: List = (1..5).map { themeRepository.save(ThemeFixture.createRequest.copy().toEntity(id = tsidFactory.next())).id } val store = dummyInitializer.createStore() // 첫 번째 테마: 유효한 2개 예약 (1L..2L).forEach { dummyInitializer.createConfirmReservation( user = user, storeId = store.id, scheduleRequest = ScheduleFixture.createRequest.copy( date = DateUtils.getSundayOfPreviousWeek(LocalDate.now()).plusDays(it), themeId = themeIds[0], ) ) } // 두 번째 테마: 유효한 1개 예약 dummyInitializer.createConfirmReservation( user = user, storeId = store.id, scheduleRequest = ScheduleFixture.createRequest.copy( date = DateUtils.getSundayOfPreviousWeek(LocalDate.now()), themeId = themeIds[1], ) ) // 세 번째 테마: 유효한 3개 예약 (1L..3L).forEach { dummyInitializer.createConfirmReservation( user = user, storeId = store.id, scheduleRequest = ScheduleFixture.createRequest.copy( date = DateUtils.getSundayOfPreviousWeek(LocalDate.now()).plusDays(it), themeId = themeIds[2], ) ) } // 네 번째 테마: Pending 상태인 3개 예약 -> 집계되지 않음. (1L..3L).forEach { dummyInitializer.createPendingReservation( user = user, storeId = store.id, scheduleRequest = ScheduleFixture.createRequest.copy( date = DateUtils.getSundayOfPreviousWeek(LocalDate.now()).plusDays(it), themeId = themeIds[3], ) ) } // 다섯 번째 테마: 이번주의 확정 예약 -> 집계되지 않음. (1L..3L).forEach { i -> val thisMonday = DateUtils.getSundayOfPreviousWeek(LocalDate.now()).plusDays(8) dummyInitializer.createConfirmReservation( user = user, storeId = store.id, scheduleRequest = ScheduleFixture.createRequest.copy( date = thisMonday.plusDays(i), themeId = themeIds[4], ) ) } // 조회 예상 결과: 세번째, 첫번째, 두번째 테마 순서 return listOf(themeIds[2], themeIds[0], themeIds[1]) } }