From 915ed71c998134b35f1d04c7a23ebde3a4cd0f1a Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 3 Sep 2025 10:59:46 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8A=94=20RestAssured=20=EC=9C=A0=ED=8B=B8=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/util/RestAssuredUtils.kt | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/test/kotlin/roomescape/util/RestAssuredUtils.kt diff --git a/src/test/kotlin/roomescape/util/RestAssuredUtils.kt b/src/test/kotlin/roomescape/util/RestAssuredUtils.kt new file mode 100644 index 00000000..168630ff --- /dev/null +++ b/src/test/kotlin/roomescape/util/RestAssuredUtils.kt @@ -0,0 +1,106 @@ +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.auth.web.LoginRequest +import roomescape.common.config.next +import roomescape.member.infrastructure.persistence.MemberEntity +import roomescape.member.infrastructure.persistence.MemberRepository +import roomescape.member.infrastructure.persistence.Role + +class LoginUtil( + private val memberRepository: MemberRepository +) { + 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 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}") + } +}