generated from pricelees/issue-pr-template
[#41] 예약 스키마 재정의 #42
@ -1,82 +0,0 @@
|
|||||||
package roomescape.auth.business
|
|
||||||
|
|
||||||
import io.kotest.assertions.assertSoftly
|
|
||||||
import io.kotest.assertions.throwables.shouldThrow
|
|
||||||
import io.kotest.core.spec.style.BehaviorSpec
|
|
||||||
import io.kotest.matchers.shouldBe
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import roomescape.auth.exception.AuthErrorCode
|
|
||||||
import roomescape.auth.exception.AuthException
|
|
||||||
import roomescape.auth.infrastructure.jwt.JwtHandler
|
|
||||||
import roomescape.member.exception.MemberErrorCode
|
|
||||||
import roomescape.member.exception.MemberException
|
|
||||||
import roomescape.member.implement.MemberFinder
|
|
||||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
|
||||||
import roomescape.util.JwtFixture
|
|
||||||
import roomescape.util.MemberFixture
|
|
||||||
|
|
||||||
class AuthServiceTest : BehaviorSpec({
|
|
||||||
val memberFinder: MemberFinder = mockk()
|
|
||||||
val jwtHandler: JwtHandler = JwtFixture.create()
|
|
||||||
|
|
||||||
val authService = AuthService(memberFinder, jwtHandler)
|
|
||||||
val user: MemberEntity = MemberFixture.user()
|
|
||||||
|
|
||||||
Given("로그인 요청을 받으면") {
|
|
||||||
When("이메일과 비밀번호로 회원을 찾고") {
|
|
||||||
val request = MemberFixture.userLoginRequest()
|
|
||||||
|
|
||||||
Then("회원이 있다면 JWT 토큰을 생성한 뒤 반환한다.") {
|
|
||||||
every {
|
|
||||||
memberFinder.findByEmailAndPassword(request.email, request.password)
|
|
||||||
} returns user
|
|
||||||
|
|
||||||
val accessToken: String = authService.login(request).accessToken
|
|
||||||
|
|
||||||
accessToken.isNotBlank() shouldBe true
|
|
||||||
jwtHandler.getMemberIdFromToken(accessToken) shouldBe user.id
|
|
||||||
}
|
|
||||||
|
|
||||||
Then("회원이 없다면 예외를 던진다.") {
|
|
||||||
every {
|
|
||||||
memberFinder.findByEmailAndPassword(request.email, request.password)
|
|
||||||
} throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
|
|
||||||
|
|
||||||
val exception = shouldThrow<AuthException> {
|
|
||||||
authService.login(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
exception.errorCode shouldBe AuthErrorCode.LOGIN_FAILED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Given("로그인 확인 요청을 받으면") {
|
|
||||||
When("회원 ID로 회원을 찾고") {
|
|
||||||
val userId: Long = user.id!!
|
|
||||||
|
|
||||||
Then("회원이 있다면 회원의 이름을 반환한다.") {
|
|
||||||
every { memberFinder.findById(userId) } returns user
|
|
||||||
|
|
||||||
val response = authService.checkLogin(userId)
|
|
||||||
|
|
||||||
assertSoftly(response) {
|
|
||||||
this.name shouldBe user.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Then("회원이 없다면 예외를 던진다.") {
|
|
||||||
every {
|
|
||||||
memberFinder.findById(userId)
|
|
||||||
} throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
|
|
||||||
|
|
||||||
val exception = shouldThrow<AuthException> {
|
|
||||||
authService.checkLogin(userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
exception.errorCode shouldBe AuthErrorCode.MEMBER_NOT_FOUND
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
package roomescape.auth.infrastructure.jwt
|
|
||||||
|
|
||||||
import io.jsonwebtoken.Jwts
|
|
||||||
import io.jsonwebtoken.security.Keys
|
|
||||||
import io.kotest.assertions.throwables.shouldThrow
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
|
||||||
import io.kotest.matchers.shouldBe
|
|
||||||
import roomescape.auth.exception.AuthErrorCode
|
|
||||||
import roomescape.auth.exception.AuthException
|
|
||||||
import roomescape.util.JwtFixture
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
class JwtHandlerTest : FunSpec({
|
|
||||||
|
|
||||||
context("JWT 토큰 조회") {
|
|
||||||
val memberId = Random.nextLong()
|
|
||||||
val jwtHandler: JwtHandler = JwtFixture.create()
|
|
||||||
|
|
||||||
test("토큰에서 멤버 ID를 올바르게 추출한다.") {
|
|
||||||
val token = jwtHandler.createToken(memberId)
|
|
||||||
val extractedMemberId = jwtHandler.getMemberIdFromToken(token)
|
|
||||||
|
|
||||||
extractedMemberId shouldBe memberId
|
|
||||||
}
|
|
||||||
|
|
||||||
test("만료된 토큰이면 예외를 던진다.") {
|
|
||||||
val expirationTime = 0L
|
|
||||||
val shortExpirationTimeJwtHandler: JwtHandler = JwtFixture.create(expirationTime = expirationTime)
|
|
||||||
val token = shortExpirationTimeJwtHandler.createToken(memberId)
|
|
||||||
|
|
||||||
Thread.sleep(expirationTime) // 만료 시간 이후로 대기
|
|
||||||
|
|
||||||
shouldThrow<AuthException> {
|
|
||||||
shortExpirationTimeJwtHandler.getMemberIdFromToken(token)
|
|
||||||
}.errorCode shouldBe AuthErrorCode.EXPIRED_TOKEN
|
|
||||||
}
|
|
||||||
|
|
||||||
test("토큰이 빈 값이면 예외를 던진다.") {
|
|
||||||
shouldThrow<AuthException> {
|
|
||||||
jwtHandler.getMemberIdFromToken("")
|
|
||||||
}.errorCode shouldBe AuthErrorCode.TOKEN_NOT_FOUND
|
|
||||||
}
|
|
||||||
|
|
||||||
test("시크릿 키가 잘못된 경우 예외를 던진다.") {
|
|
||||||
val now = Date()
|
|
||||||
val invalidSignatureToken: String = Jwts.builder()
|
|
||||||
.claim("memberId", memberId)
|
|
||||||
.issuedAt(now)
|
|
||||||
.expiration(Date(now.time + JwtFixture.EXPIRATION_TIME))
|
|
||||||
.signWith(Keys.hmacShaKeyFor(JwtFixture.SECRET_KEY_STRING.substring(1).toByteArray()))
|
|
||||||
.compact()
|
|
||||||
|
|
||||||
shouldThrow<AuthException> {
|
|
||||||
jwtHandler.getMemberIdFromToken(invalidSignatureToken)
|
|
||||||
}.errorCode shouldBe AuthErrorCode.INVALID_TOKEN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -1,151 +0,0 @@
|
|||||||
package roomescape.auth.web
|
|
||||||
|
|
||||||
import com.ninjasquad.springmockk.MockkBean
|
|
||||||
import io.mockk.Runs
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.just
|
|
||||||
import org.hamcrest.Matchers.equalTo
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import roomescape.auth.business.AuthService
|
|
||||||
import roomescape.auth.exception.AuthErrorCode
|
|
||||||
import roomescape.auth.exception.AuthException
|
|
||||||
import roomescape.common.exception.CommonErrorCode
|
|
||||||
import roomescape.common.exception.ErrorCode
|
|
||||||
import roomescape.util.MemberFixture
|
|
||||||
import roomescape.util.RoomescapeApiTest
|
|
||||||
|
|
||||||
@WebMvcTest(controllers = [AuthController::class])
|
|
||||||
class AuthControllerTest(val mockMvc: MockMvc) : RoomescapeApiTest() {
|
|
||||||
|
|
||||||
@MockkBean
|
|
||||||
private lateinit var authService: AuthService
|
|
||||||
|
|
||||||
val userRequest: LoginRequest = MemberFixture.userLoginRequest()
|
|
||||||
|
|
||||||
init {
|
|
||||||
Given("로그인 요청을 보낼 때") {
|
|
||||||
val endpoint = "/login"
|
|
||||||
|
|
||||||
When("로그인에 성공하면") {
|
|
||||||
val expectedToken = "expectedToken"
|
|
||||||
|
|
||||||
every {
|
|
||||||
authService.login(userRequest)
|
|
||||||
} returns LoginResponse(expectedToken)
|
|
||||||
|
|
||||||
Then("토큰을 반환한다.") {
|
|
||||||
runPostTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
body = userRequest,
|
|
||||||
) {
|
|
||||||
status { isOk() }
|
|
||||||
jsonPath("$.data.accessToken", equalTo(expectedToken))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
When("회원을 찾지 못하면") {
|
|
||||||
val expectedError = AuthErrorCode.LOGIN_FAILED
|
|
||||||
|
|
||||||
every {
|
|
||||||
authService.login(userRequest)
|
|
||||||
} throws AuthException(expectedError)
|
|
||||||
|
|
||||||
Then("에러 응답을 받는다.") {
|
|
||||||
runPostTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
body = userRequest,
|
|
||||||
) {
|
|
||||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
|
||||||
jsonPath("$.code", equalTo(expectedError.errorCode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
When("입력 값이 잘못되면") {
|
|
||||||
val expectedErrorCode: ErrorCode = CommonErrorCode.INVALID_INPUT_VALUE
|
|
||||||
|
|
||||||
Then("400 에러를 응답한다") {
|
|
||||||
listOf(
|
|
||||||
userRequest.copy(email = "invalid"),
|
|
||||||
userRequest.copy(password = " "),
|
|
||||||
"{\"email\": \"null\", \"password\": \"null\"}"
|
|
||||||
).forEach {
|
|
||||||
runPostTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
body = it,
|
|
||||||
) {
|
|
||||||
status { isEqualTo(expectedErrorCode.httpStatus.value()) }
|
|
||||||
jsonPath("$.code", equalTo(expectedErrorCode.errorCode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Given("로그인 상태를 확인할 때") {
|
|
||||||
val endpoint = "/login/check"
|
|
||||||
|
|
||||||
When("로그인된 회원의 ID로 요청하면") {
|
|
||||||
loginAsUser()
|
|
||||||
|
|
||||||
Then("회원의 이름과 권한을 응답한다") {
|
|
||||||
every {
|
|
||||||
authService.checkLogin(user.id!!)
|
|
||||||
} returns LoginCheckResponse(user.name, user.role.name)
|
|
||||||
|
|
||||||
runGetTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
) {
|
|
||||||
status { isOk() }
|
|
||||||
jsonPath("$.data.name", equalTo(user.name))
|
|
||||||
jsonPath("$.data.role", equalTo(user.role.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
When("토큰은 있지만 회원을 찾을 수 없으면") {
|
|
||||||
val invalidMemberId: Long = -1L
|
|
||||||
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
|
|
||||||
|
|
||||||
every { jwtHandler.getMemberIdFromToken(any()) } returns invalidMemberId
|
|
||||||
every { authService.checkLogin(invalidMemberId) } throws AuthException(expectedError)
|
|
||||||
|
|
||||||
Then("에러 응답을 받는다.") {
|
|
||||||
runGetTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
) {
|
|
||||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
|
||||||
jsonPath("$.code", equalTo(expectedError.errorCode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Given("로그아웃 요청을 보낼 때") {
|
|
||||||
val endpoint = "/logout"
|
|
||||||
|
|
||||||
When("토큰으로 memberId 조회가 가능하면") {
|
|
||||||
loginAsUser()
|
|
||||||
|
|
||||||
every {
|
|
||||||
authService.logout(user.id!!)
|
|
||||||
} just Runs
|
|
||||||
|
|
||||||
Then("정상 응답한다.") {
|
|
||||||
runPostTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
) {
|
|
||||||
status { isNoContent() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
package roomescape.auth.web.support
|
|
||||||
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
|
||||||
import io.kotest.matchers.shouldBe
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
|
||||||
|
|
||||||
class CookieUtilsTest : FunSpec({
|
|
||||||
context("accessToken 쿠키를 가져온다.") {
|
|
||||||
val httpServletRequest: HttpServletRequest = mockk()
|
|
||||||
|
|
||||||
test("accessToken이 있으면 해당 쿠키를 반환한다.") {
|
|
||||||
val token = "test-token"
|
|
||||||
every { httpServletRequest.getHeader("Authorization") } returns "Bearer $token"
|
|
||||||
|
|
||||||
httpServletRequest.accessToken() shouldBe token
|
|
||||||
}
|
|
||||||
|
|
||||||
test("accessToken이 없으면 null을 반환한다.") {
|
|
||||||
every { httpServletRequest.getHeader("Authorization") } returns null
|
|
||||||
|
|
||||||
httpServletRequest.accessToken() shouldBe null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
package roomescape.member.business
|
|
||||||
|
|
||||||
import io.kotest.assertions.assertSoftly
|
|
||||||
import io.kotest.assertions.throwables.shouldThrow
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
|
||||||
import io.kotest.matchers.collections.shouldContainExactly
|
|
||||||
import io.kotest.matchers.collections.shouldHaveSize
|
|
||||||
import io.kotest.matchers.shouldBe
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import roomescape.member.exception.MemberErrorCode
|
|
||||||
import roomescape.member.exception.MemberException
|
|
||||||
import roomescape.member.implement.MemberFinder
|
|
||||||
import roomescape.member.implement.MemberWriter
|
|
||||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
|
||||||
import roomescape.member.infrastructure.persistence.Role
|
|
||||||
import roomescape.member.web.SignupRequest
|
|
||||||
import roomescape.util.MemberFixture
|
|
||||||
|
|
||||||
class MemberServiceTest : FunSpec({
|
|
||||||
val memberWriter: MemberWriter = mockk()
|
|
||||||
val memberFinder: MemberFinder = mockk()
|
|
||||||
|
|
||||||
val memberService = MemberService(memberWriter, memberFinder)
|
|
||||||
|
|
||||||
context("findMembers") {
|
|
||||||
test("정상 응답") {
|
|
||||||
val members: List<MemberEntity> = listOf(
|
|
||||||
MemberFixture.create(name = "user1"),
|
|
||||||
MemberFixture.create(name = "user2"),
|
|
||||||
)
|
|
||||||
|
|
||||||
every { memberFinder.findAll() } returns members
|
|
||||||
|
|
||||||
val response = memberService.findMembers()
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertSoftly(response.members) {
|
|
||||||
it shouldHaveSize 2
|
|
||||||
it.map { member -> member.name } shouldContainExactly listOf("user1", "user2")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context("findById") {
|
|
||||||
val id = 1L
|
|
||||||
|
|
||||||
test("정상 응답") {
|
|
||||||
every {
|
|
||||||
memberFinder.findById(id)
|
|
||||||
} returns MemberFixture.create(id = id)
|
|
||||||
|
|
||||||
memberService.findById(id).id shouldBe id
|
|
||||||
}
|
|
||||||
|
|
||||||
test("회원을 찾을 수 없으면 예외 응답") {
|
|
||||||
every {
|
|
||||||
memberFinder.findById(id)
|
|
||||||
} throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
|
|
||||||
|
|
||||||
shouldThrow<MemberException> {
|
|
||||||
memberService.findById(id)
|
|
||||||
}.also {
|
|
||||||
it.errorCode shouldBe MemberErrorCode.MEMBER_NOT_FOUND
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context("createMember") {
|
|
||||||
val request = SignupRequest(name = "new-user", email = "new@test.com", password = "password")
|
|
||||||
|
|
||||||
test("정상 저장") {
|
|
||||||
val member = MemberFixture.create(
|
|
||||||
name = request.name,
|
|
||||||
account = request.email,
|
|
||||||
password = request.password
|
|
||||||
)
|
|
||||||
|
|
||||||
every {
|
|
||||||
memberWriter.create(request.name, request.email, request.password, Role.MEMBER)
|
|
||||||
} returns member
|
|
||||||
|
|
||||||
val response = memberService.createMember(request)
|
|
||||||
|
|
||||||
response.id shouldBe member.id
|
|
||||||
}
|
|
||||||
|
|
||||||
test("중복된 이메일이 있으면 예외 응답") {
|
|
||||||
every {
|
|
||||||
memberWriter.create(request.name, request.email, request.password, Role.MEMBER)
|
|
||||||
} throws MemberException(MemberErrorCode.DUPLICATE_EMAIL)
|
|
||||||
|
|
||||||
shouldThrow<MemberException> {
|
|
||||||
memberService.createMember(request)
|
|
||||||
}.also {
|
|
||||||
it.errorCode shouldBe MemberErrorCode.DUPLICATE_EMAIL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -1,136 +0,0 @@
|
|||||||
package roomescape.member.controller
|
|
||||||
|
|
||||||
import com.ninjasquad.springmockk.MockkBean
|
|
||||||
import io.kotest.assertions.assertSoftly
|
|
||||||
import io.kotest.matchers.collections.shouldContainAll
|
|
||||||
import io.kotest.matchers.shouldBe
|
|
||||||
import io.mockk.every
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
|
||||||
import org.springframework.test.web.servlet.MockMvc
|
|
||||||
import roomescape.auth.exception.AuthErrorCode
|
|
||||||
import roomescape.member.business.MemberService
|
|
||||||
import roomescape.member.exception.MemberErrorCode
|
|
||||||
import roomescape.member.exception.MemberException
|
|
||||||
import roomescape.member.infrastructure.persistence.Role
|
|
||||||
import roomescape.member.web.*
|
|
||||||
import roomescape.util.MemberFixture
|
|
||||||
import roomescape.util.RoomescapeApiTest
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
@WebMvcTest(controllers = [MemberController::class])
|
|
||||||
class MemberControllerTest(val mockMvc: MockMvc) : RoomescapeApiTest() {
|
|
||||||
@MockkBean
|
|
||||||
private lateinit var memberService: MemberService
|
|
||||||
|
|
||||||
init {
|
|
||||||
given("GET /members 요청을") {
|
|
||||||
val endpoint = "/members"
|
|
||||||
val response = listOf(
|
|
||||||
MemberFixture.create(id = Random.nextLong(), name = "name1"),
|
|
||||||
MemberFixture.create(id = Random.nextLong(), name = "name2"),
|
|
||||||
MemberFixture.create(id = Random.nextLong(), name = "name3"),
|
|
||||||
).toRetrieveListResponse()
|
|
||||||
|
|
||||||
every { memberService.findMembers() } returns response
|
|
||||||
|
|
||||||
`when`("관리자가 보내면") {
|
|
||||||
loginAsAdmin()
|
|
||||||
|
|
||||||
then("성공한다.") {
|
|
||||||
val result: MemberRetrieveListResponse = runGetTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
) {
|
|
||||||
status { isOk() }
|
|
||||||
}.andReturn().readValue(MemberRetrieveListResponse::class.java)
|
|
||||||
|
|
||||||
|
|
||||||
assertSoftly(result.members) {
|
|
||||||
it.size shouldBe response.members.size
|
|
||||||
it.map { m -> m.name } shouldContainAll response.members.map { m -> m.name }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
`when`("관리자가 아니면 에러 응답을 받는다.") {
|
|
||||||
then("비회원") {
|
|
||||||
doNotLogin()
|
|
||||||
val expectedError = AuthErrorCode.MEMBER_NOT_FOUND
|
|
||||||
|
|
||||||
runGetTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
) {
|
|
||||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
|
||||||
}.andExpect {
|
|
||||||
jsonPath("$.code") { value(expectedError.errorCode) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
then("일반 회원") {
|
|
||||||
loginAsUser()
|
|
||||||
val expectedError = AuthErrorCode.ACCESS_DENIED
|
|
||||||
|
|
||||||
runGetTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
) {
|
|
||||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
|
||||||
}.andExpect {
|
|
||||||
jsonPath("$.code") { value(expectedError.errorCode) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
given("POST /members") {
|
|
||||||
val endpoint = "/members"
|
|
||||||
val request = SignupRequest(
|
|
||||||
name = "name",
|
|
||||||
email = "email@email.com",
|
|
||||||
password = "password"
|
|
||||||
)
|
|
||||||
`when`("같은 이메일이 없으면") {
|
|
||||||
every {
|
|
||||||
memberService.createMember(request)
|
|
||||||
} returns MemberFixture.create(
|
|
||||||
id = 1,
|
|
||||||
name = request.name,
|
|
||||||
account = request.email,
|
|
||||||
password = request.password,
|
|
||||||
role = Role.MEMBER
|
|
||||||
).toSignupResponse()
|
|
||||||
|
|
||||||
then("id과 이름을 담아 성공 응답") {
|
|
||||||
runPostTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
body = request
|
|
||||||
) {
|
|
||||||
status { isCreated() }
|
|
||||||
jsonPath("$.data.name") { value(request.name) }
|
|
||||||
jsonPath("$.data.id") { value(1) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
`when`("같은 이메일이 있으면") {
|
|
||||||
val expectedError = MemberErrorCode.DUPLICATE_EMAIL
|
|
||||||
every {
|
|
||||||
memberService.createMember(request)
|
|
||||||
} throws MemberException(expectedError)
|
|
||||||
|
|
||||||
then("에러 응답") {
|
|
||||||
runPostTest(
|
|
||||||
mockMvc = mockMvc,
|
|
||||||
endpoint = endpoint,
|
|
||||||
body = request
|
|
||||||
) {
|
|
||||||
status { isEqualTo(expectedError.httpStatus.value()) }
|
|
||||||
jsonPath("$.code") { value(expectedError.errorCode) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
package roomescape.member.implement
|
|
||||||
|
|
||||||
import io.kotest.assertions.throwables.shouldThrow
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
|
||||||
import io.kotest.matchers.collections.shouldHaveSize
|
|
||||||
import io.kotest.matchers.shouldBe
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import io.mockk.verify
|
|
||||||
import org.springframework.data.repository.findByIdOrNull
|
|
||||||
import roomescape.member.exception.MemberErrorCode
|
|
||||||
import roomescape.member.exception.MemberException
|
|
||||||
import roomescape.member.infrastructure.persistence.MemberRepository
|
|
||||||
|
|
||||||
class MemberFinderTest : FunSpec({
|
|
||||||
|
|
||||||
val memberRepository: MemberRepository = mockk()
|
|
||||||
val memberFinder = MemberFinder(memberRepository)
|
|
||||||
|
|
||||||
context("findAll") {
|
|
||||||
test("모든 회원을 조회한다.") {
|
|
||||||
every {
|
|
||||||
memberRepository.findAll()
|
|
||||||
} returns listOf(mockk(), mockk(), mockk())
|
|
||||||
|
|
||||||
memberFinder.findAll() shouldHaveSize 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context("findById") {
|
|
||||||
val memberId = 1L
|
|
||||||
test("동일한 ID인 회원을 찾아 응답한다.") {
|
|
||||||
every {
|
|
||||||
memberRepository.findByIdOrNull(memberId)
|
|
||||||
} returns mockk()
|
|
||||||
|
|
||||||
memberFinder.findById(memberId)
|
|
||||||
|
|
||||||
verify(exactly = 1) {
|
|
||||||
memberRepository.findByIdOrNull(memberId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test("동일한 ID인 회원이 없으면 실패한다.") {
|
|
||||||
every {
|
|
||||||
memberRepository.findByIdOrNull(memberId)
|
|
||||||
} returns null
|
|
||||||
|
|
||||||
shouldThrow<MemberException> {
|
|
||||||
memberFinder.findById(memberId)
|
|
||||||
}.also {
|
|
||||||
it.errorCode shouldBe MemberErrorCode.MEMBER_NOT_FOUND
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context("findByEmailAndPassword") {
|
|
||||||
val email = "email"
|
|
||||||
val password = "password"
|
|
||||||
|
|
||||||
test("동일한 이메일과 비밀번호를 가진 회원을 찾아 응답한다.") {
|
|
||||||
every {
|
|
||||||
memberRepository.findByEmailAndPassword(email, password)
|
|
||||||
} returns mockk()
|
|
||||||
|
|
||||||
memberFinder.findByEmailAndPassword(email, password)
|
|
||||||
|
|
||||||
verify(exactly = 1) {
|
|
||||||
memberRepository.findByEmailAndPassword(email, password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test("동일한 이메일과 비밀번호를 가진 회원이 없으면 실패한다.") {
|
|
||||||
every {
|
|
||||||
memberRepository.findByEmailAndPassword(email, password)
|
|
||||||
} returns null
|
|
||||||
|
|
||||||
shouldThrow<MemberException> {
|
|
||||||
memberFinder.findByEmailAndPassword(email, password)
|
|
||||||
}.also {
|
|
||||||
it.errorCode shouldBe MemberErrorCode.MEMBER_NOT_FOUND
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
package roomescape.member.implement
|
|
||||||
|
|
||||||
import io.kotest.assertions.throwables.shouldNotThrow
|
|
||||||
import io.kotest.assertions.throwables.shouldThrow
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
|
||||||
import io.kotest.core.spec.style.StringSpec
|
|
||||||
import io.kotest.matchers.shouldBe
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import roomescape.member.exception.MemberErrorCode
|
|
||||||
import roomescape.member.exception.MemberException
|
|
||||||
import roomescape.member.infrastructure.persistence.MemberRepository
|
|
||||||
|
|
||||||
class MemberValidatorTest : FunSpec({
|
|
||||||
|
|
||||||
val memberRepository: MemberRepository = mockk()
|
|
||||||
val memberValidator = MemberValidator(memberRepository)
|
|
||||||
|
|
||||||
context("validateCanSignup") {
|
|
||||||
val email = "email@email.com"
|
|
||||||
|
|
||||||
test("같은 이메일을 가진 회원이 있으면 예외를 던진다.") {
|
|
||||||
every {
|
|
||||||
memberRepository.existsByEmail(email)
|
|
||||||
} returns true
|
|
||||||
|
|
||||||
shouldThrow<MemberException> {
|
|
||||||
memberValidator.validateCanSignup(email)
|
|
||||||
}.also {
|
|
||||||
it.errorCode shouldBe MemberErrorCode.DUPLICATE_EMAIL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test("같은 이메일을 가진 회원이 없으면 종료한다.") {
|
|
||||||
every {
|
|
||||||
memberRepository.existsByEmail(email)
|
|
||||||
} returns false
|
|
||||||
|
|
||||||
shouldNotThrow<MemberException> {
|
|
||||||
memberValidator.validateCanSignup(email)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
package roomescape.member.implement
|
|
||||||
|
|
||||||
import io.kotest.assertions.throwables.shouldThrow
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
|
||||||
import io.kotest.core.spec.style.StringSpec
|
|
||||||
import io.kotest.matchers.nulls.shouldNotBeNull
|
|
||||||
import io.kotest.matchers.shouldBe
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import io.mockk.slot
|
|
||||||
import io.mockk.spyk
|
|
||||||
import io.mockk.verify
|
|
||||||
import roomescape.member.exception.MemberErrorCode
|
|
||||||
import roomescape.member.exception.MemberException
|
|
||||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
|
||||||
import roomescape.member.infrastructure.persistence.MemberRepository
|
|
||||||
import roomescape.member.infrastructure.persistence.Role
|
|
||||||
import roomescape.util.MemberFixture
|
|
||||||
import roomescape.util.TsidFactory
|
|
||||||
|
|
||||||
class MemberWriterTest : FunSpec({
|
|
||||||
val memberRepository: MemberRepository = mockk()
|
|
||||||
val memberValidator = MemberValidator(memberRepository)
|
|
||||||
val memberWriter = MemberWriter(TsidFactory, memberValidator, memberRepository)
|
|
||||||
|
|
||||||
context("create") {
|
|
||||||
val name = "name"
|
|
||||||
val email = "email"
|
|
||||||
val password = "password"
|
|
||||||
val role = Role.MEMBER
|
|
||||||
|
|
||||||
test("중복된 이메일이 있으면 실패한다.") {
|
|
||||||
every {
|
|
||||||
memberRepository.existsByEmail(any())
|
|
||||||
} returns true
|
|
||||||
|
|
||||||
shouldThrow<MemberException> {
|
|
||||||
memberWriter.create(name, email, password, role)
|
|
||||||
}.also {
|
|
||||||
it.errorCode shouldBe MemberErrorCode.DUPLICATE_EMAIL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test("중복된 이메일이 없으면 저장한다.") {
|
|
||||||
every {
|
|
||||||
memberRepository.existsByEmail(any())
|
|
||||||
} returns false
|
|
||||||
|
|
||||||
every {
|
|
||||||
memberRepository.save(any())
|
|
||||||
} returns MemberFixture.create(
|
|
||||||
name = name,
|
|
||||||
account = email,
|
|
||||||
password = password,
|
|
||||||
role = role
|
|
||||||
)
|
|
||||||
|
|
||||||
memberWriter.create(name, email, password, role)
|
|
||||||
|
|
||||||
verify(exactly = 1) {
|
|
||||||
memberRepository.save(any())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
package roomescape.member.infrastructure.persistence
|
|
||||||
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
|
||||||
import io.kotest.matchers.nulls.shouldNotBeNull
|
|
||||||
import io.kotest.matchers.shouldBe
|
|
||||||
import jakarta.persistence.EntityManager
|
|
||||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
|
||||||
import roomescape.util.MemberFixture
|
|
||||||
|
|
||||||
@DataJpaTest(showSql = true)
|
|
||||||
class MemberRepositoryTest(
|
|
||||||
val entityManager: EntityManager,
|
|
||||||
val memberRepository: MemberRepository
|
|
||||||
) : FunSpec({
|
|
||||||
context("existsByEmail") {
|
|
||||||
val account = "email"
|
|
||||||
val email = "$account@email.com"
|
|
||||||
|
|
||||||
beforeTest {
|
|
||||||
entityManager.persist(MemberFixture.create(account = account))
|
|
||||||
entityManager.flush()
|
|
||||||
entityManager.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
test("동일한 이메일이 있으면 true 반환") {
|
|
||||||
val result = memberRepository.existsByEmail(email)
|
|
||||||
result shouldBe true
|
|
||||||
}
|
|
||||||
|
|
||||||
test("동일한 이메일이 없으면 false 반환") {
|
|
||||||
memberRepository.existsByEmail(email.substring(email.length - 1)) shouldBe false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context("findByEmailAndPassword") {
|
|
||||||
val account = "email"
|
|
||||||
val email = "$account@email.com"
|
|
||||||
val password = "password123"
|
|
||||||
|
|
||||||
beforeTest {
|
|
||||||
entityManager.persist(MemberFixture.create(account = account, password = password))
|
|
||||||
entityManager.flush()
|
|
||||||
entityManager.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
test("둘다 일치하면 정상 반환") {
|
|
||||||
memberRepository.findByEmailAndPassword(email, password) shouldNotBeNull {
|
|
||||||
this.email shouldBe email
|
|
||||||
this.password shouldBe password
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test("이메일이 틀리면 null 반환") {
|
|
||||||
val invalidMail = email.substring(email.length - 1)
|
|
||||||
memberRepository.findByEmailAndPassword(invalidMail, password) shouldBe null
|
|
||||||
}
|
|
||||||
|
|
||||||
test("비밀번호가 틀리면 null 반환") {
|
|
||||||
val invalidPassword = password.substring(password.length - 1)
|
|
||||||
memberRepository.findByEmailAndPassword(email, invalidPassword) shouldBe null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -290,7 +290,7 @@ class ScheduleApiTest(
|
|||||||
val themeId: Long = Given {
|
val themeId: Long = Given {
|
||||||
contentType(MediaType.APPLICATION_JSON_VALUE)
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
header("Authorization", "Bearer $token")
|
header("Authorization", "Bearer $token")
|
||||||
body(ThemeFixtureV2.createRequest.copy(name = "theme-${System.currentTimeMillis()}"))
|
body(ThemeFixture.createRequest.copy(name = "theme-${System.currentTimeMillis()}"))
|
||||||
} When {
|
} When {
|
||||||
post("/admin/themes")
|
post("/admin/themes")
|
||||||
} Extract {
|
} Extract {
|
||||||
@ -616,7 +616,7 @@ class ScheduleApiTest(
|
|||||||
Given {
|
Given {
|
||||||
contentType(MediaType.APPLICATION_JSON_VALUE)
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
header("Authorization", "Bearer $token")
|
header("Authorization", "Bearer $token")
|
||||||
body(ThemeFixtureV2.createRequest.copy(name = "theme-${System.currentTimeMillis()}"))
|
body(ThemeFixture.createRequest.copy(name = "theme-${System.currentTimeMillis()}"))
|
||||||
} When {
|
} When {
|
||||||
post("/admin/themes")
|
post("/admin/themes")
|
||||||
} Extract {
|
} Extract {
|
||||||
|
|||||||
@ -19,18 +19,18 @@ import roomescape.theme.business.MIN_DURATION
|
|||||||
import roomescape.theme.business.MIN_PARTICIPANTS
|
import roomescape.theme.business.MIN_PARTICIPANTS
|
||||||
import roomescape.theme.business.MIN_PRICE
|
import roomescape.theme.business.MIN_PRICE
|
||||||
import roomescape.theme.exception.ThemeErrorCode
|
import roomescape.theme.exception.ThemeErrorCode
|
||||||
import roomescape.theme.infrastructure.persistence.v2.ThemeEntityV2
|
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||||
import roomescape.theme.infrastructure.persistence.v2.ThemeRepositoryV2
|
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
||||||
import roomescape.theme.web.ThemeCreateRequestV2
|
import roomescape.theme.web.ThemeCreateRequest
|
||||||
import roomescape.theme.web.ThemeUpdateRequest
|
import roomescape.theme.web.ThemeUpdateRequest
|
||||||
import roomescape.util.FunSpecSpringbootTest
|
import roomescape.util.FunSpecSpringbootTest
|
||||||
import roomescape.util.ThemeFixtureV2.createRequest
|
import roomescape.util.ThemeFixture.createRequest
|
||||||
import roomescape.util.assertProperties
|
import roomescape.util.assertProperties
|
||||||
import roomescape.util.runTest
|
import roomescape.util.runTest
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
class ThemeApiTest(
|
class ThemeApiTest(
|
||||||
private val themeRepository: ThemeRepositoryV2
|
private val themeRepository: ThemeRepository
|
||||||
) : FunSpecSpringbootTest() {
|
) : FunSpecSpringbootTest() {
|
||||||
|
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ class ThemeApiTest(
|
|||||||
}
|
}
|
||||||
).also {
|
).also {
|
||||||
val createdThemeId: Long = it.extract().path("data.id")
|
val createdThemeId: Long = it.extract().path("data.id")
|
||||||
val createdTheme: ThemeEntityV2 = themeRepository.findByIdOrNull(createdThemeId)
|
val createdTheme: ThemeEntity = themeRepository.findByIdOrNull(createdThemeId)
|
||||||
?: throw AssertionError("Unexpected Exception Occurred.")
|
?: throw AssertionError("Unexpected Exception Occurred.")
|
||||||
|
|
||||||
createdTheme.name shouldBe createRequest.name
|
createdTheme.name shouldBe createRequest.name
|
||||||
@ -376,7 +376,7 @@ class ThemeApiTest(
|
|||||||
|
|
||||||
context("관리자 페이지에서 특정 테마의 상세 정보를 조회한다.") {
|
context("관리자 페이지에서 특정 테마의 상세 정보를 조회한다.") {
|
||||||
test("정상 응답") {
|
test("정상 응답") {
|
||||||
val createdTheme: ThemeEntityV2 = createDummyTheme(createRequest)
|
val createdTheme: ThemeEntity = createDummyTheme(createRequest)
|
||||||
|
|
||||||
runTest(
|
runTest(
|
||||||
token = loginUtil.loginAsAdmin(),
|
token = loginUtil.loginAsAdmin(),
|
||||||
@ -445,7 +445,7 @@ class ThemeApiTest(
|
|||||||
|
|
||||||
context("테마를 수정한다.") {
|
context("테마를 수정한다.") {
|
||||||
lateinit var token: String
|
lateinit var token: String
|
||||||
lateinit var createdTheme: ThemeEntityV2
|
lateinit var createdTheme: ThemeEntity
|
||||||
lateinit var apiPath: String
|
lateinit var apiPath: String
|
||||||
|
|
||||||
val updateRequest = ThemeUpdateRequest(name = "modified")
|
val updateRequest = ThemeUpdateRequest(name = "modified")
|
||||||
@ -671,7 +671,7 @@ class ThemeApiTest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createDummyTheme(request: ThemeCreateRequestV2): ThemeEntityV2 {
|
fun createDummyTheme(request: ThemeCreateRequest): ThemeEntity {
|
||||||
val createdThemeId: Long = Given {
|
val createdThemeId: Long = Given {
|
||||||
contentType(MediaType.APPLICATION_JSON_VALUE)
|
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
header("Authorization", "Bearer ${loginUtil.loginAsAdmin()}")
|
header("Authorization", "Bearer ${loginUtil.loginAsAdmin()}")
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
package roomescape.util
|
package roomescape.util
|
||||||
|
|
||||||
|
import com.github.f4b6a3.tsid.TsidFactory
|
||||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||||
import roomescape.member.infrastructure.persistence.Role
|
import roomescape.member.infrastructure.persistence.Role
|
||||||
import roomescape.schedule.web.ScheduleCreateRequest
|
import roomescape.schedule.web.ScheduleCreateRequest
|
||||||
import roomescape.theme.infrastructure.persistence.v2.Difficulty
|
import roomescape.theme.infrastructure.persistence.Difficulty
|
||||||
import roomescape.theme.web.ThemeCreateRequestV2
|
import roomescape.theme.web.ThemeCreateRequest
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
|
|
||||||
object MemberFixtureV2 {
|
val tsidFactory = TsidFactory(0)
|
||||||
|
|
||||||
|
object MemberFixture {
|
||||||
val admin: MemberEntity = MemberEntity(
|
val admin: MemberEntity = MemberEntity(
|
||||||
_id = 9304,
|
_id = 9304,
|
||||||
name = "ADMIN",
|
name = "ADMIN",
|
||||||
@ -26,8 +29,8 @@ object MemberFixtureV2 {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
object ThemeFixtureV2 {
|
object ThemeFixture {
|
||||||
val createRequest: ThemeCreateRequestV2 = ThemeCreateRequestV2(
|
val createRequest: ThemeCreateRequest = ThemeCreateRequest(
|
||||||
name = "Matilda Green",
|
name = "Matilda Green",
|
||||||
description = "constituto",
|
description = "constituto",
|
||||||
thumbnailUrl = "https://duckduckgo.com/?q=mediocrem",
|
thumbnailUrl = "https://duckduckgo.com/?q=mediocrem",
|
||||||
|
|||||||
@ -21,7 +21,7 @@ class LoginUtil(
|
|||||||
if (!memberRepository.existsByEmail(email)) {
|
if (!memberRepository.existsByEmail(email)) {
|
||||||
memberRepository.save(
|
memberRepository.save(
|
||||||
MemberEntity(
|
MemberEntity(
|
||||||
_id = TsidFactory.next(),
|
_id = tsidFactory.next(),
|
||||||
email = email,
|
email = email,
|
||||||
password = password,
|
password = password,
|
||||||
name = email.split("@").first(),
|
name = email.split("@").first(),
|
||||||
@ -43,11 +43,11 @@ class LoginUtil(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun loginAsAdmin(): String {
|
fun loginAsAdmin(): String {
|
||||||
return login(MemberFixtureV2.admin.email, MemberFixtureV2.admin.password, Role.ADMIN)
|
return login(MemberFixture.admin.email, MemberFixture.admin.password, Role.ADMIN)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loginAsUser(): String {
|
fun loginAsUser(): String {
|
||||||
return login(MemberFixtureV2.user.email, MemberFixtureV2.user.password)
|
return login(MemberFixture.user.email, MemberFixture.user.password)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,145 +0,0 @@
|
|||||||
package roomescape.util
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.github.f4b6a3.tsid.TsidFactory
|
|
||||||
import com.ninjasquad.springmockk.MockkBean
|
|
||||||
import com.ninjasquad.springmockk.SpykBean
|
|
||||||
import io.kotest.core.spec.style.BehaviorSpec
|
|
||||||
import io.mockk.every
|
|
||||||
import org.springframework.boot.test.context.TestConfiguration
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.annotation.Import
|
|
||||||
import org.springframework.context.annotation.Primary
|
|
||||||
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext
|
|
||||||
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.common.log.ApiLogMessageConverter
|
|
||||||
import roomescape.member.exception.MemberErrorCode
|
|
||||||
import roomescape.member.exception.MemberException
|
|
||||||
import roomescape.member.implement.MemberFinder
|
|
||||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
|
||||||
import roomescape.member.infrastructure.persistence.MemberRepository
|
|
||||||
import roomescape.util.MemberFixture.NOT_LOGGED_IN_USERID
|
|
||||||
|
|
||||||
@Import(TestConfig::class, JacksonConfig::class)
|
|
||||||
@MockkBean(JpaMetamodelMappingContext::class)
|
|
||||||
abstract class RoomescapeApiTest : BehaviorSpec() {
|
|
||||||
|
|
||||||
@SpykBean
|
|
||||||
private lateinit var authInterceptor: AuthInterceptor
|
|
||||||
|
|
||||||
@SpykBean
|
|
||||||
private lateinit var memberIdResolver: MemberIdResolver
|
|
||||||
|
|
||||||
@MockkBean
|
|
||||||
private lateinit var memberRepository: MemberRepository
|
|
||||||
|
|
||||||
@SpykBean
|
|
||||||
lateinit var apiLogMessageConverter: ApiLogMessageConverter
|
|
||||||
|
|
||||||
@SpykBean
|
|
||||||
lateinit var memberFinder: MemberFinder
|
|
||||||
|
|
||||||
@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.AUTHORIZATION, "Bearer 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.AUTHORIZATION, "Bearer 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.AUTHORIZATION, "Bearer token")
|
|
||||||
}.apply {
|
|
||||||
log.takeIf { it }?.let { this.andDo { print() } }
|
|
||||||
}.andExpect(assert)
|
|
||||||
|
|
||||||
fun loginAsAdmin() {
|
|
||||||
every {
|
|
||||||
jwtHandler.getMemberIdFromToken(any())
|
|
||||||
} returns admin.id!!
|
|
||||||
|
|
||||||
every { memberFinder.findById(admin.id!!) } returns admin
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loginAsUser() {
|
|
||||||
every {
|
|
||||||
jwtHandler.getMemberIdFromToken(any())
|
|
||||||
} returns user.id!!
|
|
||||||
|
|
||||||
every { memberFinder.findById(user.id!!) } returns user
|
|
||||||
}
|
|
||||||
|
|
||||||
fun doNotLogin() {
|
|
||||||
every {
|
|
||||||
jwtHandler.getMemberIdFromToken(any())
|
|
||||||
} throws AuthException(AuthErrorCode.INVALID_TOKEN)
|
|
||||||
|
|
||||||
every {
|
|
||||||
memberFinder.findById(NOT_LOGGED_IN_USERID)
|
|
||||||
} throws MemberException(MemberErrorCode.MEMBER_NOT_FOUND)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> MvcResult.readValue(valueType: Class<T>): 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 <T> readValue(responseJson: String, valueType: Class<T>): 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()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestConfiguration
|
|
||||||
class TestConfig {
|
|
||||||
@Bean
|
|
||||||
@Primary
|
|
||||||
fun tsidFactory(): TsidFactory = TsidFactory
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user