refactor: ThemeEntity 코틀린 전환 및 테스트 추가

This commit is contained in:
이상진 2025-07-18 01:16:05 +09:00
parent 766f3b48a5
commit 90b86f4098
2 changed files with 168 additions and 13 deletions

View File

@ -1,12 +1,10 @@
package roomescape.theme.infrastructure.persistence; package roomescape.theme.infrastructure.persistence
import java.time.LocalDate; import org.springframework.data.jpa.repository.JpaRepository
import java.util.List; import org.springframework.data.jpa.repository.Query
import java.time.LocalDate
import org.springframework.data.jpa.repository.JpaRepository; interface ThemeRepository : JpaRepository<ThemeEntity, Long> {
import org.springframework.data.jpa.repository.Query;
public interface ThemeRepository extends JpaRepository<ThemeEntity, Long> {
@Query(value = """ @Query(value = """
SELECT t SELECT t
@ -16,10 +14,12 @@ public interface ThemeRepository extends JpaRepository<ThemeEntity, Long> {
GROUP BY r.theme.id GROUP BY r.theme.id
ORDER BY COUNT(r.theme.id) DESC, t.id ASC ORDER BY COUNT(r.theme.id) DESC, t.id ASC
LIMIT :limit LIMIT :limit
""")
List<ThemeEntity> findTopNThemeBetweenStartDateAndEndDate(LocalDate startDate, LocalDate endDate, int limit);
boolean existsByName(String name); """
)
fun findTopNThemeBetweenStartDateAndEndDate(startDate: LocalDate, endDate: LocalDate, limit: Int): List<ThemeEntity>
fun existsByName(name: String): Boolean
@Query(value = """ @Query(value = """
SELECT EXISTS( SELECT EXISTS(
@ -28,5 +28,5 @@ public interface ThemeRepository extends JpaRepository<ThemeEntity, Long> {
WHERE r.theme.id = :id WHERE r.theme.id = :id
) )
""") """)
boolean isReservedTheme(Long id); fun isReservedTheme(id: Long): Boolean
} }

View File

@ -0,0 +1,155 @@
package roomescape.theme.infrastructure.persistence
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldContainInOrder
import io.kotest.matchers.shouldBe
import jakarta.persistence.EntityManager
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.transaction.support.TransactionTemplate
import roomescape.theme.util.TestThemeCreateUtil
import java.time.LocalDate
@DataJpaTest
class ThemeRepositoryTest(
val themeRepository: ThemeRepository,
val transactionTemplate: TransactionTemplate,
val entityManager: EntityManager
) : FunSpec() {
init {
beforeSpec {
/**
* 테마 10개를 생성한다.
* 이름: "테마N", 예약 : N, 날짜: 오늘 기준으로 N일
*/
transactionTemplate.executeWithoutResult {
for (i in 1..10) {
TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
name = "테마$i",
reservedCount = i,
date = LocalDate.now().minusDays(i.toLong()),
)
}
}
}
context("findTopNThemeBetweenStartDateAndEndDate") {
test("지난 10일간 예약 수가 가장 많은 테마 5개를 조회한다.") {
themeRepository.findTopNThemeBetweenStartDateAndEndDate(
LocalDate.now().minusDays(10),
LocalDate.now().minusDays(1),
5
).also { themes ->
themes.size shouldBe 5
themes.map { it.name } shouldContainInOrder listOf(
"테마10", "테마9", "테마8", "테마7", "테마6"
)
}
}
test("8일 전부터 5일 전까지 예약 수가 가장 많은 테마 3개를 조회한다.") {
themeRepository.findTopNThemeBetweenStartDateAndEndDate(
LocalDate.now().minusDays(8),
LocalDate.now().minusDays(5),
3
).also { themes ->
themes.size shouldBe 3
themes.map { it.name } shouldContainInOrder listOf(
"테마8", "테마7", "테마6"
)
}
}
test("예약 수가 동일하면 먼저 생성된 테마를 우선 조회한다.") {
TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
name = "테마11",
reservedCount = 5,
date = LocalDate.now().minusDays(5),
)
themeRepository.findTopNThemeBetweenStartDateAndEndDate(
LocalDate.now().minusDays(6),
LocalDate.now().minusDays(4),
5
).also { themes ->
themes.size shouldBe 4
themes.map { it.name } shouldContainInOrder listOf(
"테마6", "테마5", "테마11", "테마4"
)
}
}
test("입력된 갯수보다 조회된 갯수가 작으면, 조회된 갯수만큼 반환한다.") {
themeRepository.findTopNThemeBetweenStartDateAndEndDate(
LocalDate.now().minusDays(10),
LocalDate.now().minusDays(6),
10
).also { themes ->
themes.size shouldBe 5
}
}
test("입력된 갯수보다 조회된 갯수가 많으면, 입력된 갯수만큼 반환한다.") {
themeRepository.findTopNThemeBetweenStartDateAndEndDate(
LocalDate.now().minusDays(10),
LocalDate.now().minusDays(1),
15
).also { themes ->
themes.size shouldBe 10
}
}
test("입력된 날짜 범위에 예약된 테마가 없을 경우 빈 리스트를 반환한다.") {
themeRepository.findTopNThemeBetweenStartDateAndEndDate(
LocalDate.now().plusDays(1),
LocalDate.now().plusDays(10),
5
).also { themes ->
themes.size shouldBe 0
}
}
}
context("existsByName ") {
val themeName = "test-theme"
beforeTest {
TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
name = themeName,
reservedCount = 0,
date = LocalDate.now()
)
}
test("테마 이름이 존재하면 true를 반환한다.") {
themeRepository.existsByName(themeName) shouldBe true
}
test("테마 이름이 존재하지 않으면 false를 반환한다.") {
themeRepository.existsByName(themeName.repeat(2)) shouldBe false
}
}
context("isReservedTheme") {
test("테마가 예약 중이면 true를 반환한다.") {
val theme = TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
name = "예약된 테마",
reservedCount = 1,
date = LocalDate.now()
)
themeRepository.isReservedTheme(theme.id!!) shouldBe true
}
test("테마가 예약 중이 아니면 false를 반환한다.") {
val theme = TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
name = "예약되지 않은 테마",
reservedCount = 0,
date = LocalDate.now()
)
themeRepository.isReservedTheme(theme.id!!) shouldBe false
}
}
}
}