diff --git a/src/main/java/com/yam/app/account/application/AccountFacade.java b/src/main/java/com/yam/app/account/application/AccountFacade.java index b1a1911..034e88c 100644 --- a/src/main/java/com/yam/app/account/application/AccountFacade.java +++ b/src/main/java/com/yam/app/account/application/AccountFacade.java @@ -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)); } } diff --git a/src/main/java/com/yam/app/account/infrastructure/AccountPrincipal.java b/src/main/java/com/yam/app/account/infrastructure/AccountPrincipal.java index 553044f..fc67256 100644 --- a/src/main/java/com/yam/app/account/infrastructure/AccountPrincipal.java +++ b/src/main/java/com/yam/app/account/infrastructure/AccountPrincipal.java @@ -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; + } } diff --git a/src/main/java/com/yam/app/account/infrastructure/AppConfiguration.java b/src/main/java/com/yam/app/account/infrastructure/AppConfiguration.java index 9121b5a..184cd98 100644 --- a/src/main/java/com/yam/app/account/infrastructure/AppConfiguration.java +++ b/src/main/java/com/yam/app/account/infrastructure/AppConfiguration.java @@ -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)); } } diff --git a/src/main/java/com/yam/app/account/infrastructure/LoginSessionUtils.java b/src/main/java/com/yam/app/account/infrastructure/LoginSessionUtils.java deleted file mode 100644 index e9cd61d..0000000 --- a/src/main/java/com/yam/app/account/infrastructure/LoginSessionUtils.java +++ /dev/null @@ -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); - } -} diff --git a/src/main/java/com/yam/app/account/infrastructure/SessionBasedLoginAccountProcessor.java b/src/main/java/com/yam/app/account/infrastructure/SessionBasedLoginAccountProcessor.java index 45ee3b7..deed1d0 100644 --- a/src/main/java/com/yam/app/account/infrastructure/SessionBasedLoginAccountProcessor.java +++ b/src/main/java/com/yam/app/account/infrastructure/SessionBasedLoginAccountProcessor.java @@ -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)); } } diff --git a/src/main/java/com/yam/app/account/infrastructure/SessionManager.java b/src/main/java/com/yam/app/account/infrastructure/SessionManager.java new file mode 100644 index 0000000..7449b41 --- /dev/null +++ b/src/main/java/com/yam/app/account/infrastructure/SessionManager.java @@ -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); + } +} diff --git a/src/main/java/com/yam/app/account/presentation/AccountCommandApi.java b/src/main/java/com/yam/app/account/presentation/AccountCommandApi.java index d4a191b..190a025 100644 --- a/src/main/java/com/yam/app/account/presentation/AccountCommandApi.java +++ b/src/main/java/com/yam/app/account/presentation/AccountCommandApi.java @@ -29,7 +29,7 @@ public final class AccountCommandApi { @PostMapping("/api/accounts") public ResponseEntity 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 registerConfirm( - @ModelAttribute @Valid ConfirmRegisterAccountRequestCommand command) throws Exception { + @ModelAttribute @Valid ConfirmRegisterAccountCommand command) throws Exception { try { accountFacade.registerConfirm(command); } catch (Exception e) { diff --git a/src/main/java/com/yam/app/account/presentation/AccountQueryApi.java b/src/main/java/com/yam/app/account/presentation/AccountQueryApi.java index 6e1c0ab..773fa8a 100644 --- a/src/main/java/com/yam/app/account/presentation/AccountQueryApi.java +++ b/src/main/java/com/yam/app/account/presentation/AccountQueryApi.java @@ -29,31 +29,29 @@ public final class AccountQueryApi { @PostMapping("/api/accounts/login") public ResponseEntity 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 getAccount( + public ResponseEntity 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); } } diff --git a/src/main/java/com/yam/app/account/presentation/ConfirmRegisterAccountRequestCommand.java b/src/main/java/com/yam/app/account/presentation/ConfirmRegisterAccountCommand.java similarity index 81% rename from src/main/java/com/yam/app/account/presentation/ConfirmRegisterAccountRequestCommand.java rename to src/main/java/com/yam/app/account/presentation/ConfirmRegisterAccountCommand.java index dccd865..9c56877 100644 --- a/src/main/java/com/yam/app/account/presentation/ConfirmRegisterAccountRequestCommand.java +++ b/src/main/java/com/yam/app/account/presentation/ConfirmRegisterAccountCommand.java @@ -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; diff --git a/src/main/java/com/yam/app/account/presentation/LoginAccountRequestCommand.java b/src/main/java/com/yam/app/account/presentation/LoginAccountCommand.java similarity index 90% rename from src/main/java/com/yam/app/account/presentation/LoginAccountRequestCommand.java rename to src/main/java/com/yam/app/account/presentation/LoginAccountCommand.java index 7d5258c..7e4efa5 100644 --- a/src/main/java/com/yam/app/account/presentation/LoginAccountRequestCommand.java +++ b/src/main/java/com/yam/app/account/presentation/LoginAccountCommand.java @@ -6,7 +6,7 @@ import javax.validation.constraints.Pattern; import lombok.Data; @Data -public final class LoginAccountRequestCommand { +public final class LoginAccountCommand { @NotBlank @Email diff --git a/src/main/java/com/yam/app/account/presentation/LoginAccountMethodArgumentResolver.java b/src/main/java/com/yam/app/account/presentation/LoginAccountMethodArgumentResolver.java index 9435964..391c1f0 100644 --- a/src/main/java/com/yam/app/account/presentation/LoginAccountMethodArgumentResolver.java +++ b/src/main/java/com/yam/app/account/presentation/LoginAccountMethodArgumentResolver.java @@ -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(); } } diff --git a/src/main/java/com/yam/app/account/presentation/RegisterAccountRequestCommand.java b/src/main/java/com/yam/app/account/presentation/RegisterAccountCommand.java similarity index 91% rename from src/main/java/com/yam/app/account/presentation/RegisterAccountRequestCommand.java rename to src/main/java/com/yam/app/account/presentation/RegisterAccountCommand.java index 4556821..6acef0c 100644 --- a/src/main/java/com/yam/app/account/presentation/RegisterAccountRequestCommand.java +++ b/src/main/java/com/yam/app/account/presentation/RegisterAccountCommand.java @@ -6,7 +6,7 @@ import javax.validation.constraints.Pattern; import lombok.Data; @Data -public final class RegisterAccountRequestCommand { +public final class RegisterAccountCommand { @Email @NotBlank diff --git a/src/main/java/com/yam/app/account/presentation/WebConfiguration.java b/src/main/java/com/yam/app/account/presentation/WebConfiguration.java index ec717a7..a3a6349 100644 --- a/src/main/java/com/yam/app/account/presentation/WebConfiguration.java +++ b/src/main/java/com/yam/app/account/presentation/WebConfiguration.java @@ -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 resolvers) { - resolvers.add(loginAccountMethodArgumentResolver); - + resolvers.add(new LoginAccountMethodArgumentResolver()); } } diff --git a/src/test/java/com/yam/app/ArchUnitTests.java b/src/test/java/com/yam/app/ArchUnitTests.java index 817b385..7080560 100644 --- a/src/test/java/com/yam/app/ArchUnitTests.java +++ b/src/test/java/com/yam/app/ArchUnitTests.java @@ -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(); } diff --git a/src/test/java/com/yam/app/account/domain/ConfirmRegisterAccountProcessorTest.java b/src/test/java/com/yam/app/account/domain/ConfirmRegisterAccountProcessorTest.java index 4b8182c..a4df82a 100644 --- a/src/test/java/com/yam/app/account/domain/ConfirmRegisterAccountProcessorTest.java +++ b/src/test/java/com/yam/app/account/domain/ConfirmRegisterAccountProcessorTest.java @@ -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(); diff --git a/src/test/java/com/yam/app/account/domain/PasswordEncrypterStub.java b/src/test/java/com/yam/app/account/domain/PasswordEncrypterStub.java index de3c585..2fe5da1 100644 --- a/src/test/java/com/yam/app/account/domain/PasswordEncrypterStub.java +++ b/src/test/java/com/yam/app/account/domain/PasswordEncrypterStub.java @@ -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) { diff --git a/src/test/java/com/yam/app/account/infrastructure/SessionBasedLoginAccountProcessorTest.java b/src/test/java/com/yam/app/account/infrastructure/SessionBasedLoginAccountProcessorTest.java index 5c362e9..cf0d04e 100644 --- a/src/test/java/com/yam/app/account/infrastructure/SessionBasedLoginAccountProcessorTest.java +++ b/src/test/java/com/yam/app/account/infrastructure/SessionBasedLoginAccountProcessorTest.java @@ -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 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 diff --git a/src/test/java/com/yam/app/account/integration/AccountIntegrationTests.java b/src/test/java/com/yam/app/account/integration/AccountIntegrationTests.java index d290493..53ddc1c 100644 --- a/src/test/java/com/yam/app/account/integration/AccountIntegrationTests.java +++ b/src/test/java/com/yam/app/account/integration/AccountIntegrationTests.java @@ -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()); + } + } diff --git a/src/test/java/com/yam/app/account/presentation/AccountApiUri.java b/src/test/java/com/yam/app/account/presentation/AccountApiUri.java new file mode 100644 index 0000000..9ba4a93 --- /dev/null +++ b/src/test/java/com/yam/app/account/presentation/AccountApiUri.java @@ -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"; + +} diff --git a/src/test/java/com/yam/app/account/presentation/AccountCommandApiTests.java b/src/test/java/com/yam/app/account/presentation/AccountCommandApiTests.java index 9ca84c9..82d08a8 100644 --- a/src/test/java/com/yam/app/account/presentation/AccountCommandApiTests.java +++ b/src/test/java/com/yam/app/account/presentation/AccountCommandApiTests.java @@ -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 diff --git a/src/test/java/com/yam/app/account/presentation/AccountQueryApiTest.java b/src/test/java/com/yam/app/account/presentation/AccountQueryApiTest.java index 36bcbc1..aa1b807 100644 --- a/src/test/java/com/yam/app/account/presentation/AccountQueryApiTest.java +++ b/src/test/java/com/yam/app/account/presentation/AccountQueryApiTest.java @@ -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