Refactor code

- Remove util class
- Add SessionManager
- Refactor test code
This commit is contained in:
Rebwon
2021-09-14 10:44:38 +09:00
committed by MaengSol
parent d877744afd
commit b9e1e52de6
21 changed files with 217 additions and 229 deletions

View File

@@ -6,9 +6,9 @@ import com.yam.app.account.domain.LoginAccountProcessor;
import com.yam.app.account.domain.RegisterAccountEvent;
import com.yam.app.account.domain.RegisterAccountProcessor;
import com.yam.app.account.presentation.AccountResponse;
import com.yam.app.account.presentation.ConfirmRegisterAccountRequestCommand;
import com.yam.app.account.presentation.LoginAccountRequestCommand;
import com.yam.app.account.presentation.RegisterAccountRequestCommand;
import com.yam.app.account.presentation.ConfirmRegisterAccountCommand;
import com.yam.app.account.presentation.LoginAccountCommand;
import com.yam.app.account.presentation.RegisterAccountCommand;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -36,30 +36,30 @@ public class AccountFacade {
}
@Transactional
public AccountResponse register(RegisterAccountRequestCommand request) {
public AccountResponse register(RegisterAccountCommand command) {
var entity = registerProcessor.process(
request.getEmail(),
request.getNickname(),
request.getPassword()
command.getEmail(),
command.getNickname(),
command.getPassword()
);
publisher.publishEvent(new RegisterAccountEvent(entity));
return translator.toResponse(entity);
}
@Transactional
public void registerConfirm(ConfirmRegisterAccountRequestCommand request) {
confirmRegisterProcessor.registerConfirm(request.getToken(), request.getEmail());
public void registerConfirm(ConfirmRegisterAccountCommand command) {
confirmRegisterProcessor.registerConfirm(command.getToken(), command.getEmail());
}
@Transactional(readOnly = true)
public void login(LoginAccountRequestCommand request) {
loginProcessor.login(request.getEmail(), request.getPassword());
public void login(LoginAccountCommand command) {
loginProcessor.login(command.getEmail(), command.getPassword());
}
@Transactional(readOnly = true)
public AccountResponse getLoginAccount(String email) {
public AccountResponse findInfo(String email) {
return translator.toResponse(accountReader.findByEmail(email)
.orElseThrow(IllegalStateException::new));
.orElseThrow(IllegalArgumentException::new));
}
}

View File

@@ -1,14 +1,16 @@
package com.yam.app.account.infrastructure;
import java.io.Serializable;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import lombok.Data;
import lombok.Getter;
import lombok.ToString;
@Data
@Getter
@ToString
public final class AccountPrincipal implements Serializable {
@NotBlank
@Email
private final String email;
public AccountPrincipal(String email) {
this.email = email;
}
}

View File

@@ -7,6 +7,7 @@ import com.yam.app.account.domain.LoginAccountProcessor;
import com.yam.app.account.domain.PasswordEncrypter;
import com.yam.app.account.domain.RegisterAccountProcessor;
import com.yam.app.account.domain.TokenVerifier;
import javax.servlet.http.HttpSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -65,8 +66,9 @@ public class AppConfiguration {
@Bean
public LoginAccountProcessor loginAccountProcessor(AccountReader accountReader,
PasswordEncrypter passwordEncrypter) {
return new SessionBasedLoginAccountProcessor(accountReader, passwordEncrypter);
PasswordEncrypter passwordEncrypter, HttpSession httpSession) {
return new SessionBasedLoginAccountProcessor(accountReader, passwordEncrypter,
new SessionManager(httpSession));
}
}

View File

@@ -1,27 +0,0 @@
package com.yam.app.account.infrastructure;
import javax.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public final class LoginSessionUtils {
public static final String LOGIN_ACCOUNT_EMAIL = "LOGIN_ACCOUNT_EMAIL";
private LoginSessionUtils() {
}
private static HttpSession getHttpSession() {
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest()
.getSession(true);
}
public static AccountPrincipal getAccountPrincipal() {
return (AccountPrincipal) getHttpSession().getAttribute(LOGIN_ACCOUNT_EMAIL);
}
public static void setAccountPrincipal(AccountPrincipal accountPrincipal) {
getHttpSession().setAttribute(LOGIN_ACCOUNT_EMAIL, accountPrincipal);
}
}

View File

@@ -8,11 +8,14 @@ public final class SessionBasedLoginAccountProcessor implements LoginAccountProc
private final AccountReader accountReader;
private final PasswordEncrypter passwordEncrypter;
private final SessionManager sessionManager;
public SessionBasedLoginAccountProcessor(AccountReader accountReader,
PasswordEncrypter passwordEncrypter) {
PasswordEncrypter passwordEncrypter,
SessionManager sessionManager) {
this.accountReader = accountReader;
this.passwordEncrypter = passwordEncrypter;
this.sessionManager = sessionManager;
}
@Override
@@ -28,6 +31,6 @@ public final class SessionBasedLoginAccountProcessor implements LoginAccountProc
throw new IllegalStateException();
}
LoginSessionUtils.setAccountPrincipal(new AccountPrincipal(email));
sessionManager.setPrincipal(new AccountPrincipal(email));
}
}

View File

@@ -0,0 +1,22 @@
package com.yam.app.account.infrastructure;
import javax.servlet.http.HttpSession;
public final class SessionManager {
public static final String LOGIN_ACCOUNT = "LOGIN_ACCOUNT_EMAIL";
private final HttpSession httpSession;
public SessionManager(HttpSession httpSession) {
this.httpSession = httpSession;
}
public void setPrincipal(AccountPrincipal principal) {
this.httpSession.setAttribute(LOGIN_ACCOUNT, principal);
}
public AccountPrincipal fetchPrincipal() {
return (AccountPrincipal) httpSession.getAttribute(LOGIN_ACCOUNT);
}
}

View File

@@ -29,7 +29,7 @@ public final class AccountCommandApi {
@PostMapping("/api/accounts")
public ResponseEntity<AccountResponse> register(
@RequestBody @Valid RegisterAccountRequestCommand command) {
@RequestBody @Valid RegisterAccountCommand command) {
return ResponseEntity.ok(accountFacade.register(command));
}
@@ -40,7 +40,7 @@ public final class AccountCommandApi {
*/
@GetMapping("/api/accounts/authorize")
public ResponseEntity<Void> registerConfirm(
@ModelAttribute @Valid ConfirmRegisterAccountRequestCommand command) throws Exception {
@ModelAttribute @Valid ConfirmRegisterAccountCommand command) throws Exception {
try {
accountFacade.registerConfirm(command);
} catch (Exception e) {

View File

@@ -29,31 +29,29 @@ public final class AccountQueryApi {
@PostMapping("/api/accounts/login")
public ResponseEntity<Void> login(
@Valid @RequestBody LoginAccountRequestCommand request) {
@Valid @RequestBody LoginAccountCommand request) {
try {
accountFacade.login(request);
} catch (IllegalStateException e) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok().build();
}
@GetMapping("/api/accounts/me")
public ResponseEntity<AccountResponse> getAccount(
public ResponseEntity<AccountResponse> findInfo(
@LoginAccount AccountPrincipal accountPrincipal) {
if (accountPrincipal == null) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
AccountResponse accountResponse;
try {
accountResponse = accountFacade.getLoginAccount(accountPrincipal.getEmail());
return ResponseEntity.ok(accountFacade.findInfo(
accountPrincipal.getEmail()));
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(accountResponse);
}
}

View File

@@ -5,7 +5,7 @@ import javax.validation.constraints.NotBlank;
import lombok.Data;
@Data
public final class ConfirmRegisterAccountRequestCommand {
public final class ConfirmRegisterAccountCommand {
@NotBlank
private String token;

View File

@@ -6,7 +6,7 @@ import javax.validation.constraints.Pattern;
import lombok.Data;
@Data
public final class LoginAccountRequestCommand {
public final class LoginAccountCommand {
@NotBlank
@Email

View File

@@ -1,16 +1,14 @@
package com.yam.app.account.presentation;
import com.yam.app.account.infrastructure.LoginSessionUtils;
import com.yam.app.account.infrastructure.SessionManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
@Component
public final class LoginAccountMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
@@ -28,7 +26,7 @@ public final class LoginAccountMethodArgumentResolver implements HandlerMethodAr
return null;
}
return session.getAttribute(LoginSessionUtils.LOGIN_ACCOUNT_EMAIL);
var sessionManager = new SessionManager(session);
return sessionManager.fetchPrincipal();
}
}

View File

@@ -6,7 +6,7 @@ import javax.validation.constraints.Pattern;
import lombok.Data;
@Data
public final class RegisterAccountRequestCommand {
public final class RegisterAccountCommand {
@Email
@NotBlank

View File

@@ -8,16 +8,8 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
private final LoginAccountMethodArgumentResolver loginAccountMethodArgumentResolver;
public WebConfiguration(
LoginAccountMethodArgumentResolver loginAccountMethodArgumentResolver) {
this.loginAccountMethodArgumentResolver = loginAccountMethodArgumentResolver;
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginAccountMethodArgumentResolver);
resolvers.add(new LoginAccountMethodArgumentResolver());
}
}

View File

@@ -25,6 +25,6 @@ final class ArchUnitTests {
.whereLayer("Presentation").mayOnlyBeAccessedByLayers("Application", "Integration")
.whereLayer("Application").mayOnlyBeAccessedByLayers("Presentation", "Domain")
.whereLayer("Domain").mayOnlyBeAccessedByLayers("Application", "Infrastructure")
.whereLayer("Infrastructure").mayOnlyBeAccessedByLayers("Presentation")
.whereLayer("Infrastructure").mayOnlyBeAccessedByLayers("Presentation", "Integration")
.whereLayer("Integration").mayNotBeAccessedByAnyLayer();
}

View File

@@ -29,7 +29,7 @@ final class ConfirmRegisterAccountProcessorTest {
// Act
confirmRegisterAccountProcessor.registerConfirm(account.getEmailCheckToken(),
account.getEmail());
Account updatedAccount = accountRepository.findByEmail(account.getEmail()).get();
var updatedAccount = accountRepository.findByEmail(account.getEmail()).get();
// Assert
assertThat(updatedAccount.isEmailVerified()).isTrue();

View File

@@ -1,6 +1,6 @@
package com.yam.app.account.domain;
public class PasswordEncrypterStub implements PasswordEncrypter {
public final class PasswordEncrypterStub implements PasswordEncrypter {
@Override
public String encode(CharSequence rawPassword) {

View File

@@ -12,31 +12,43 @@ import java.util.Collection;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.springframework.mock.web.MockHttpSession;
@DisplayName("세션 기반 회원 로그인 처리기")
final class SessionBasedLoginAccountProcessorTest {
@TestFactory
@DisplayName("세션 회원 로그인 검증 테스트")
@DisplayName("회원 로그인 검증 테스트")
Collection<DynamicTest> login_success() {
//Arrange
var accountRepository = new FakeAccountRepository();
var accountNotConfirm = Account.of("hello1@naver.com", "hello1",
var fakeObject = new FakeAccountRepository();
final var accountRepository = fakeObject;
final var accountReader = fakeObject;
var accountNotYetConfirm = Account.of("hello1@naver.com", "hello1",
"password!");
var accountCompleted = Account.of("hello@naver.com", "hello",
"password!");
accountCompleted.completeRegister();
accountRepository.save(accountCompleted);
accountRepository.save(accountNotConfirm);
accountRepository.save(accountNotYetConfirm);
var passwordEncryptor = new PasswordEncrypterStub();
var loginAccountProcessor = new SessionBasedLoginAccountProcessor(accountRepository,
passwordEncryptor);
var sessionManager = new SessionManager(new MockHttpSession());
var loginProcessor = new SessionBasedLoginAccountProcessor(accountReader,
new PasswordEncrypterStub(), sessionManager);
return Arrays.asList(
dynamicTest("로그인이 성공적으로 완료되며, 세션에 값이 저장된다.", () -> {
// Act
loginProcessor.login("hello@naver.com", "password!");
// Assert
var result = sessionManager.fetchPrincipal();
assertThat(result).isNotNull();
}),
dynamicTest("이메일이 유효하지 않은 경우 예외를 리턴한다.", () -> {
// Act
var throwable = catchThrowable(
() -> loginAccountProcessor.login("dwqko@naver.com",
() -> loginProcessor.login("dwqko@naver.com",
accountCompleted.getPassword())
);
@@ -46,8 +58,8 @@ final class SessionBasedLoginAccountProcessorTest {
dynamicTest("이메일은 유효하나 검증을 완료하지 않은 경우 예외를 리턴한다.", () -> {
// Act
var throwable = catchThrowable(
() -> loginAccountProcessor.login(accountNotConfirm.getEmail(),
accountNotConfirm.getPassword())
() -> loginProcessor.login(accountNotYetConfirm.getEmail(),
accountNotYetConfirm.getPassword())
);
// Assert
@@ -56,7 +68,7 @@ final class SessionBasedLoginAccountProcessorTest {
dynamicTest("비밀번호가 유효하지 않은 경우 예외를 리턴한다.", () -> {
// Act
var throwable = catchThrowable(
() -> loginAccountProcessor.login(accountCompleted.getEmail(), "11111111!")
() -> loginProcessor.login(accountCompleted.getEmail(), "11111111!")
);
// Assert

View File

@@ -1,5 +1,9 @@
package com.yam.app.account.integration;
import static com.yam.app.account.presentation.AccountApiUri.EMAIL_CONFIRM;
import static com.yam.app.account.presentation.AccountApiUri.FIND_INFO;
import static com.yam.app.account.presentation.AccountApiUri.LOGIN;
import static com.yam.app.account.presentation.AccountApiUri.REGISTER;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
@@ -8,14 +12,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yam.app.account.presentation.LoginAccountRequestCommand;
import com.yam.app.account.presentation.RegisterAccountRequestCommand;
import com.yam.app.account.infrastructure.AccountPrincipal;
import com.yam.app.account.infrastructure.SessionManager;
import com.yam.app.account.presentation.LoginAccountCommand;
import com.yam.app.account.presentation.RegisterAccountCommand;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
@@ -25,25 +32,27 @@ import org.springframework.test.web.servlet.MockMvc;
@ActiveProfiles("test")
final class AccountIntegrationTests {
private static final String EMAIL_CONFIRM_SUCCESS_REDIRECT_URI = "http://localhost:3000/login";
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
@DisplayName("새로운 계정 등록하는 회원가입 시나리오")
@DisplayName("새로운 계정 등록에 적절한 파라미터가 입력되고, 계정이 성공적으로 등록된다.")
void new_account_request_in_register_correctly() throws Exception {
// Arrange
var request = new RegisterAccountRequestCommand();
request.setEmail("msolo021015@gmail.com");
request.setNickname("rebwon");
request.setPassword("password!");
var command = new RegisterAccountCommand();
command.setEmail("msolo021015@gmail.com");
command.setNickname("rebwon");
command.setPassword("password!");
// Act
final var actions = mockMvc.perform(post("/api/accounts")
final var actions = mockMvc.perform(post(REGISTER)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.content(objectMapper.writeValueAsString(command))
);
// Assert
@@ -56,10 +65,10 @@ final class AccountIntegrationTests {
}
@Test
@DisplayName("이메일과 토큰을 검증하고 회원의 상태를 업데이트하는 시나리오")
@DisplayName("이메일 인증에 적절한 토큰과 이메일 정보가 입력되고, 이메일 인증 상태가 성공적으로 압데이트 된다.")
void email_and_token_verify_request_in_correctly() throws Exception {
// Act
final var actions = mockMvc.perform(get("/api/accounts/authorize")
final var actions = mockMvc.perform(get(EMAIL_CONFIRM)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.param("token", "emailchecktoken")
@@ -70,22 +79,22 @@ final class AccountIntegrationTests {
actions
.andDo(print())
.andExpect(status().isSeeOther())
.andExpect(redirectedUrl("http://localhost:3000/login"));
.andExpect(redirectedUrl(EMAIL_CONFIRM_SUCCESS_REDIRECT_URI));
}
@Test
@DisplayName("로그인 요청을 성공하여 서버의 세션 등록을 완료하는 시나리오")
@DisplayName("로그인에 적절한 파라미터를 입력하고 로그인 요청이 성공적으로 완료된다.")
void login_success() throws Exception {
//Arrange
LoginAccountRequestCommand request = new LoginAccountRequestCommand();
request.setEmail("loginCheck@gmail.com");
request.setPassword("password!");
var command = new LoginAccountCommand();
command.setEmail("loginCheck@gmail.com");
command.setPassword("password!");
//Act
final var actions = mockMvc.perform(post("/api/accounts/login")
final var actions = mockMvc.perform(post(LOGIN)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.content(objectMapper.writeValueAsString(command))
);
//Assert
@@ -94,4 +103,28 @@ final class AccountIntegrationTests {
.andExpect(status().isOk());
}
@Test
@DisplayName("인증된 기본 사용자가 자신의 정보를 조회한다.")
void authentication_member_find_info_success() throws Exception {
//Arrange
var session = new MockHttpSession();
session.setAttribute(SessionManager.LOGIN_ACCOUNT,
new AccountPrincipal("loginCheck@gmail.com"));
//Act
final var actions = mockMvc.perform(get(FIND_INFO)
.session(session)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
);
//Assert
actions
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").isNumber())
.andExpect(jsonPath("$.email").isString())
.andExpect(jsonPath("$.nickname").isString());
}
}

View File

@@ -0,0 +1,10 @@
package com.yam.app.account.presentation;
public interface AccountApiUri {
String REGISTER = "/api/accounts";
String EMAIL_CONFIRM = "/api/accounts/authorize";
String LOGIN = "/api/accounts/login";
String FIND_INFO = "/api/accounts/me";
}

View File

@@ -1,5 +1,7 @@
package com.yam.app.account.presentation;
import static com.yam.app.account.presentation.AccountApiUri.EMAIL_CONFIRM;
import static com.yam.app.account.presentation.AccountApiUri.REGISTER;
import static org.mockito.Mockito.doThrow;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
@@ -35,7 +37,6 @@ final class AccountCommandApiTests {
@DisplayName("이메일 검증 HTTP API")
class RegisterConfirmApi {
private static final String EMAIL_AUTHORIZE_API = "/api/accounts/authorize";
private static final String TOKEN = "token";
private static final String EMAIL = "email";
@@ -44,12 +45,12 @@ final class AccountCommandApiTests {
@DisplayName("HTTP 파라메타가 비었거나 null인 검증요청을 보낸 경우 400 HTTP Code 리턴한다.")
void http_param_is_empty_or_null(String args) throws Exception {
// Arrange
var request = new ConfirmRegisterAccountRequestCommand();
request.setToken(args);
request.setEmail(args);
var command = new ConfirmRegisterAccountCommand();
command.setToken(args);
command.setEmail(args);
// Act
final var actions = mockMvc.perform(get(EMAIL_AUTHORIZE_API)
final var actions = mockMvc.perform(get(EMAIL_CONFIRM)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.param(TOKEN, args)
@@ -67,14 +68,14 @@ final class AccountCommandApiTests {
@DisplayName("HTTP 파라메타가 유효하지 않은 값으로 검증요청을 보낸 경우 400 HTTP Code 리턴한다.")
void http_param_is_not_valid(String arg) throws Exception {
// Arrange
var request = new ConfirmRegisterAccountRequestCommand();
request.setToken(arg);
request.setEmail(arg);
var command = new ConfirmRegisterAccountCommand();
command.setToken(arg);
command.setEmail(arg);
// Act
doThrow(IllegalStateException.class).when(accountFacade).registerConfirm(request);
doThrow(IllegalStateException.class).when(accountFacade).registerConfirm(command);
final var actions = mockMvc.perform(get(EMAIL_AUTHORIZE_API)
final var actions = mockMvc.perform(get(EMAIL_CONFIRM)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.param(TOKEN, arg)
@@ -92,22 +93,20 @@ final class AccountCommandApiTests {
@DisplayName("회원가입 등록 HTTP API")
class RegisterApi {
private static final String REGISTER_API = "/api/accounts";
@ParameterizedTest
@AutoSource
@DisplayName("Accept와 Content-Type을 지정하지 않아, HttpMediaTypeNotSupportedException 발생.")
void register_account_api_not_use_accept_header_and_content_type(String arg)
throws Exception {
// Arrange
var request = new RegisterAccountRequestCommand();
request.setEmail(arg);
request.setNickname(arg);
request.setPassword(arg);
var command = new RegisterAccountCommand();
command.setEmail(arg);
command.setNickname(arg);
command.setPassword(arg);
// Act
final var actions = mockMvc.perform(post(REGISTER_API)
.content(objectMapper.writeValueAsString(request))
final var actions = mockMvc.perform(post(REGISTER)
.content(objectMapper.writeValueAsString(command))
);
// Assert
@@ -120,16 +119,16 @@ final class AccountCommandApiTests {
@DisplayName("HTTP 입력이 Null이거나 Emtpy인 경우 검증에 실패하여 에러를 응답한다.")
void register_http_parameter_is_null_and_empty(String arg) throws Exception {
// Arrange
var request = new RegisterAccountRequestCommand();
request.setEmail(arg);
request.setNickname(arg);
request.setPassword(arg);
var command = new RegisterAccountCommand();
command.setEmail(arg);
command.setNickname(arg);
command.setPassword(arg);
// Act
final var actions = mockMvc.perform(post(REGISTER_API)
final var actions = mockMvc.perform(post(REGISTER)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.content(objectMapper.writeValueAsString(command))
);
// Assert
@@ -139,20 +138,20 @@ final class AccountCommandApiTests {
}
@ParameterizedTest
@AutoSource
@DisplayName("HTTP 입력 파라미터가 이메일, 비밀번호 검증에 실패하여 에러를 응답한다.")
@ValueSource(strings = {"@@@@@@@@@", "@naver.com", "jiwon"})
@DisplayName("요청 Body의 이메일 형식이 맞지 않는 경우 400 에러를 반환한다.")
void register_http_parameter_is_invalid_email_and_password(String arg) throws Exception {
// Arrange
var request = new RegisterAccountRequestCommand();
request.setEmail(arg);
request.setNickname(arg);
request.setPassword(arg);
var command = new RegisterAccountCommand();
command.setEmail(arg);
command.setNickname("jiwon");
command.setPassword("password1!");
// Act
final var actions = mockMvc.perform(post(REGISTER_API)
final var actions = mockMvc.perform(post(REGISTER)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.content(objectMapper.writeValueAsString(command))
);
// Assert
@@ -163,19 +162,19 @@ final class AccountCommandApiTests {
@ParameterizedTest
@ValueSource(strings = {"1", "a", "1a234567890123456"})
@DisplayName("요청 Body 의 비밀번호 형식이 맞지 않은 경우 400 에러를 반환한다.")
@DisplayName("요청 Body의 비밀번호 형식이 맞지 않은 경우 400 에러를 반환한다.")
void http_json_password_is_invalid(String args) throws Exception {
// Arrange
var request = new RegisterAccountRequestCommand();
request.setEmail("jiwon@naver.com");
request.setNickname("jiwon");
request.setPassword(args);
var command = new RegisterAccountCommand();
command.setEmail("jiwon@naver.com");
command.setNickname("jiwon");
command.setPassword(args);
// Act
final var actions = mockMvc.perform(post(REGISTER_API)
final var actions = mockMvc.perform(post(REGISTER)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.content(objectMapper.writeValueAsString(command))
);
// Assert

View File

@@ -1,18 +1,15 @@
package com.yam.app.account.presentation;
import static org.mockito.Mockito.doNothing;
import static com.yam.app.account.presentation.AccountApiUri.FIND_INFO;
import static com.yam.app.account.presentation.AccountApiUri.LOGIN;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yam.app.account.application.AccountFacade;
import com.yam.app.account.infrastructure.AccountPrincipal;
import com.yam.app.account.infrastructure.LoginSessionUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -23,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.web.servlet.MockMvc;
@DisplayName("Account Query HTTP API")
@@ -42,39 +38,10 @@ class AccountQueryApiTest {
class LoginApi {
@Test
@DisplayName("로그인한 회원이 요청하면 세션에서 Account 정보를 성공적으로 반환 받는다.")
void login_member_get_account_session_request() throws Exception {
//Arrange
var session = new MockHttpSession();
session.setAttribute(LoginSessionUtils.LOGIN_ACCOUNT_EMAIL,
new AccountPrincipal("loginCheck@gmail.com"));
when(accountFacade.getLoginAccount("loginCheck@gmail.com"))
.thenReturn(new AccountResponse(1L, "loginCheck@gmail.com",
"loginNick"));
//Act
final var actions = mockMvc.perform(get("/api/accounts/me")
.session(session)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON));
//Assert
actions
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").isNumber())
.andExpect(jsonPath("$.email").isString())
.andExpect(jsonPath("$.nickname").isString());
session.clearAttributes();
}
@Test
@DisplayName("세션이 없는 상태로 Account 정보를 요청하면 401 에러를 반환한다.")
@DisplayName("인증되지 않은 상태로 요청을 보낸 경우 401 에러를 반환한다.")
void no_current_session_account_request() throws Exception {
//Act
final var actions = mockMvc.perform(get("/api/accounts/me")
final var actions = mockMvc.perform(get(FIND_INFO)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON));
@@ -84,39 +51,18 @@ class AccountQueryApiTest {
.andExpect(status().isUnauthorized());
}
@Test
@DisplayName("정상적인 이메일과 비밀번호를 보내 로그인에 성공하고 200을 반환한다.")
void login_success() throws Exception {
//Arrange
LoginAccountRequestCommand request = new LoginAccountRequestCommand();
request.setEmail("jiwon@gmail.com");
request.setPassword("password!");
doNothing().when(accountFacade).login(request);
//Act
final var actions = mockMvc.perform(post("/api/accounts/login")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
);
//Assert
actions
.andDo(print())
.andExpect(status().isOk());
}
@Test
@DisplayName("이메일, 비밀번호 형식은 유효하나 로그인이 실패한 경우 401 에러를 반환한다.")
void login_fail() throws Exception {
// Arrange
var request = new LoginAccountRequestCommand();
var request = new LoginAccountCommand();
request.setEmail("wejiwef@naver.com");
request.setPassword("password1!");
doThrow(IllegalStateException.class).when(accountFacade).login(request);
// Act
final var actions = mockMvc.perform(post("/api/accounts/login")
doThrow(IllegalStateException.class).when(accountFacade).login(request);
final var actions = mockMvc.perform(post(LOGIN)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
@@ -130,16 +76,15 @@ class AccountQueryApiTest {
@ParameterizedTest
@ValueSource(strings = {"1", "a", "1a234567890123456"})
@DisplayName("요청 Body 의 비밀번호 형식이 맞지 않은 경우 400 에러를 반환한다.")
@DisplayName("요청 Body의 비밀번호 형식이 맞지 않은 경우 400 에러를 반환한다.")
void http_json_password_is_invalid(String args) throws Exception {
// Arrange
var request = new LoginAccountRequestCommand();
var request = new LoginAccountCommand();
request.setEmail("jiwon22@gmail.com");
request.setPassword(args);
doThrow(IllegalStateException.class).when(accountFacade).login(request);
// Act
final var actions = mockMvc.perform(post("/api/accounts/login")
final var actions = mockMvc.perform(post(LOGIN)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
@@ -153,19 +98,18 @@ class AccountQueryApiTest {
@ParameterizedTest
@ValueSource(strings = {"@@@@@@@@@", "@naver.com", "jiwon"})
@DisplayName("요청 Body 의 이메일 형식이 맞지 않은 경우 400 에러를 반환한다.")
void http_json_email_is_invalid() throws Exception {
@DisplayName("요청 Body의 이메일 형식이 맞지 않은 경우 400 에러를 반환한다.")
void http_json_email_is_invalid(String email) throws Exception {
// Arrange
var request = new LoginAccountRequestCommand();
request.setEmail("DQWJIDWQ291");
request.setPassword("1abcabcabc");
doThrow(IllegalStateException.class).when(accountFacade).login(request);
var command = new LoginAccountCommand();
command.setEmail(email);
command.setPassword("password!1");
// Act
final var actions = mockMvc.perform(post("/api/accounts/login")
final var actions = mockMvc.perform(post(LOGIN)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.content(objectMapper.writeValueAsString(command))
);
// Assert
@@ -176,18 +120,18 @@ class AccountQueryApiTest {
@ParameterizedTest
@NullAndEmptySource
@DisplayName("요청 Body 의 이메일,비밀번호가 null 이거나 비어있다면 400 에러를 반환한다.")
@DisplayName("요청 Body의 이메일,비밀번호가 null 이거나 비어있다면 400 에러를 반환한다.")
void http_json_value_is_empty_or_null(String args) throws Exception {
//Arrange
var request = new LoginAccountRequestCommand();
request.setEmail(args);
request.setPassword(args);
var command = new LoginAccountCommand();
command.setEmail(args);
command.setPassword(args);
//Act
final var actions = mockMvc.perform(post("/api/accounts/login")
final var actions = mockMvc.perform(post(LOGIN)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.content(objectMapper.writeValueAsString(command))
);
//Assert