diff --git a/src/test/java/roomescape/system/auth/web/AuthControllerTest.kt b/src/test/java/roomescape/system/auth/web/AuthControllerTest.kt index a3c2a274..7e6303c9 100644 --- a/src/test/java/roomescape/system/auth/web/AuthControllerTest.kt +++ b/src/test/java/roomescape/system/auth/web/AuthControllerTest.kt @@ -1,118 +1,139 @@ -package roomescape.system.auth.web; +package roomescape.system.auth.web -import static org.assertj.core.api.Assertions.*; -import static org.hamcrest.Matchers.*; +import io.mockk.every +import org.hamcrest.Matchers.containsString +import org.hamcrest.Matchers.`is` +import org.springframework.data.repository.findByIdOrNull +import roomescape.common.MemberFixture +import roomescape.common.RoomescapeApiTest +import roomescape.system.exception.ErrorType -import java.util.Map; +class AuthControllerTest : RoomescapeApiTest() { -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.test.context.jdbc.Sql; + val userRequest: LoginRequest = MemberFixture.userLoginRequest() -import io.restassured.RestAssured; -import io.restassured.http.ContentType; -import roomescape.member.infrastructure.persistence.Member; -import roomescape.member.infrastructure.persistence.MemberRepository; -import roomescape.member.infrastructure.persistence.Role; + init { + Given("로그인 요청을 보낼 때") { + val endpoint: String = "/login" -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@Sql(scripts = "/truncate.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) -class AuthControllerTest { + When("로그인에 성공하면") { + val expectedToken: String = "expectedToken" - @Autowired - private MemberRepository memberRepository; + every { + memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password) + } returns user - @LocalServerPort - private int port; + every { + jwtHandler.createToken(user.id!!) + } returns expectedToken - @Test - @DisplayName("로그인에 성공하면 JWT accessToken을 응답 받는다.") - void getJwtAccessTokenWhenlogin() { - // given - String email = "test@email.com"; - String password = "12341234"; - memberRepository.save(new Member(null, "이름", email, password, Role.MEMBER)); + Then("토큰을 쿠키에 담아 응답한다") { + runPostTest(endpoint, body = MemberFixture.userLoginRequest()) { + statusCode(200) + cookie("accessToken", expectedToken) + header("Set-Cookie", containsString("Max-Age=1800000")) + header("Set-Cookie", containsString("HttpOnly")) + header("Set-Cookie", containsString("Secure")) + } + } + } - Map loginParams = Map.of( - "email", email, - "password", password - ); + When("회원을 찾지 못하면") { + every { + memberRepository.findByEmailAndPassword(userRequest.email, userRequest.password) + } returns null - // when - Map cookies = RestAssured.given().log().all() - .contentType(ContentType.JSON) - .port(port) - .body(loginParams) - .when().post("/login") - .then().log().all().extract().cookies(); + Then("400 에러를 응답한다") { + runPostTest(endpoint, body = userRequest) { + log().all() + statusCode(400) + body("errorType", `is`(ErrorType.MEMBER_NOT_FOUND.name)) + } + } + } - // then - assertThat(cookies.get("accessToken")).isNotNull(); - } + When("잘못된 요청을 보내면 400 에러를 응답한다.") { - @Test - @DisplayName("로그인 검증 시, 회원의 name을 응답 받는다.") - void checkLogin() { - // given - String email = "test@test.com"; - String password = "12341234"; - String accessTokenCookie = getAccessTokenCookieByLogin(email, password); + Then("이메일 형식이 잘못된 경우") { + val invalidRequest: LoginRequest = userRequest.copy(email = "invalid") - // when & then - RestAssured.given().log().all() - .contentType(ContentType.JSON) - .port(port) - .header("cookie", accessTokenCookie) - .when().get("/login/check") - .then() - .body("data.name", is("이름")); - } + runPostTest(endpoint, body = invalidRequest) { + log().all() + statusCode(400) + body("message", `is`("이메일 형식이 일치하지 않습니다. 예시: abc123@gmail.com")) + } + } - @Test - @DisplayName("로그인 없이 검증요청을 보내면 401 Unauthorized 를 응답한다.") - void checkLoginFailByNotAuthorized() { - RestAssured.given().log().all() - .contentType(ContentType.JSON) - .port(port) - .when().get("/login/check") - .then() - .statusCode(401); - } + Then("비밀번호가 공백인 경우") { + val invalidRequest = userRequest.copy(password = " ") - @Test - @DisplayName("로그아웃 요청 시, accessToken 쿠키가 삭제된다.") - void checkLogout() { - // given - String accessToken = getAccessTokenCookieByLogin("email@email.com", "password"); + runPostTest(endpoint, body = invalidRequest) { + log().all() + statusCode(400) + body("message", `is`("비밀번호는 공백일 수 없습니다.")) + } + } + } + } - // when & then - RestAssured.given().log().all() - .port(port) - .header("cookie", accessToken) - .when().post("/logout") - .then() - .statusCode(200) - .cookie("accessToken", ""); - } + Given("로그인 상태를 확인할 때") { + val endpoint: String = "/login/check" - private String getAccessTokenCookieByLogin(final String email, final String password) { - memberRepository.save(new Member(null, "이름", email, password, Role.ADMIN)); + When("로그인된 회원의 ID로 요청하면") { + every { jwtHandler.getMemberIdFromToken(any()) } returns user.id!! + every { memberRepository.findByIdOrNull(user.id!!) } returns user - Map loginParams = Map.of( - "email", email, - "password", password - ); + Then("회원의 이름을 응답한다") { + runGetTest(endpoint) { + statusCode(200) + body("data.name", `is`(user.name)) + } + } + } - String accessToken = RestAssured.given().log().all() - .contentType(ContentType.JSON) - .port(port) - .body(loginParams) - .when().post("/login") - .then().log().all().extract().cookie("accessToken"); + When("토큰은 있지만 회원을 찾을 수 없으면") { + val invalidMemberId: Long = -1L - return "accessToken=" + accessToken; - } + every { jwtHandler.getMemberIdFromToken(any()) } returns invalidMemberId + every { memberRepository.findByIdOrNull(invalidMemberId) } returns null + + Then("400 에러를 응답한다.") { + runGetTest(endpoint) { + statusCode(400) + body("errorType", `is`(ErrorType.MEMBER_NOT_FOUND.name)) + } + } + } + } + + Given("로그아웃 요청을 보낼 때") { + val endpoint: String = "/logout" + + When("로그인 상태가 아니라면") { + setUpNotLoggedIn() + + Then("로그인 페이지로 이동한다.") { + runPostTest(endpoint) { + log().all() + statusCode(302) + header("Location", containsString("/login")) + } + } + } + + When("로그인 상태라면") { + setUpUser() + every { memberRepository.findByIdOrNull(user.id!!) } returns user + + Then("토큰의 존재 여부와 무관하게 토큰을 만료시킨다.") { + runPostTest(endpoint) { + log().all() + statusCode(200) + cookie("accessToken", "") + header("Set-Cookie", containsString("Max-Age=0")) + } + } + } + } + } }