package roomescape.util import com.fasterxml.jackson.databind.ObjectMapper import com.ninjasquad.springmockk.MockkBean import com.ninjasquad.springmockk.SpykBean import io.kotest.core.spec.style.BehaviorSpec import io.mockk.every import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpHeaders import org.springframework.http.MediaType import org.springframework.test.web.servlet.* import roomescape.auth.exception.AuthErrorCode import roomescape.auth.exception.AuthException import roomescape.auth.infrastructure.jwt.JwtHandler import roomescape.auth.web.support.AuthInterceptor import roomescape.auth.web.support.MemberIdResolver import roomescape.common.config.JacksonConfig import roomescape.member.business.MemberService import roomescape.member.infrastructure.persistence.MemberEntity import roomescape.member.infrastructure.persistence.MemberRepository import roomescape.util.MemberFixture.NOT_LOGGED_IN_USERID abstract class RoomescapeApiTest : BehaviorSpec() { @SpykBean private lateinit var authInterceptor: AuthInterceptor @SpykBean private lateinit var memberIdResolver: MemberIdResolver @SpykBean lateinit var memberService: MemberService @MockkBean lateinit var memberRepository: MemberRepository @MockkBean lateinit var jwtHandler: JwtHandler val objectMapper: ObjectMapper = JacksonConfig().objectMapper() val admin: MemberEntity = MemberFixture.admin() val user: MemberEntity = MemberFixture.user() fun runGetTest( mockMvc: MockMvc, endpoint: String, log: Boolean = false, assert: MockMvcResultMatchersDsl.() -> Unit ): ResultActionsDsl = mockMvc.get(endpoint) { header(HttpHeaders.COOKIE, "accessToken=token") }.apply { log.takeIf { it }?.let { this.andDo { print() } } }.andExpect(assert) fun runPostTest( mockMvc: MockMvc, endpoint: String, body: Any? = null, log: Boolean = false, assert: MockMvcResultMatchersDsl.() -> Unit ): ResultActionsDsl = mockMvc.post(endpoint) { this.header(HttpHeaders.COOKIE, "accessToken=token") body?.let { this.contentType = MediaType.APPLICATION_JSON this.content = objectMapper.writeValueAsString(it) } }.apply { log.takeIf { it }?.let { this.andDo { print() } } }.andExpect(assert) fun runDeleteTest( mockMvc: MockMvc, endpoint: String, log: Boolean = false, assert: MockMvcResultMatchersDsl.() -> Unit ): ResultActionsDsl = mockMvc.delete(endpoint) { header(HttpHeaders.COOKIE, "accessToken=token") }.apply { log.takeIf { it }?.let { this.andDo { print() } } }.andExpect(assert) fun loginAsAdmin() { every { jwtHandler.getMemberIdFromToken(any()) } returns admin.id!! every { memberRepository.existsById(admin.id!!) } returns true every { memberRepository.findByIdOrNull(admin.id!!) } returns admin } fun loginAsUser() { every { jwtHandler.getMemberIdFromToken(any()) } returns user.id!! every { memberRepository.existsById(user.id!!) } returns true every { memberRepository.findByIdOrNull(user.id!!) } returns user } fun doNotLogin() { every { jwtHandler.getMemberIdFromToken(any()) } throws AuthException(AuthErrorCode.INVALID_TOKEN) every { memberRepository.existsById(NOT_LOGGED_IN_USERID) } returns false every { memberRepository.findByIdOrNull(NOT_LOGGED_IN_USERID) } returns null } fun MvcResult.readValue(valueType: Class): T = this.response.contentAsString .takeIf { it.isNotBlank() } ?.let { readValue(it, valueType) } ?: throw RuntimeException(""" [Test] Exception occurred while reading response json: ${this.response.contentAsString} with value type: $valueType """.trimIndent()) fun readValue(responseJson: String, valueType: Class): T = objectMapper .readTree(responseJson)["data"] ?.let { objectMapper.convertValue(it, valueType) } ?: throw RuntimeException(""" [Test] Exception occurred while reading response json: $responseJson with value type: $valueType """.trimIndent()) }