test: 테마에 추가된 캐시 기능 테스트 반영

This commit is contained in:
이상진 2025-10-17 19:01:28 +09:00
parent a15adcbd16
commit 92b76f95f8
3 changed files with 116 additions and 6 deletions

View File

@ -6,21 +6,28 @@ import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType
import com.sangdol.roomescape.auth.exception.AuthErrorCode import com.sangdol.roomescape.auth.exception.AuthErrorCode
import com.sangdol.roomescape.supports.* import com.sangdol.roomescape.supports.*
import com.sangdol.roomescape.supports.ThemeFixture.createRequest import com.sangdol.roomescape.supports.ThemeFixture.createRequest
import com.sangdol.roomescape.theme.business.AdminThemeService
import com.sangdol.roomescape.theme.business.MIN_DURATION import com.sangdol.roomescape.theme.business.MIN_DURATION
import com.sangdol.roomescape.theme.business.MIN_PARTICIPANTS import com.sangdol.roomescape.theme.business.MIN_PARTICIPANTS
import com.sangdol.roomescape.theme.business.MIN_PRICE import com.sangdol.roomescape.theme.business.MIN_PRICE
import com.sangdol.roomescape.theme.business.ThemeService
import com.sangdol.roomescape.theme.dto.ThemeInfoResponse
import com.sangdol.roomescape.theme.exception.ThemeErrorCode import com.sangdol.roomescape.theme.exception.ThemeErrorCode
import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity
import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository
import com.sangdol.roomescape.theme.dto.ThemeUpdateRequest import com.sangdol.roomescape.theme.dto.ThemeUpdateRequest
import io.kotest.assertions.assertSoftly
import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.equalTo
import org.springframework.cache.CacheManager
import org.springframework.data.repository.findByIdOrNull import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpMethod import org.springframework.http.HttpMethod
class AdminThemeApiTest( class AdminThemeApiTest(
private val themeRepository: ThemeRepository private val themeRepository: ThemeRepository,
private val themeService: ThemeService,
private val cacheManager: CacheManager
) : FunSpecSpringbootTest() { ) : FunSpecSpringbootTest() {
init { init {
@ -482,14 +489,19 @@ class AdminThemeApiTest(
} }
} }
test("정상 삭제") { test("정상 삭제 및 캐시 제거 확인") {
val token = testAuthUtil.defaultHqAdminLogin().second val token = testAuthUtil.defaultHqAdminLogin().second
val createdTheme = initialize("테스트를 위한 테마 생성") { val createdTheme = initialize("테스트를 위한 테마 생성") {
dummyInitializer.createTheme() dummyInitializer.createTheme()
} }
initialize("테마 캐시 추가") {
themeService.findInfoById(createdTheme.id)
cacheManager.getCache("theme-details")?.get(createdTheme.id, ThemeInfoResponse::class.java).shouldNotBeNull()
}
runTest( runTest(
token = testAuthUtil.defaultHqAdminLogin().second, token = token,
on = { on = {
delete("/admin/themes/${createdTheme.id}") delete("/admin/themes/${createdTheme.id}")
}, },
@ -498,6 +510,7 @@ class AdminThemeApiTest(
} }
).also { ).also {
themeRepository.findByIdOrNull(createdTheme.id) shouldBe null themeRepository.findByIdOrNull(createdTheme.id) shouldBe null
cacheManager.getCache("theme-details")?.get(createdTheme.id, ThemeInfoResponse::class.java) shouldBe null
} }
} }
@ -566,7 +579,7 @@ class AdminThemeApiTest(
val updateRequest = ThemeUpdateRequest(name = "modified") val updateRequest = ThemeUpdateRequest(name = "modified")
test("정상 수정 및 감사 정보 변경 확인") { test("정상 수정 및 감사 정보 & 캐시 변경 확인") {
val createdThemeId: Long = initialize("테스트를 위한 관리자1의 테마 생성") { val createdThemeId: Long = initialize("테스트를 위한 관리자1의 테마 생성") {
runTest( runTest(
token = testAuthUtil.defaultHqAdminLogin().second, token = testAuthUtil.defaultHqAdminLogin().second,
@ -582,6 +595,11 @@ class AdminThemeApiTest(
).extract().path("data.id") ).extract().path("data.id")
} }
initialize("테마 캐시 추가") {
themeService.findInfoById(createdThemeId)
cacheManager.getCache("theme-details")?.get(createdThemeId, ThemeInfoResponse::class.java).shouldNotBeNull()
}
val (otherAdmin, otherAdminToken) = initialize("감사 정보 변경 확인을 위한 관리자2 로그인") { val (otherAdmin, otherAdminToken) = initialize("감사 정보 변경 확인을 위한 관리자2 로그인") {
testAuthUtil.adminLogin( testAuthUtil.adminLogin(
AdminFixture.createHqAdmin(permissionLevel = AdminPermissionLevel.WRITABLE) AdminFixture.createHqAdmin(permissionLevel = AdminPermissionLevel.WRITABLE)
@ -604,6 +622,12 @@ class AdminThemeApiTest(
updatedTheme.name shouldBe updateRequest.name updatedTheme.name shouldBe updateRequest.name
updatedTheme.updatedBy shouldBe otherAdmin.id updatedTheme.updatedBy shouldBe otherAdmin.id
// 캐시 제거 확인
assertSoftly(cacheManager.getCache("theme-details")?.get(createdThemeId, ThemeInfoResponse::class.java)) {
this shouldBe null
}
} }
} }

View File

@ -10,23 +10,32 @@ import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity
import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository
import com.sangdol.roomescape.theme.mapper.toEntity import com.sangdol.roomescape.theme.mapper.toEntity
import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity
import io.kotest.assertions.assertSoftly
import io.kotest.matchers.collections.shouldContainInOrder import io.kotest.matchers.collections.shouldContainInOrder
import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.comparables.shouldBeLessThan import io.kotest.matchers.comparables.shouldBeLessThan
import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.shouldBe
import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.equalTo
import org.springframework.cache.CacheManager
import org.springframework.http.HttpMethod import org.springframework.http.HttpMethod
import java.time.LocalDate import java.time.LocalDate
class ThemeApiTest( class ThemeApiTest(
private val themeRepository: ThemeRepository private val themeRepository: ThemeRepository,
private val cacheManager: CacheManager
) : FunSpecSpringbootTest() { ) : FunSpecSpringbootTest() {
init { init {
context("ID로 테마 정보를 조회한다.") { context("ID로 테마 정보를 조회한다.") {
test("정상 응답") { test("정상 응답 및 캐시 저장 확인") {
val createdTheme: ThemeEntity = initialize("조회를 위한 테마 생성") { val createdTheme: ThemeEntity = initialize("조회를 위한 테마 생성") {
dummyInitializer.createTheme() dummyInitializer.createTheme()
} }
cacheManager.getCache("theme-details")?.get(createdTheme.id, ThemeInfoResponse::class.java).also {
it shouldBe null
}
runTest( runTest(
on = { on = {
get("/themes/${createdTheme.id}") get("/themes/${createdTheme.id}")
@ -43,6 +52,15 @@ class ThemeApiTest(
) )
} }
) )
assertSoftly(cacheManager.getCache("theme-details")) {
this.shouldNotBeNull()
val themeFromCache = this.get(createdTheme.id, ThemeInfoResponse::class.java)
themeFromCache.shouldNotBeNull()
themeFromCache.id shouldBe createdTheme.id
}
} }
test("테마가 없으면 실패한다.") { test("테마가 없으면 실패한다.") {

View File

@ -0,0 +1,68 @@
package com.sangdol.roomescape.theme
import com.ninjasquad.springmockk.MockkBean
import com.sangdol.roomescape.supports.FunSpecSpringbootTest
import com.sangdol.roomescape.supports.IDGenerator
import com.sangdol.roomescape.supports.initialize
import com.sangdol.roomescape.theme.business.ThemeService
import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty
import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity
import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeRepository
import io.mockk.every
import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext
import org.springframework.data.repository.findByIdOrNull
import java.util.concurrent.CountDownLatch
class ThemeConcurrencyTest(
private val themeService: ThemeService,
@MockkBean private val themeRepository: ThemeRepository,
) : FunSpecSpringbootTest() {
init {
test("동일한 테마에 대한 반복 조회 요청시, DB 요청은 1회만 발생한다.") {
val entity = ThemeEntity(
id = IDGenerator.create(),
name = "테스트입니다.",
description = "테스트에요!",
thumbnailUrl = "http://localhost:8080/hello",
difficulty = Difficulty.VERY_EASY,
price = 10000,
minParticipants = 3,
maxParticipants = 5,
availableMinutes = 90,
expectedMinutesFrom = 70,
expectedMinutesTo = 80,
isActive = true
)
every {
themeRepository.findByIdOrNull(entity.id)
} returns entity
initialize("캐시 등록") {
themeService.findInfoById(entity.id)
}
val requestCount = 64
withContext(Dispatchers.IO) {
val latch = CountDownLatch(requestCount)
(1..requestCount).map {
async {
latch.countDown()
latch.await()
themeService.findInfoById(entity.id)
}
}
}
verify(exactly = 1) {
themeRepository.findByIdOrNull(entity.id)
}
}
}
}