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, propsNameIfList: String? = null) { val jsonDefaultPath = propsNameIfList?.let { "data.$propsNameIfList" } ?: "data" val json = extract().jsonPath().get(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" }