194 lines
6.2 KiB
Kotlin

package roomescape.supports
import io.restassured.module.kotlin.extensions.Extract
import io.restassured.module.kotlin.extensions.Given
import io.restassured.module.kotlin.extensions.Then
import io.restassured.module.kotlin.extensions.When
import io.restassured.response.Response
import io.restassured.response.ValidatableResponse
import io.restassured.specification.RequestSpecification
import org.hamcrest.CoreMatchers.equalTo
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import roomescape.admin.infrastructure.persistence.AdminEntity
import roomescape.admin.infrastructure.persistence.AdminRepository
import roomescape.auth.web.LoginRequest
import roomescape.common.dto.PrincipalType
import roomescape.common.exception.ErrorCode
import roomescape.store.infrastructure.persistence.StoreRepository
import roomescape.user.infrastructure.persistence.UserEntity
import roomescape.user.infrastructure.persistence.UserRepository
import roomescape.user.web.UserCreateRequest
import kotlin.random.Random
class AuthUtil(
private val userRepository: UserRepository,
private val adminRepository: AdminRepository,
private val storeRepository: StoreRepository,
) {
fun createAdmin(admin: AdminEntity): AdminEntity {
val storeId = admin.storeId
if (storeId != null && storeRepository.findByIdOrNull(storeId) == null) {
storeRepository.save(
StoreFixture.create(
id = storeId,
businessRegNum = generateBusinessRegNum(),
)
)
}
return adminRepository.save(admin)
}
fun signup(request: UserCreateRequest): UserEntity {
val userId: Long = Given {
contentType(MediaType.APPLICATION_JSON_VALUE)
body(request)
} When {
post("/users")
} Then {
statusCode(HttpStatus.OK.value())
} Extract {
path("data.id")
}
return userRepository.findByIdOrNull(userId)
?: throw AssertionError("Unexpected Exception Occurred.")
}
fun adminLogin(admin: AdminEntity): String {
val saved = createAdmin(admin)
val requestBody = LoginRequest(saved.account, saved.password, PrincipalType.ADMIN)
return Given {
contentType(MediaType.APPLICATION_JSON_VALUE)
body(requestBody)
} When {
post("/auth/login")
} Then {
statusCode(200)
} Extract {
path("data.accessToken")
}
}
fun defaultStoreAdminLogin(): String = adminLogin(AdminFixture.storeDefault)
fun defaultHqAdminLogin(): String = adminLogin(AdminFixture.hqDefault)
fun userLogin(user: UserEntity): String {
if (userRepository.findByEmail(user.email) == null) {
userRepository.save(user)
}
return Given {
contentType(MediaType.APPLICATION_JSON_VALUE)
body(LoginRequest(account = user.email, password = user.password, principalType = PrincipalType.USER))
} When {
post("/auth/login")
} Then {
statusCode(200)
} Extract {
path("data.accessToken")
}
}
fun defaultUserLogin(): String = userLogin(UserFixture.default)
fun defaultUser(): UserEntity = userRepository.findByEmail(UserFixture.default.email)
?: userRepository.save(UserFixture.default)
}
fun runTest(
token: String? = null,
using: RequestSpecification.() -> RequestSpecification = { this },
on: RequestSpecification.() -> Response,
expect: ValidatableResponse.() -> Unit
): ValidatableResponse {
return Given {
contentType(MediaType.APPLICATION_JSON_VALUE)
token?.also { header("Authorization", "Bearer $token") }
using()
} When {
on()
} Then {
expect()
}
}
fun runExceptionTest(
token: String? = null,
method: HttpMethod,
requestBody: Any? = null,
endpoint: String,
expectedErrorCode: ErrorCode
): ValidatableResponse {
return runTest(
token = token,
using = {
requestBody?.let { body(requestBody) } ?: this
},
on = {
when (method) {
HttpMethod.GET -> get(endpoint)
HttpMethod.POST -> post(endpoint)
HttpMethod.PATCH -> patch(endpoint)
HttpMethod.DELETE -> delete(endpoint)
else -> {
throw AssertionError("Unsupported HTTP method: $method")
}
}
},
expect = {
statusCode(expectedErrorCode.httpStatus.value())
body("code", equalTo(expectedErrorCode.errorCode))
}
)
}
/**
* @param props: RestAssured 응답 Body 에서 존재 & Null 여부를 확인할 프로퍼티 이름
*/
fun ValidatableResponse.assertProperties(props: Set<String>, propsNameIfList: String? = null) {
val jsonDefaultPath = propsNameIfList?.let { "data.$propsNameIfList" } ?: "data"
val json = extract().jsonPath().get<Any>(jsonDefaultPath)
fun checkMap(map: Map<*, *>) {
val responseKeys = map.keys.map { it.toString() }.toSet()
val expectedKeys = props
val missing = expectedKeys - responseKeys
val extra = responseKeys - expectedKeys
require(missing.isEmpty() && extra.isEmpty()) {
buildString {
if (missing.isNotEmpty()) append("Missing keys: $missing. ")
if (extra.isNotEmpty()) append("Unexpected keys: $extra.")
}
}
expectedKeys.forEach { key ->
require(map[key] != null) { "Property '$key' is null" }
}
}
when (json) {
is List<*> -> json.forEach { item ->
val map = item as? Map<*, *> ?: error("Expected Map but got ${item?.javaClass}")
checkMap(map)
}
is Map<*, *> -> checkMap(json)
else -> error("Unexpected data type: ${json::class}")
}
}
private fun generateBusinessRegNum(): String {
val part1 = Random.nextInt(100, 1000)
val part2 = Random.nextInt(10, 100)
val part3 = Random.nextInt(10000, 100000)
return "$part1-$part2-$part3"
}