test: 새로 추가된 ThemeFinder,Writer,Validator 테스트 추가 및 기존 테스트에 반영

This commit is contained in:
이상진 2025-08-05 17:32:11 +09:00
parent 18f3a1aebc
commit 853d1a0c4e
7 changed files with 466 additions and 208 deletions

View File

@ -1,87 +1,108 @@
package roomescape.theme.business
import io.kotest.assertions.assertSoftly
import io.kotest.assertions.throwables.shouldNotThrow
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk
import org.springframework.data.repository.findByIdOrNull
import io.mockk.*
import roomescape.theme.exception.ThemeErrorCode
import roomescape.theme.exception.ThemeException
import roomescape.theme.implement.ThemeFinder
import roomescape.theme.implement.ThemeWriter
import roomescape.theme.infrastructure.persistence.ThemeEntity
import roomescape.theme.infrastructure.persistence.ThemeRepository
import roomescape.theme.web.ThemeCreateRequest
import roomescape.theme.web.ThemeRetrieveResponse
import roomescape.util.TsidFactory
import roomescape.theme.web.ThemeCreateResponse
import roomescape.util.ThemeFixture
import java.time.LocalDate
class ThemeServiceTest : FunSpec({
val themeFinder: ThemeFinder = mockk()
val themeWriter: ThemeWriter = mockk()
val themeRepository: ThemeRepository = mockk()
val themeService = ThemeService(TsidFactory, themeRepository)
val themeService = ThemeService(themeFinder, themeWriter)
context("findThemeById") {
val themeId = 1L
test("조회 성공") {
test("정상 응답") {
val theme: ThemeEntity = ThemeFixture.create(id = themeId)
every {
themeRepository.findByIdOrNull(themeId)
themeFinder.findById(themeId)
} returns theme
theme.id shouldBe themeId
}
test("ID로 테마를 찾을 수 없으면 400 예외를 던진다.") {
test("테마를 찾을 수 없으면 예외 응답") {
every {
themeRepository.findByIdOrNull(themeId)
} returns null
themeFinder.findById(themeId)
} throws ThemeException(ThemeErrorCode.THEME_NOT_FOUND)
val exception = shouldThrow<ThemeException> {
shouldThrow<ThemeException> {
themeService.findById(themeId)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_NOT_FOUND
}
exception.errorCode shouldBe ThemeErrorCode.THEME_NOT_FOUND
}
}
context("findAllThemes") {
test("모든 테마를 조회한다.") {
val themes = listOf(ThemeFixture.create(id = 1, name = "t1"), ThemeFixture.create(id = 2, name = "t2"))
context("findThemes") {
test("정상 응답") {
val themes = listOf(
ThemeFixture.create(id = 1, name = "t1"),
ThemeFixture.create(id = 2, name = "t2")
)
every {
themeRepository.findAll()
themeFinder.findAll()
} returns themes
assertSoftly(themeService.findThemes()) {
this.themes.size shouldBe themes.size
this.themes[0].name shouldBe "t1"
this.themes[1].name shouldBe "t2"
val response = themeService.findThemes()
assertSoftly(response.themes) {
this.size shouldBe themes.size
it.map { theme -> theme.name } shouldContainExactly themes.map { theme -> theme.name }
}
}
}
context("save") {
context("findMostReservedThemes") {
test("7일 전 부터 1일 전 까지 조회") {
val count = 10
val startFrom = slot<LocalDate>()
val endAt = slot<LocalDate>()
every {
themeFinder.findMostReservedThemes(count, capture(startFrom), capture(endAt))
} returns emptyList()
themeService.findMostReservedThemes(count)
startFrom.captured shouldBe LocalDate.now().minusDays(7)
endAt.captured shouldBe LocalDate.now().minusDays(1)
}
}
context("createTheme") {
val request = ThemeCreateRequest(
name = "New Theme",
description = "Description",
thumbnail = "http://example.com/thumbnail.jpg"
)
test("저장 성공") {
test("정상 저장") {
every {
themeRepository.existsByName(request.name)
} returns false
every {
themeRepository.save(any())
themeWriter.create(request.name, request.description, request.thumbnail)
} returns ThemeFixture.create(
id = 1L,
id = 1,
name = request.name,
description = request.description,
thumbnail = request.thumbnail
)
val response: ThemeRetrieveResponse = themeService.createTheme(request)
val response: ThemeCreateResponse = themeService.createTheme(request)
assertSoftly(response) {
this.id shouldBe 1L
@ -91,32 +112,51 @@ class ThemeServiceTest : FunSpec({
}
}
test("테마 이름이 중복되면 409 예외를 던진다.") {
test("중복된 이름이 있으면 예외 응답") {
every {
themeRepository.existsByName(request.name)
} returns true
themeWriter.create(request.name, request.description, request.thumbnail)
} throws ThemeException(ThemeErrorCode.THEME_NAME_DUPLICATED)
val exception = shouldThrow<ThemeException> {
shouldThrow<ThemeException> {
themeService.createTheme(request)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_NAME_DUPLICATED
}
exception.errorCode shouldBe ThemeErrorCode.THEME_NAME_DUPLICATED
}
}
context("deleteById") {
test("이미 예약 중인 테마라면 409 예외를 던진다.") {
val themeId = 1L
val theme: ThemeEntity = ThemeFixture.create(id = themeId)
every {
themeRepository.isReservedTheme(themeId)
} returns true
test("정상 응답") {
every { themeFinder.findById(themeId) } returns theme
every { themeWriter.delete(theme) } just Runs
val exception = shouldThrow<ThemeException> {
shouldNotThrow<ThemeException> {
themeService.deleteTheme(themeId)
}
}
exception.errorCode shouldBe ThemeErrorCode.THEME_ALREADY_RESERVED
test("테마를 찾을 수 없으면 예외 응답") {
every { themeFinder.findById(themeId) } throws ThemeException(ThemeErrorCode.THEME_NOT_FOUND)
shouldThrow<ThemeException> {
themeService.deleteTheme(themeId)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_NOT_FOUND
}
}
test("예약이 있는 테마이면 예외 응답") {
every { themeFinder.findById(themeId) } returns theme
every { themeWriter.delete(theme) } throws ThemeException(ThemeErrorCode.THEME_ALREADY_RESERVED)
shouldThrow<ThemeException> {
themeService.deleteTheme(themeId)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_ALREADY_RESERVED
}
}
}
})

View File

@ -0,0 +1,76 @@
package roomescape.theme.implement
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.springframework.data.repository.findByIdOrNull
import roomescape.theme.exception.ThemeErrorCode
import roomescape.theme.exception.ThemeException
import roomescape.theme.infrastructure.persistence.ThemeRepository
import roomescape.util.ThemeFixture
import java.time.LocalDate
class ThemeFinderTest : FunSpec({
val themeRepository: ThemeRepository = mockk()
val themeFinder = ThemeFinder(themeRepository)
context("findAll") {
test("모든 테마를 조회한다.") {
every {
themeRepository.findAll()
} returns listOf(mockk(), mockk(), mockk())
themeRepository.findAll() shouldHaveSize 3
}
}
context("findById") {
val timeId = 1L
test("동일한 ID인 테마를 찾아 응답한다.") {
every {
themeRepository.findByIdOrNull(timeId)
} returns mockk()
themeFinder.findById(timeId)
verify(exactly = 1) {
themeRepository.findByIdOrNull(timeId)
}
}
test("동일한 ID인 테마가 없으면 실패한다.") {
every {
themeRepository.findByIdOrNull(timeId)
} returns null
shouldThrow<ThemeException> {
themeFinder.findById(timeId)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_NOT_FOUND
}
}
}
context("findMostReservedThemes") {
test("입력된 개수보다 조회된 개수가 작으면 조회된 개수만큼 반환한다.") {
val count = 10
val startFrom = LocalDate.now().minusDays(7)
val endAt = LocalDate.now().minusDays(1)
every {
themeRepository.findPopularThemes(startFrom, endAt, count)
} returns listOf(
ThemeFixture.create(id = 1, name = "name1"),
ThemeFixture.create(id = 2, name = "name2"),
ThemeFixture.create(id = 3, name = "name3"),
)
themeFinder.findMostReservedThemes(count, startFrom, endAt) shouldHaveSize 3
}
}
})

View File

@ -0,0 +1,79 @@
package roomescape.theme.implement
import io.kotest.assertions.throwables.shouldNotThrow
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk
import roomescape.theme.exception.ThemeErrorCode
import roomescape.theme.exception.ThemeException
import roomescape.theme.infrastructure.persistence.ThemeEntity
import roomescape.theme.infrastructure.persistence.ThemeRepository
import roomescape.util.ThemeFixture
class ThemeValidatorTest : FunSpec({
val themeRepository: ThemeRepository = mockk()
val themeValidator = ThemeValidator(themeRepository)
context("validateNameAlreadyExists") {
val name = "name"
test("같은 이름을 가진 테마가 있으면 예외를 던진다.") {
every {
themeRepository.existsByName(name)
} returns true
shouldThrow<ThemeException> {
themeValidator.validateNameAlreadyExists(name)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_NAME_DUPLICATED
}
}
test("같은 이름을 가진 테마가 없으면 종료한다.") {
every {
themeRepository.existsByName(name)
} returns false
shouldNotThrow<ThemeException> {
themeValidator.validateNameAlreadyExists(name)
}
}
}
context("validateIsReserved") {
test("입력된 id가 null 이면 예외를 던진다.") {
shouldThrow<ThemeException> {
themeValidator.validateIsReserved(ThemeFixture.create(id = null))
}.also {
it.errorCode shouldBe ThemeErrorCode.INVALID_REQUEST_VALUE
}
}
val theme: ThemeEntity = ThemeFixture.create(id = 1L, name = "name")
test("예약이 있는 테마이면 예외를 던진다.") {
every {
themeRepository.isReservedTheme(theme.id!!)
} returns true
shouldThrow<ThemeException> {
themeValidator.validateIsReserved(theme)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_ALREADY_RESERVED
}
}
test("예약이 없는 테마이면 종료한다.") {
every {
themeRepository.isReservedTheme(theme.id!!)
} returns false
shouldNotThrow<ThemeException> {
themeValidator.validateIsReserved(theme)
}
}
}
})

View File

@ -0,0 +1,86 @@
package roomescape.theme.implement
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.mockk.*
import roomescape.common.config.next
import roomescape.theme.exception.ThemeErrorCode
import roomescape.theme.exception.ThemeException
import roomescape.theme.infrastructure.persistence.ThemeEntity
import roomescape.theme.infrastructure.persistence.ThemeRepository
import roomescape.util.ThemeFixture
import roomescape.util.TsidFactory
class ThemeWriterTest : FunSpec({
val themeValidator: ThemeValidator = mockk()
val themeRepository: ThemeRepository = mockk()
val themeWriter = ThemeWriter(themeValidator, themeRepository, TsidFactory)
context("create") {
val name = "name"
val description = "description"
val thumbnail = "thumbnail"
test("중복된 이름이 있으면 실패한다.") {
every {
themeValidator.validateNameAlreadyExists(name)
} throws ThemeException(ThemeErrorCode.THEME_NAME_DUPLICATED)
shouldThrow<ThemeException> {
themeWriter.create(name, description, thumbnail)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_NAME_DUPLICATED
}
}
test("중복된 이름이 없으면 저장한다.") {
every {
themeValidator.validateNameAlreadyExists(name)
} just Runs
every {
themeRepository.save(any())
} returns ThemeFixture.create(name = name, description = description, thumbnail = thumbnail)
themeWriter.create(name, description, thumbnail)
verify(exactly = 1) {
themeRepository.save(any())
}
}
}
context("delete") {
val theme: ThemeEntity = ThemeFixture.create(id = TsidFactory.next())
test("예약이 있는 테마이면 실패한다.") {
every {
themeValidator.validateIsReserved(theme)
} throws ThemeException(ThemeErrorCode.THEME_ALREADY_RESERVED)
shouldThrow<ThemeException> {
themeWriter.delete(theme)
}.also {
it.errorCode shouldBe ThemeErrorCode.THEME_ALREADY_RESERVED
}
}
test("예약이 없는 테마이면 제거한다.") {
every {
themeValidator.validateIsReserved(theme)
} just Runs
every {
themeRepository.delete(theme)
} just Runs
themeWriter.delete(theme)
verify(exactly = 1) {
themeRepository.delete(theme)
}
}
}
})

View File

@ -5,7 +5,7 @@ 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 roomescape.theme.util.TestThemeCreateUtil
import roomescape.theme.util.TestThemeDataHelper
import java.time.LocalDate
@DataJpaTest(showSql = false)
@ -14,12 +14,13 @@ class ThemeRepositoryTest(
val entityManager: EntityManager
) : FunSpec() {
val helper = TestThemeDataHelper(entityManager, transactionTemplate = null)
init {
context("findTopNThemeBetweenStartDateAndEndDate") {
beforeTest {
for (i in 1..10) {
TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
helper.createThemeWithReservations(
name = "테마$i",
reservedCount = i,
date = LocalDate.now().minusDays(i.toLong()),
@ -27,7 +28,7 @@ class ThemeRepositoryTest(
}
}
test("지난 10일간 예약 수가 가장 많은 테마 5개를 조회한다.") {
test("지난 10일간 예약 수가 가장 많은 테마 5개를 조회") {
themeRepository.findPopularThemes(
LocalDate.now().minusDays(10),
LocalDate.now().minusDays(1),
@ -40,7 +41,7 @@ class ThemeRepositoryTest(
}
}
test("8일 전부터 5일 전까지 예약 수가 가장 많은 테마 3개를 조회한다.") {
test("8일 전부터 5일 전까지 예약 수가 가장 많은 테마 3개를 조회") {
themeRepository.findPopularThemes(
LocalDate.now().minusDays(8),
LocalDate.now().minusDays(5),
@ -53,9 +54,8 @@ class ThemeRepositoryTest(
}
}
test("예약 수가 동일하면 먼저 생성된 테마를 우선 조회한다.") {
TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
test("예약 수가 동일하면 먼저 생성된 테마를 우선 조회") {
helper.createThemeWithReservations(
name = "테마11",
reservedCount = 5,
date = LocalDate.now().minusDays(5),
@ -73,7 +73,7 @@ class ThemeRepositoryTest(
}
}
test("입력된 갯수보다 조회된 갯수가 작으면, 조회된 갯수만큼 반환한다.") {
test("입력된 갯수보다 조회된 갯수가 작으면, 조회된 갯수만큼 반환") {
themeRepository.findPopularThemes(
LocalDate.now().minusDays(10),
LocalDate.now().minusDays(6),
@ -83,7 +83,7 @@ class ThemeRepositoryTest(
}
}
test("입력된 갯수보다 조회된 갯수가 많으면, 입력된 갯수만큼 반환한다.") {
test("입력된 갯수보다 조회된 갯수가 많으면, 입력된 갯수만큼 반환") {
themeRepository.findPopularThemes(
LocalDate.now().minusDays(10),
LocalDate.now().minusDays(1),
@ -93,7 +93,7 @@ class ThemeRepositoryTest(
}
}
test("입력된 날짜 범위에 예약된 테마가 없을 경우 빈 리스트 반환한다.") {
test("입력된 날짜 범위에 예약된 테마가 없을 경우 빈 리스트 반환") {
themeRepository.findPopularThemes(
LocalDate.now().plusDays(1),
LocalDate.now().plusDays(10),
@ -106,26 +106,24 @@ class ThemeRepositoryTest(
context("existsByName ") {
val themeName = "test-theme"
beforeTest {
TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
helper.createThemeWithReservations(
name = themeName,
reservedCount = 0,
date = LocalDate.now()
)
}
test("테마 이름이 존재하면 true 반환한다.") {
test("테마 이름이 존재하면 true 반환") {
themeRepository.existsByName(themeName) shouldBe true
}
test("테마 이름이 존재하지 않으면 false 반환한다.") {
test("테마 이름이 존재하지 않으면 false 반환") {
themeRepository.existsByName(themeName.repeat(2)) shouldBe false
}
}
context("isReservedTheme") {
test("테마가 예약 중이면 true를 반환한다.") {
val theme = TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
test("테마가 예약 중이면 true 반환") {
val theme = helper.createThemeWithReservations(
name = "예약된 테마",
reservedCount = 1,
date = LocalDate.now()
@ -133,9 +131,8 @@ class ThemeRepositoryTest(
themeRepository.isReservedTheme(theme.id!!) shouldBe true
}
test("테마가 예약 중이 아니면 false를 반환한다.") {
val theme = TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
test("테마가 예약 중이 아니면 false 반환") {
val theme = helper.createThemeWithReservations(
name = "예약되지 않은 테마",
reservedCount = 0,
date = LocalDate.now()

View File

@ -9,7 +9,7 @@ import org.hamcrest.Matchers.equalTo
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.transaction.support.TransactionTemplate
import roomescape.theme.util.TestThemeCreateUtil
import roomescape.theme.util.TestThemeDataHelper
import roomescape.util.CleanerMode
import roomescape.util.DatabaseCleanerExtension
import java.time.LocalDate
@ -20,16 +20,16 @@ class MostReservedThemeApiTest(
@LocalServerPort val port: Int,
val transactionTemplate: TransactionTemplate,
val entityManager: EntityManager,
) : FunSpec({
extension(DatabaseCleanerExtension(mode = CleanerMode.AFTER_SPEC))
}) {
) : FunSpec({ extension(DatabaseCleanerExtension(mode = CleanerMode.AFTER_SPEC)) }) {
val helper = TestThemeDataHelper(entityManager, transactionTemplate)
init {
beforeSpec {
transactionTemplate.executeWithoutResult {
// 지난 7일간 예약된 테마 10개 생성
// 7일 전 ~ 1일 전 예약된 테마 10개 생성
for (i in 1..10) {
TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
helper.createThemeWithReservations(
name = "테마$i",
reservedCount = 1,
date = LocalDate.now().minusDays(Random.nextLong(1, 7))
@ -37,16 +37,22 @@ class MostReservedThemeApiTest(
}
// 8일 전 예약된 테마 1개 생성
TestThemeCreateUtil.createThemeWithReservations(
entityManager = entityManager,
helper.createThemeWithReservations(
name = "테마11",
reservedCount = 1,
date = LocalDate.now().minusDays(8)
)
// 당일 예약된 테마 1개 생성
helper.createThemeWithReservations(
name = "테마12",
reservedCount = 1,
date = LocalDate.now()
)
}
}
context("지난 주 가장 많이 예약된 테마 API") {
context("GET /themes/most-reserved-last-week") {
val endpoint = "/themes/most-reserved-last-week"
test("count 파라미터가 없으면 10개를 반환한다") {
@ -87,8 +93,8 @@ class MostReservedThemeApiTest(
}
test("지난 7일 동안의 예약만 집계한다") {
// 8일 전에 예약된 테마는 집계에서 제외되어야 한다.
val count = 11
// beforeSpec 에서 정의한 테스트 데이터 중, 8일 전 / 당일 예약 테마는 제외되어야 한다.
val count = 12
Given {
port(port)
param("count", count)

View File

@ -1,42 +1,32 @@
package roomescape.theme.web
import com.ninjasquad.springmockk.MockkBean
import com.ninjasquad.springmockk.SpykBean
import io.kotest.assertions.assertSoftly
import io.kotest.matchers.collections.shouldContainAll
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.just
import io.mockk.runs
import org.hamcrest.Matchers.equalTo
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import roomescape.auth.exception.AuthErrorCode
import roomescape.common.exception.CommonErrorCode
import roomescape.theme.business.ThemeService
import roomescape.theme.exception.ThemeErrorCode
import roomescape.theme.infrastructure.persistence.ThemeRepository
import roomescape.theme.exception.ThemeException
import roomescape.util.RoomescapeApiTest
import roomescape.util.ThemeFixture
@WebMvcTest(ThemeController::class)
class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
@SpykBean
class ThemeControllerTest(val mockMvc: MockMvc) : RoomescapeApiTest() {
@MockkBean
private lateinit var themeService: ThemeService
@MockkBean
private lateinit var themeRepository: ThemeRepository
init {
Given("모든 테마를 조회할 때") {
Given("GET /themes 요청을") {
val endpoint = "/themes"
When("로그인 상태가 아니라") {
When("로그인 하지 않은 사용자가 보내") {
doNotLogin()
Then("에러 응답을 받는다.") {
Then("예외 응답") {
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
runGetTest(
mockMvc = mockMvc,
@ -52,34 +42,32 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
When("로그인 상태라면") {
loginAsUser()
Then("조회에 성공한다.") {
Then("정상 응답") {
every {
themeRepository.findAll()
themeService.findThemes()
} returns listOf(
ThemeFixture.create(id = 1, name = "theme1"),
ThemeFixture.create(id = 2, name = "theme2"),
ThemeFixture.create(id = 3, name = "theme3")
)
).toRetrieveListResponse()
val response: ThemeRetrieveListResponse = runGetTest(
runGetTest(
mockMvc = mockMvc,
endpoint = endpoint,
) {
status { isOk() }
content {
contentType(MediaType.APPLICATION_JSON)
jsonPath("$.data.themes[0].id") { value(1) }
jsonPath("$.data.themes[1].id") { value(2) }
jsonPath("$.data.themes[2].id") { value(3) }
}
}.andReturn().readValue(ThemeRetrieveListResponse::class.java)
assertSoftly(response.themes) {
it.size shouldBe 3
it.map { m -> m.name } shouldContainAll listOf("theme1", "theme2", "theme3")
}
}
}
}
Given("테마를 추가할 때") {
Given("POST /themes 요청을") {
val endpoint = "/themes"
val request = ThemeCreateRequest(
name = "theme1",
@ -87,9 +75,10 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
thumbnail = "http://example.com/thumbnail1.jpg"
)
When("로그인 상태가 아니라") {
When("로그인 하지 않은 사용자가 보내") {
doNotLogin()
Then("에러 응답을 받는다.") {
Then("예외 응답") {
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
runPostTest(
mockMvc = mockMvc,
@ -102,9 +91,10 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
}
}
When("관리자가 아닌 회원은") {
When("관리자가 아닌 사용자가 보내면") {
loginAsUser()
Then("에러 응답을 받는다.") {
Then("예외 응답") {
val expectedError = AuthErrorCode.ACCESS_DENIED
runPostTest(
mockMvc = mockMvc,
@ -117,15 +107,17 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
}
}
When("동일한 이름의 테마가 있으면") {
When("관리자가 보낼 때") {
beforeTest {
loginAsAdmin()
}
Then("동일한 이름의 테마가 있으면 예외 응답") {
val expectedError = ThemeErrorCode.THEME_NAME_DUPLICATED
Then("에러 응답을 받는다.") {
every {
themeRepository.existsByName(request.name)
} returns true
themeService.createTheme(request)
} throws ThemeException(expectedError)
runPostTest(
mockMvc = mockMvc,
@ -136,13 +128,8 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
jsonPath("$.code") { value(expectedError.errorCode) }
}
}
}
When("값이 잘못 입력되면 400 에러를 응답한다") {
beforeTest {
loginAsAdmin()
}
When("입력 값의 형식이 잘못되면 예외 응답") {
val request = ThemeCreateRequest(
name = "theme1",
description = "description1",
@ -150,12 +137,15 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
)
fun runTest(request: ThemeCreateRequest) {
val expectedError = CommonErrorCode.INVALID_INPUT_VALUE
runPostTest(
mockMvc = mockMvc,
endpoint = endpoint,
body = request,
) {
status { isBadRequest() }
status { isEqualTo(expectedError.httpStatus.value()) }
jsonPath("$.code") { value(expectedError.errorCode) }
}
}
@ -190,9 +180,7 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
}
}
When("저장에 성공하면") {
loginAsAdmin()
Then("정상 응답") {
val theme = ThemeFixture.create(
id = 1,
name = request.name,
@ -202,14 +190,8 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
every {
themeService.createTheme(request)
} returns ThemeRetrieveResponse(
id = theme.id!!,
name = theme.name,
description = theme.description,
thumbnail = theme.thumbnail
)
} returns theme.toCreateResponse()
Then("201 응답을 받는다.") {
runPostTest(
mockMvc = mockMvc,
endpoint = endpoint,
@ -228,13 +210,14 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
}
}
Given("테마를 제거할 때") {
Given("DELETE /themes/{id} 요청을") {
val themeId = 1L
val endpoint = "/themes/$themeId"
When("로그인 상태가 아니라면") {
When("관리자가 아닌 사용자가 보내면 예외 응답") {
Then("로그인 하지 않은 경우") {
doNotLogin()
Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
runDeleteTest(
mockMvc = mockMvc,
@ -244,11 +227,10 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
jsonPath("$.code", equalTo(expectedError.errorCode))
}
}
}
When("관리자가 아닌 회원은") {
Then("로그인은 하였으나 관리자가 아닌 경우") {
loginAsUser()
Then("에러 응답을 받는다.") {
val expectedError = AuthErrorCode.ACCESS_DENIED
runDeleteTest(
mockMvc = mockMvc,
@ -260,14 +242,16 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
}
}
When("이미 예약된 테마이면") {
When("관리자가 보낼 때") {
beforeTest {
loginAsAdmin()
val expectedError = ThemeErrorCode.THEME_ALREADY_RESERVED
}
Then("에러 응답을 받는다.") {
Then("이미 예약된 테마이면 예외 응답") {
val expectedError = ThemeErrorCode.THEME_ALREADY_RESERVED
every {
themeRepository.isReservedTheme(themeId)
} returns true
themeService.deleteTheme(themeId)
} throws ThemeException(expectedError)
runDeleteTest(
mockMvc = mockMvc,
@ -277,20 +261,10 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
jsonPath("$.code") { value(expectedError.errorCode) }
}
}
}
When("정상적으로 제거되면") {
loginAsAdmin()
Then("정상 응답") {
every { themeService.deleteTheme(themeId) } returns Unit
every {
themeRepository.isReservedTheme(themeId)
} returns false
every {
themeRepository.deleteById(themeId)
} just runs
Then("204 응답을 받는다.") {
runDeleteTest(
mockMvc = mockMvc,
endpoint = endpoint,