Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GETP-141 test: 로그인 컨트롤러 단위 테스트 및 API 문서 작성 #66

Merged
merged 4 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/docs/asciidoc/auth/login.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
operation::/login/login[snippets="http-request,request-fields,http-response,response-fields-data"]
operation::/login/login-error-code[snippets="error-code-fields"]
1 change: 1 addition & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
include::{docdir}/auth/signup.adoc[]

=== 로그인
include::{docdir}/auth/login.adoc[]

=== 회원 가입 시 이메일 인증 코드 전송

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import jakarta.validation.constraints.Pattern;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Pattern(
regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*_+=/])[A-Za-z\\d!@#$%^&*_+=/]{8,20}$")
regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*_+=/])[A-Za-z\\d!@#$%^&*_+=/]{8,20}$",
message = "{validation.constraints.Password.message}"
)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Constraint(validatedBy = {})
Expand Down
23 changes: 14 additions & 9 deletions src/main/java/es/princip/getp/domain/auth/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import es.princip.getp.domain.auth.domain.entity.TokenVerification;
import es.princip.getp.domain.auth.dto.request.LoginRequest;
import es.princip.getp.domain.auth.dto.response.Token;
import es.princip.getp.domain.auth.exception.LoginErrorCode;
import es.princip.getp.domain.auth.exception.TokenErrorCode;
import es.princip.getp.domain.auth.repository.TokenVerificationRepository;
import es.princip.getp.domain.member.service.MemberService;
Expand Down Expand Up @@ -33,16 +34,20 @@ public class AuthService {
public Token login(LoginRequest request) {
String email = request.email();
String password = request.password();
UsernamePasswordAuthenticationToken authenticationToken =
try {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(email, password);
Authentication authentication = authenticationManagerBuilder
.getObject()
.authenticate(authenticationToken);
PrincipalDetails principalDetails = (PrincipalDetails) authentication.getPrincipal();
Long memberId = principalDetails.getMember().getMemberId();
Token token = jwtTokenProvider.generateToken(authentication);
cacheRefreshToken(memberId, token.refreshToken());
return token;
Authentication authentication = authenticationManagerBuilder
.getObject()
.authenticate(authenticationToken);
PrincipalDetails principalDetails = (PrincipalDetails) authentication.getPrincipal();
Long memberId = principalDetails.getMember().getMemberId();
Token token = jwtTokenProvider.generateToken(authentication);
cacheRefreshToken(memberId, token.refreshToken());
return token;
} catch (Exception e) {
throw new BusinessLogicException(LoginErrorCode.INCORRECT_EMAIL_OR_PASSWORD);
}
}

public Token reissueAccessToken(HttpServletRequest servletRequest) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
package es.princip.getp.global.exception.handler;

import es.princip.getp.global.exception.BusinessLogicException;
import es.princip.getp.global.exception.ErrorCode;
import org.springframework.http.HttpStatus;
import es.princip.getp.global.util.ApiResponse;
import es.princip.getp.global.util.ApiResponse.ApiErrorResult;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import es.princip.getp.global.exception.BusinessLogicException;
import es.princip.getp.global.util.ApiResponse;
import es.princip.getp.global.util.ApiResponse.ApiErrorResult;

@RestControllerAdvice
public class BusinessLogicExceptionHandler {

@ExceptionHandler(BusinessLogicException.class)
public ResponseEntity<ApiErrorResult> businessLogicException(
final BusinessLogicException businessLogicException) {
ErrorCode errorCode = businessLogicException.getErrorCode();
HttpStatus status = errorCode.status();
return ResponseEntity.status(status).body(ApiResponse.error(errorCode));
public ResponseEntity<ApiErrorResult> validationException(
final BusinessLogicException exception) {
ErrorCode errorCode = exception.getErrorCode();
return ResponseEntity.status(errorCode.status())
.body(ApiResponse.error(errorCode.status(), errorCode.description()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import jakarta.validation.constraints.NotNull;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@NotNull
@NotNull(message = "{validation.constraints.Enum.message}")
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Constraint(validatedBy = {})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package es.princip.getp.global.validator.annotation;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import jakarta.validation.constraints.Pattern;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import jakarta.validation.constraints.Pattern;

@Pattern(
regexp = "^(https|ftp|mailto|tel)://.*")
regexp = "^(https|ftp|mailto|tel)://.*",
message = "{validation.constraints.Hyperlink.message}"
)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@Constraint(validatedBy = {})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import jakarta.validation.constraints.Pattern;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Pattern(
regexp = "^[^-]*$")
regexp = "^[^-]*$",
message = "{validation.constraints.PhoneNumber.message}"
)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@Constraint(validatedBy = {})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package es.princip.getp.domain.auth.controller;

import es.princip.getp.domain.auth.dto.request.LoginRequest;
import es.princip.getp.domain.auth.dto.response.Token;
import es.princip.getp.domain.auth.exception.LoginErrorCode;
import es.princip.getp.domain.auth.fixture.LoginFixture;
import es.princip.getp.domain.auth.service.AuthService;
import es.princip.getp.global.controller.ErrorCodeController;
import es.princip.getp.global.exception.BusinessLogicException;
import es.princip.getp.global.support.AbstractControllerTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;

import static es.princip.getp.global.support.ErrorCodeFields.errorCodeFields;
import static es.princip.getp.global.support.FieldDescriptorHelper.getDescriptor;
import static es.princip.getp.global.support.PayloadDocumentationHelper.responseFields;
import static org.mockito.BDDMockito.given;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest({AuthController.class, ErrorCodeController.class})
class AuthControllerTest extends AbstractControllerTest {

@MockBean
private AuthService authService;

@DisplayName("사용자는")
@Nested
class Login {

@Test
void loginErrorCode() throws Exception {
mockMvc.perform(get("/error-code/login"))
.andDo(restDocs.document(errorCodeFields(LoginErrorCode.values())));
}

@DisplayName("로그인을 할 수 있다.")
@Test
void login() throws Exception {
given(authService.login(LoginFixture.createLoginRequest()))
.willReturn(Token.builder()
.grantType("Bearer")
.accessToken("access-token")
.refreshToken("refresh-token")
.build());

mockMvc.perform(post("/auth/login")
.content(objectMapper.writeValueAsString(LoginFixture.createLoginRequest()))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated())
.andDo(
restDocs.document(
requestFields(
getDescriptor("email", "이메일", LoginRequest.class),
getDescriptor("password", "비밀번호", LoginRequest.class)
),
responseFields(
getDescriptor("grantType", "토큰 타입", Token.class)
.attributes(key("format").value("Bearer")),
getDescriptor("accessToken", "Access Token", Token.class),
getDescriptor("refreshToken", "Refresh Token", Token.class)
)
)
)
.andDo(print());
}

@DisplayName("올바르지 않은 이메일 또는 비밀번호인 경우 로그인할 수 없다.")
@Test
void login_WhenEmailAndPasswordIsIncorrect_ShouldFail() throws Exception {
given(authService.login(LoginFixture.createLoginRequest()))
.willThrow(new BusinessLogicException(LoginErrorCode.INCORRECT_EMAIL_OR_PASSWORD));

mockMvc.perform(post("/auth/login")
.content(objectMapper.writeValueAsString(LoginFixture.createLoginRequest()))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(errorCode(LoginErrorCode.INCORRECT_EMAIL_OR_PASSWORD))
.andDo(print());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package es.princip.getp.fixture;
package es.princip.getp.domain.auth.fixture;

import es.princip.getp.domain.auth.domain.entity.EmailVerification;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package es.princip.getp.domain.auth.fixture;

import es.princip.getp.domain.auth.dto.request.LoginRequest;

public class LoginFixture {
private static final String EMAIL = "[email protected]";
private static final String PASSWORD = "1q2w3e4r!";

public static LoginRequest createLoginRequest() {
return new LoginRequest(EMAIL, PASSWORD);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import java.util.Optional;

import static es.princip.getp.fixture.EmailVerificationFixture.createEmailVerification;
import static es.princip.getp.domain.auth.fixture.EmailVerificationFixture.createEmailVerification;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import es.princip.getp.domain.auth.dto.response.SignUpResponse;
import es.princip.getp.domain.member.domain.enums.MemberType;
import es.princip.getp.domain.serviceTerm.dto.reqeust.ServiceTermAgreementRequest;
import es.princip.getp.domain.serviceTerm.support.ServiceTermFixture;
import es.princip.getp.domain.serviceTerm.fixture.ServiceTermFixture;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import es.princip.getp.domain.member.dto.request.UpdateMemberRequest;
import es.princip.getp.domain.member.exception.MemberErrorCode;
import es.princip.getp.domain.member.repository.MemberRepository;
import es.princip.getp.domain.serviceTerm.fixture.ServiceTermFixture;
import es.princip.getp.domain.serviceTerm.service.ServiceTermService;
import es.princip.getp.domain.serviceTerm.support.ServiceTermFixture;
import es.princip.getp.domain.storage.service.ImageStorageService;
import es.princip.getp.global.exception.BusinessLogicException;
import es.princip.getp.global.util.PathUtil;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package es.princip.getp.domain.serviceTerm.support;
package es.princip.getp.domain.serviceTerm.fixture;

import es.princip.getp.domain.serviceTerm.dto.reqeust.ServiceTermAgreementRequest;
import es.princip.getp.domain.serviceTerm.dto.response.ServiceTermAgreementResponse;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package es.princip.getp.global.controller;

import es.princip.getp.domain.auth.exception.LoginErrorCode;
import es.princip.getp.domain.auth.exception.SignUpErrorCode;
import es.princip.getp.domain.storage.exception.ImageErrorCode;
import es.princip.getp.global.domain.dto.response.ErrorCodeResponse;
Expand All @@ -15,6 +16,15 @@
@RequestMapping("/error-code")
public class ErrorCodeController {

@GetMapping("/login")
public Map<String, ErrorCodeResponse> getLoginErrorCode() {
Map<String, ErrorCodeResponse> map = new HashMap<>();
for (ErrorCode errorCode : LoginErrorCode.values()) {
map.put(errorCode.description().code(), ErrorCodeResponse.from(errorCode));
}
return map;
}

@GetMapping("/signup")
public Map<String, ErrorCodeResponse> getSignUpErrorCode() {
Map<String, ErrorCodeResponse> map = new HashMap<>();
Expand Down