package roomescape.util 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.springframework.http.MediaType import roomescape.admin.infrastructure.persistence.AdminEntity import roomescape.admin.infrastructure.persistence.AdminRepository import roomescape.auth.web.LoginRequest import roomescape.auth.web.LoginRequestV2 import roomescape.common.config.next import roomescape.common.dto.PrincipalType import roomescape.member.infrastructure.persistence.* class AuthUtil( private val memberRepository: MemberRepository, private val userRepository: UserRepository, private val adminRepository: AdminRepository ) { fun login(email: String, password: String, role: Role = Role.MEMBER): String { if (!memberRepository.existsByEmail(email)) { memberRepository.save( MemberEntity( _id = tsidFactory.next(), email = email, password = password, name = email.split("@").first(), role = role ) ) } return Given { contentType(MediaType.APPLICATION_JSON_VALUE) body(LoginRequest(email, password)) } When { post("/login") } Then { statusCode(200) } Extract { path("data.accessToken") } } fun loginAsAdmin(): String { return login(MemberFixture.admin.email, MemberFixture.admin.password, Role.ADMIN) } fun loginAsUser(): String { return login(MemberFixture.user.email, MemberFixture.user.password) } fun getUser(): MemberEntity = memberRepository.findByEmailAndPassword( MemberFixture.user.email, MemberFixture.user.password ) ?: throw AssertionError("Unexpected Exception Occurred.") fun adminLogin(admin: AdminEntity): String { if (adminRepository.findByAccount(admin.account) == null) { adminRepository.save(admin) } return Given { contentType(MediaType.APPLICATION_JSON_VALUE) body(LoginRequestV2(account = admin.account, password = admin.password, principalType = PrincipalType.ADMIN)) } When { post("/auth/login") } Then { statusCode(200) } Extract { path("data.accessToken") } } fun defaultAdminLogin(): String = adminLogin(AdminFixture.default) fun userLogin(user: UserEntity): String { if (userRepository.findByEmail(user.email) == null) { userRepository.save(user) } return Given { contentType(MediaType.APPLICATION_JSON_VALUE) body(LoginRequestV2(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 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() } } /** * @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}") } }