[#44] 매장 기능 도입 #45

Merged
pricelees merged 116 commits from feat/#44 into main 2025-09-20 03:15:06 +00:00
2 changed files with 159 additions and 47 deletions
Showing only changes of commit c3eceedea1 - Show all commits

View File

@ -64,12 +64,53 @@ object AdminFixture {
storeId = null
)
fun createStoreAdmin(
id: Long = tsidFactory.next(),
account: String = "admin",
password: String = "adminPassword",
name: String = "admin12345",
phone: String = randomPhoneNumber(),
storeId: Long = tsidFactory.next(),
permissionLevel: AdminPermissionLevel = AdminPermissionLevel.FULL_ACCESS
): AdminEntity {
return create(
id = id,
account = account,
password = password,
name = name,
phone = phone,
type = AdminType.STORE,
storeId = storeId,
permissionLevel = permissionLevel
)
}
fun createHqAdmin(
id: Long = tsidFactory.next(),
account: String = "admin",
password: String = "adminPassword",
name: String = "admin12345",
phone: String = randomPhoneNumber(),
permissionLevel: AdminPermissionLevel = AdminPermissionLevel.FULL_ACCESS
): AdminEntity {
return create(
id = id,
account = account,
password = password,
name = name,
phone = phone,
type = AdminType.HQ,
storeId = null,
permissionLevel = permissionLevel
)
}
fun create(
id: Long = tsidFactory.next(),
account: String = "admin",
password: String = "adminPassword",
name: String = "admin12345",
phone: String = "01012345678",
phone: String = randomPhoneNumber(),
type: AdminType = AdminType.STORE,
storeId: Long? = tsidFactory.next(),
permissionLevel: AdminPermissionLevel = AdminPermissionLevel.FULL_ACCESS

View File

@ -11,6 +11,7 @@ import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import roomescape.admin.infrastructure.persistence.AdminPermissionLevel
import roomescape.admin.infrastructure.persistence.AdminType
import roomescape.auth.exception.AuthErrorCode
import roomescape.theme.business.MIN_DURATION
import roomescape.theme.business.MIN_PARTICIPANTS
@ -52,9 +53,22 @@ class ThemeApiTest(
)
}
listOf(AdminPermissionLevel.READ_SUMMARY, AdminPermissionLevel.READ_ALL).forEach {
test("권한이 ${it}인 관리자") {
val admin = AdminFixture.create(permissionLevel = it)
AdminPermissionLevel.entries.forEach {
test("관리자: Type=${AdminType.STORE} / Permission=${it}") {
val admin = AdminFixture.createStoreAdmin(permissionLevel = it)
runExceptionTest(
token = authUtil.adminLogin(admin),
method = HttpMethod.POST,
requestBody = createRequest,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
if (it == AdminPermissionLevel.READ_ALL || it == AdminPermissionLevel.READ_SUMMARY) {
test("관리자: Type=${AdminType.HQ} / Permission=${it}") {
val admin = AdminFixture.createHqAdmin(permissionLevel = it)
runExceptionTest(
token = authUtil.adminLogin(admin),
@ -66,10 +80,11 @@ class ThemeApiTest(
}
}
}
}
test("정상 생성 및 감사 정보 확인") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
runTest(
token = token,
@ -97,7 +112,7 @@ class ThemeApiTest(
}
test("이미 동일한 이름의 테마가 있으면 실패한다.") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
val commonName = "test123"
dummyInitializer.createTheme(
adminToken = token,
@ -120,7 +135,7 @@ class ThemeApiTest(
}
test("금액이 ${MIN_PRICE}원 미만이면 실패한다.") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
runTest(
token = token,
using = {
@ -143,7 +158,7 @@ class ThemeApiTest(
}
test("field: availableMinutes") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
runTest(
token = token,
using = {
@ -157,7 +172,7 @@ class ThemeApiTest(
}
test("field: expectedMinutesFrom") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
runTest(
token = token,
using = {
@ -171,7 +186,7 @@ class ThemeApiTest(
}
test("field: expectedMinutesTo") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
runTest(
token = token,
using = {
@ -187,7 +202,7 @@ class ThemeApiTest(
context("시간 범위가 잘못 지정되면 실패한다.") {
test("최소 예상 시간 > 최대 예상 시간") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
runTest(
token = token,
using = {
@ -204,7 +219,7 @@ class ThemeApiTest(
}
test("최대 예상 시간 > 이용 가능 시간") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
runTest(
token = token,
using = {
@ -234,7 +249,7 @@ class ThemeApiTest(
}
test("field: minParticipants") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
runTest(
token = token,
using = {
@ -248,7 +263,7 @@ class ThemeApiTest(
}
test("field: maxParticipants") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
runTest(
token = token,
using = {
@ -264,7 +279,7 @@ class ThemeApiTest(
context("인원 범위가 잘못 지정되면 실패한다.") {
test("최소 인원 > 최대 인원") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
runTest(
token = token,
using = {
@ -284,7 +299,7 @@ class ThemeApiTest(
context("입력된 모든 ID에 대한 테마를 조회한다.") {
test("정상 응답") {
val adminToken = authUtil.defaultStoreAdminLogin()
val adminToken = authUtil.defaultHqAdminLogin()
val themeSize = 3
val themeIds = mutableListOf<Long>()
@ -309,7 +324,7 @@ class ThemeApiTest(
}
test("없는 테마가 있으면 생략한다.") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
val themeSize = 3
val themeIds = mutableListOf<Long>()
@ -359,11 +374,25 @@ class ThemeApiTest(
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
AdminPermissionLevel.entries.forEach {
test("관리자: Type=${AdminType.STORE} / Permission=${it}") {
val admin = AdminFixture.createStoreAdmin(permissionLevel = it)
runExceptionTest(
token = authUtil.adminLogin(admin),
method = HttpMethod.POST,
requestBody = createRequest,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
}
}
test("비공개 테마까지 포함하여 간단한 정보만 조회된다.") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
requests.forEach { dummyInitializer.createTheme(token, it) }
runTest(
@ -384,7 +413,7 @@ class ThemeApiTest(
context("예약 페이지에서 테마를 조회한다.") {
test("공개된 테마의 전체 정보가 조회된다.") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
listOf(
createRequest.copy(name = "open", isOpen = true),
createRequest.copy(name = "close", isOpen = false)
@ -434,8 +463,21 @@ class ThemeApiTest(
)
}
test("권한이 ${AdminPermissionLevel.READ_SUMMARY}인 관리자") {
val admin = AdminFixture.create(permissionLevel = AdminPermissionLevel.READ_SUMMARY)
AdminPermissionLevel.entries.forEach {
test("관리자: Type=${AdminType.STORE} / Permission=${it}") {
val admin = AdminFixture.createStoreAdmin(permissionLevel = it)
runExceptionTest(
token = authUtil.adminLogin(admin),
method = HttpMethod.GET,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
if (it == AdminPermissionLevel.READ_SUMMARY) {
test("관리자: Type=${AdminType.HQ} / Permission=${it}") {
val admin = AdminFixture.createHqAdmin(permissionLevel = it)
runExceptionTest(
token = authUtil.adminLogin(admin),
@ -445,9 +487,11 @@ class ThemeApiTest(
)
}
}
}
}
test("정상 응답") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
val createdTheme = dummyInitializer.createTheme(
adminToken = token,
request = createRequest
@ -475,7 +519,7 @@ class ThemeApiTest(
test("테마가 없으면 실패한다.") {
runExceptionTest(
token = authUtil.defaultStoreAdminLogin(),
token = authUtil.defaultHqAdminLogin(),
method = HttpMethod.GET,
endpoint = "/admin/themes/$INVALID_PK",
expectedErrorCode = ThemeErrorCode.THEME_NOT_FOUND
@ -505,9 +549,21 @@ class ThemeApiTest(
)
}
listOf(AdminPermissionLevel.READ_SUMMARY, AdminPermissionLevel.READ_ALL).forEach {
test("권한이 ${it}인 관리자") {
val admin = AdminFixture.create(permissionLevel = it)
AdminPermissionLevel.entries.forEach {
test("관리자: Type=${AdminType.STORE} / Permission=${it}") {
val admin = AdminFixture.createStoreAdmin(permissionLevel = it)
runExceptionTest(
token = authUtil.adminLogin(admin),
method = HttpMethod.DELETE,
endpoint = endpoint,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
if (it == AdminPermissionLevel.READ_ALL || it == AdminPermissionLevel.READ_SUMMARY) {
test("관리자: Type=${AdminType.HQ} / Permission=${it}") {
val admin = AdminFixture.createHqAdmin(permissionLevel = it)
runExceptionTest(
token = authUtil.adminLogin(admin),
@ -518,9 +574,10 @@ class ThemeApiTest(
}
}
}
}
test("정상 삭제") {
val token = authUtil.defaultStoreAdminLogin()
val token = authUtil.defaultHqAdminLogin()
val createdTheme = dummyInitializer.createTheme(
adminToken = token,
request = createRequest
@ -541,7 +598,7 @@ class ThemeApiTest(
test("테마가 없으면 실패한다.") {
runExceptionTest(
token = authUtil.defaultStoreAdminLogin(),
token = authUtil.defaultHqAdminLogin(),
method = HttpMethod.DELETE,
endpoint = "/admin/themes/$INVALID_PK",
expectedErrorCode = ThemeErrorCode.THEME_NOT_FOUND
@ -573,9 +630,22 @@ class ThemeApiTest(
)
}
listOf(AdminPermissionLevel.READ_SUMMARY, AdminPermissionLevel.READ_ALL).forEach {
test("권한이 ${it}인 관리자") {
val admin = AdminFixture.create(permissionLevel = it)
AdminPermissionLevel.entries.forEach {
test("관리자: Type=${AdminType.STORE} / Permission=${it}") {
val admin = AdminFixture.createStoreAdmin(permissionLevel = it)
runExceptionTest(
token = authUtil.adminLogin(admin),
method = HttpMethod.PATCH,
endpoint = endpoint,
requestBody = request,
expectedErrorCode = AuthErrorCode.ACCESS_DENIED
)
}
if (it == AdminPermissionLevel.READ_ALL || it == AdminPermissionLevel.READ_SUMMARY) {
test("관리자: Type=${AdminType.HQ} / Permission=${it}") {
val admin = AdminFixture.createHqAdmin(permissionLevel = it)
runExceptionTest(
token = authUtil.adminLogin(admin),
@ -587,16 +657,17 @@ class ThemeApiTest(
}
}
}
}
val updateRequest = ThemeUpdateRequest(name = "modified")
test("정상 수정 및 감사 정보 변경 확인") {
val createdTheme: ThemeEntity = dummyInitializer.createTheme(
adminToken = authUtil.defaultStoreAdminLogin(),
adminToken = authUtil.defaultHqAdminLogin(),
request = createRequest.copy(name = "theme-${Random.nextInt()}")
)
val otherAdminToken: String = authUtil.adminLogin(
AdminFixture.create(account = "hello", phone = "0101828402")
AdminFixture.createHqAdmin(permissionLevel = AdminPermissionLevel.WRITABLE)
)
runTest(
@ -622,12 +693,12 @@ class ThemeApiTest(
test("입력값이 없으면 수정하지 않는다.") {
val createdTheme: ThemeEntity = dummyInitializer.createTheme(
adminToken = authUtil.defaultStoreAdminLogin(),
adminToken = authUtil.defaultHqAdminLogin(),
request = createRequest.copy(name = "theme-${Random.nextInt()}")
)
runTest(
token = authUtil.defaultStoreAdminLogin(),
token = authUtil.defaultHqAdminLogin(),
using = {
body(ThemeUpdateRequest())
},
@ -647,7 +718,7 @@ class ThemeApiTest(
test("테마가 없으면 실패한다.") {
runExceptionTest(
token = authUtil.defaultStoreAdminLogin(),
token = authUtil.defaultHqAdminLogin(),
method = HttpMethod.PATCH,
endpoint = "/admin/themes/$INVALID_PK",
requestBody = updateRequest,
@ -656,7 +727,7 @@ class ThemeApiTest(
}
test("금액이 ${MIN_PRICE}원 미만이면 실패한다.") {
val adminToken = authUtil.defaultStoreAdminLogin()
val adminToken = authUtil.defaultHqAdminLogin()
val createdTheme: ThemeEntity = dummyInitializer.createTheme(
adminToken = adminToken,
request = createRequest.copy(name = "theme-${Random.nextInt()}")
@ -676,7 +747,7 @@ class ThemeApiTest(
lateinit var createdTheme: ThemeEntity
beforeTest {
adminToken = authUtil.defaultStoreAdminLogin()
adminToken = authUtil.defaultHqAdminLogin()
createdTheme = dummyInitializer.createTheme(
adminToken = adminToken,
request = createRequest.copy(name = "theme-${Random.nextInt()}")
@ -719,7 +790,7 @@ class ThemeApiTest(
lateinit var createdTheme: ThemeEntity
beforeTest {
adminToken = authUtil.defaultStoreAdminLogin()
adminToken = authUtil.defaultHqAdminLogin()
createdTheme = dummyInitializer.createTheme(
adminToken = adminToken,
request = createRequest.copy(name = "theme-${Random.nextInt()}")
@ -760,7 +831,7 @@ class ThemeApiTest(
lateinit var createdTheme: ThemeEntity
beforeTest {
adminToken = authUtil.defaultStoreAdminLogin()
adminToken = authUtil.defaultHqAdminLogin()
createdTheme = dummyInitializer.createTheme(
adminToken = adminToken,
request = createRequest.copy(name = "theme-${Random.nextInt()}")
@ -793,7 +864,7 @@ class ThemeApiTest(
lateinit var createdTheme: ThemeEntity
beforeTest {
adminToken = authUtil.defaultStoreAdminLogin()
adminToken = authUtil.defaultHqAdminLogin()
createdTheme = dummyInitializer.createTheme(
adminToken = adminToken,
request = createRequest.copy(name = "theme-${Random.nextInt()}")