Merge pull request #29 from f-lab-edu/feature-verify-email-token
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
package com.yam.app.account.application;
|
||||
|
||||
import com.yam.app.account.domain.ConfirmRegisterAccountProcessor;
|
||||
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.ConfirmRegisterAccountRequest;
|
||||
import com.yam.app.account.presentation.RegisterAccountRequest;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -14,13 +16,15 @@ public class AccountFacade {
|
||||
private final RegisterAccountProcessor processor;
|
||||
private final AccountTranslator translator;
|
||||
private final ApplicationEventPublisher publisher;
|
||||
private final ConfirmRegisterAccountProcessor confirmRegisterProcessor;
|
||||
|
||||
public AccountFacade(RegisterAccountProcessor processor,
|
||||
AccountTranslator translator,
|
||||
ApplicationEventPublisher publisher) {
|
||||
AccountTranslator translator, ApplicationEventPublisher publisher,
|
||||
ConfirmRegisterAccountProcessor confirmRegisterProcessor) {
|
||||
this.processor = processor;
|
||||
this.translator = translator;
|
||||
this.publisher = publisher;
|
||||
this.confirmRegisterProcessor = confirmRegisterProcessor;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -29,4 +33,8 @@ public class AccountFacade {
|
||||
publisher.publishEvent(new RegisterAccountEvent(entity));
|
||||
return translator.toResponse(entity);
|
||||
}
|
||||
|
||||
public void registerConfirm(ConfirmRegisterAccountRequest request) {
|
||||
confirmRegisterProcessor.registerConfirm(translator.toCommand(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.yam.app.account.application;
|
||||
|
||||
import com.yam.app.account.domain.Account;
|
||||
import com.yam.app.account.presentation.AccountResponse;
|
||||
import com.yam.app.account.presentation.ConfirmRegisterAccountRequest;
|
||||
import com.yam.app.account.presentation.RegisterAccountRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -13,6 +14,10 @@ final class AccountTranslator {
|
||||
request.getPassword());
|
||||
}
|
||||
|
||||
public ConfirmRegisterAccountCommand toCommand(ConfirmRegisterAccountRequest request) {
|
||||
return new ConfirmRegisterAccountCommand(request.getToken(), request.getEmail());
|
||||
}
|
||||
|
||||
public AccountResponse toResponse(Account entity) {
|
||||
return new AccountResponse(entity.getId(), entity.getEmail(),
|
||||
entity.getNickname());
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.yam.app.account.application;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class ConfirmRegisterAccountCommand {
|
||||
|
||||
private final String token;
|
||||
private final String email;
|
||||
|
||||
public ConfirmRegisterAccountCommand(String token, String email) {
|
||||
this.token = token;
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,7 @@ public interface AccountRepository {
|
||||
boolean existsByNickname(String nickname);
|
||||
|
||||
Account save(Account entity);
|
||||
|
||||
Account update(Account entity);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.yam.app.account.domain;
|
||||
|
||||
import com.yam.app.account.application.ConfirmRegisterAccountCommand;
|
||||
|
||||
public final class ConfirmRegisterAccountProcessor {
|
||||
|
||||
private final AccountReader accountReader;
|
||||
private final AccountRepository accountRepository;
|
||||
private final TokenVerifier tokenVerifier;
|
||||
|
||||
public ConfirmRegisterAccountProcessor(AccountReader accountReader,
|
||||
AccountRepository accountRepository, TokenVerifier tokenVerifier) {
|
||||
this.accountReader = accountReader;
|
||||
this.accountRepository = accountRepository;
|
||||
this.tokenVerifier = tokenVerifier;
|
||||
}
|
||||
|
||||
public void registerConfirm(ConfirmRegisterAccountCommand command) {
|
||||
tokenVerifier.verify(command.getToken(), command.getEmail());
|
||||
Account account = accountReader.findByEmail(command.getEmail());
|
||||
account.completeRegister();
|
||||
accountRepository.update(account);
|
||||
}
|
||||
}
|
||||
23
src/main/java/com/yam/app/account/domain/TokenVerifier.java
Normal file
23
src/main/java/com/yam/app/account/domain/TokenVerifier.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.yam.app.account.domain;
|
||||
|
||||
public final class TokenVerifier {
|
||||
|
||||
private final AccountReader accountReader;
|
||||
|
||||
public TokenVerifier(AccountReader accountReader) {
|
||||
this.accountReader = accountReader;
|
||||
}
|
||||
|
||||
public void verify(String token, String email) {
|
||||
var account = accountReader.findByEmail(email);
|
||||
|
||||
if (account == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
if (!account.isValidToken(token)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package com.yam.app.account.infrastructure;
|
||||
|
||||
import com.yam.app.account.domain.AccountReader;
|
||||
import com.yam.app.account.domain.AccountRepository;
|
||||
import com.yam.app.account.domain.ConfirmRegisterAccountProcessor;
|
||||
import com.yam.app.account.domain.PasswordEncrypter;
|
||||
import com.yam.app.account.domain.RegisterAccountProcessor;
|
||||
import com.yam.app.account.domain.TokenVerifier;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -46,4 +48,16 @@ public class AppConfiguration {
|
||||
PasswordEncrypter passwordEncrypter) {
|
||||
return new RegisterAccountProcessor(accountRepository, passwordEncrypter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TokenVerifier tokenVerifier(AccountReader accountReader) {
|
||||
return new TokenVerifier(accountReader);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ConfirmRegisterAccountProcessor confirmRegisterAccountProcessor(
|
||||
AccountReader accountReader, AccountRepository accountRepository,
|
||||
TokenVerifier tokenVerifier) {
|
||||
return new ConfirmRegisterAccountProcessor(accountReader, accountRepository, tokenVerifier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,16 @@ public final class MybatisAccountRepository implements AccountRepository, Accoun
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account update(Account entity) {
|
||||
int result = template.update(COMMAND_NAMESPACE + "update", entity);
|
||||
if (result != 1) {
|
||||
throw new RuntimeException(
|
||||
String.format("There was a problem updating the object : %s", entity));
|
||||
}
|
||||
return findByEmail(entity.getEmail());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsByNickname(String nickname) {
|
||||
int result = template.selectOne(COMMAND_NAMESPACE + "existsByNickname", nickname);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.yam.app.account.presentation;
|
||||
|
||||
import com.yam.app.account.application.AccountFacade;
|
||||
import java.net.URI;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(
|
||||
produces = MediaType.APPLICATION_JSON_VALUE,
|
||||
consumes = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
public final class AccountCommandApi {
|
||||
|
||||
private final AccountFacade accountFacade;
|
||||
|
||||
public AccountCommandApi(AccountFacade accountFacade) {
|
||||
this.accountFacade = accountFacade;
|
||||
}
|
||||
|
||||
@PostMapping("/api/accounts")
|
||||
public ResponseEntity<AccountResponse> register(
|
||||
@RequestBody @Valid RegisterAccountRequest request) {
|
||||
return ResponseEntity.ok(accountFacade.register(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 회원가입 이메일 검증 컨트롤러
|
||||
* - 임시로 "http://localhost:3000/login"로 리다이렉트 되도록 설정.
|
||||
* - 임시로 검증에 실패해서 예외가 발생하면 400 HTTP 을 반환하도록 설정.
|
||||
*/
|
||||
@GetMapping("/api/accounts/authorize")
|
||||
public ResponseEntity<Void> registerConfirm(
|
||||
@ModelAttribute ConfirmRegisterAccountRequest request) throws Exception {
|
||||
try {
|
||||
accountFacade.registerConfirm(request);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
var uri = new URI("http://localhost:3000/login");
|
||||
var header = new HttpHeaders();
|
||||
header.setLocation(uri);
|
||||
return new ResponseEntity<>(header, HttpStatus.SEE_OTHER);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.yam.app.account.presentation;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public final class ConfirmRegisterAccountRequest {
|
||||
|
||||
@NotBlank
|
||||
private String token;
|
||||
|
||||
@Email
|
||||
@NotBlank
|
||||
private String email;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.yam.app.account.presentation;
|
||||
|
||||
import com.yam.app.account.application.AccountFacade;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(
|
||||
produces = MediaType.APPLICATION_JSON_VALUE,
|
||||
consumes = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
public final class RegisterAccountApi {
|
||||
|
||||
private final AccountFacade accountFacade;
|
||||
|
||||
public RegisterAccountApi(AccountFacade accountFacade) {
|
||||
this.accountFacade = accountFacade;
|
||||
}
|
||||
|
||||
@PostMapping("/api/accounts")
|
||||
public ResponseEntity<AccountResponse> register(
|
||||
@RequestBody @Valid RegisterAccountRequest request) {
|
||||
return ResponseEntity.ok(accountFacade.register(request));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,17 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.yam.app.account.domain.AccountRepository">
|
||||
<update id="update" parameterType="com.yam.app.account.domain.Account">
|
||||
UPDATE ACCOUNT
|
||||
SET email = #{email},
|
||||
email_check_token = #{emailCheckToken},
|
||||
email_check_token_generated_at = #{emailCheckTokenGeneratedAt},
|
||||
email_verified = #{emailVerified},
|
||||
nickname = #{nickname},
|
||||
password = #{password},
|
||||
withdraw = #{withdraw}
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
<select id="existsByEmail" parameterType="String" resultType="int">
|
||||
SELECT COUNT(email)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.yam.app.account.domain;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import com.yam.app.account.application.ConfirmRegisterAccountCommand;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
|
||||
@DisplayName("회원 검증 도메인 서비스")
|
||||
class ConfirmRegisterAccountProcessorTest {
|
||||
|
||||
@TestFactory
|
||||
@DisplayName("이메일 검증 시나리오")
|
||||
Collection<DynamicTest> verify_account_scenarios() {
|
||||
var accountRepository = new FakeAccountRepository();
|
||||
var accountReader = accountRepository;
|
||||
var tokenVerifier = new TokenVerifier(accountReader);
|
||||
var confirmRegisterAccountProcessor = new ConfirmRegisterAccountProcessor(accountReader,
|
||||
accountRepository, tokenVerifier);
|
||||
|
||||
var account = accountRepository.save(
|
||||
Account.of("jiwonDev@gmail.com", "jiwon", "password!"));
|
||||
|
||||
return Arrays.asList(
|
||||
DynamicTest.dynamicTest("회원 이메일 토큰검증에 성공한다.", () -> {
|
||||
//Arrange
|
||||
var command = new ConfirmRegisterAccountCommand(account.getEmailCheckToken(),
|
||||
account.getEmail());
|
||||
|
||||
// Act
|
||||
confirmRegisterAccountProcessor.registerConfirm(command);
|
||||
Account updatedAccount = accountRepository.findByEmail(account.getEmail());
|
||||
|
||||
// Assert
|
||||
assertThat(updatedAccount.isEmailVerified()).isTrue();
|
||||
}),
|
||||
DynamicTest.dynamicTest("이메일이나 토큰의 값이 null인 경우 예외를 리턴한다.",
|
||||
() -> {
|
||||
//Arrange
|
||||
var command = new ConfirmRegisterAccountCommand(null, null);
|
||||
|
||||
// Act & Assert
|
||||
assertThatExceptionOfType(NullPointerException.class)
|
||||
.isThrownBy(() -> confirmRegisterAccountProcessor.registerConfirm(command));
|
||||
}),
|
||||
DynamicTest.dynamicTest("이메일 검증에 실패하여 예외를 리턴한다.",
|
||||
() -> {
|
||||
//Arrange
|
||||
var command = new ConfirmRegisterAccountCommand(account.getEmailCheckToken(),
|
||||
"HiIamNotExistEmail@naver.com");
|
||||
|
||||
// Act & Assert
|
||||
assertThatExceptionOfType(IllegalStateException.class)
|
||||
.isThrownBy(() -> confirmRegisterAccountProcessor.registerConfirm(command));
|
||||
}),
|
||||
DynamicTest.dynamicTest("토큰 검증에 실패하여 예외를 리턴한다.",
|
||||
() -> {
|
||||
//Arrange
|
||||
var command = new ConfirmRegisterAccountCommand("안녕난가짜토큰이라고해",
|
||||
account.getEmail());
|
||||
|
||||
// Act & Assert
|
||||
assertThatExceptionOfType(IllegalStateException.class)
|
||||
.isThrownBy(() -> confirmRegisterAccountProcessor.registerConfirm(command));
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.yam.app.account.domain;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public final class FakeAccountRepository implements AccountRepository, AccountReader {
|
||||
|
||||
private final Map<Long, Account> data = new ConcurrentHashMap<>();
|
||||
private final AtomicLong idGenerator = new AtomicLong();
|
||||
|
||||
@Override
|
||||
public Account findByEmail(String email) {
|
||||
return data.values().stream()
|
||||
.filter(account -> email.equals(account.getEmail()))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsByEmail(String email) {
|
||||
return data.values().stream()
|
||||
.anyMatch(account -> account.getEmail().equals(email));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account update(Account entity) {
|
||||
return data.putIfAbsent(entity.getId(), entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsByNickname(String nickname) {
|
||||
return data.values().stream()
|
||||
.anyMatch(account -> account.getNickname().equals(nickname));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account save(Account entity) {
|
||||
entity.setId(idGenerator.incrementAndGet());
|
||||
data.put(entity.getId(), entity);
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,6 @@ import com.yam.app.account.application.RegisterAccountCommand;
|
||||
import com.yam.app.account.domain.PasswordEncrypterTest.PasswordEncrypterStub;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
@@ -20,7 +17,7 @@ class RegisterAccountProcessorTest {
|
||||
@TestFactory
|
||||
@DisplayName("회원가입 시나리오")
|
||||
Collection<DynamicTest> register_account_scenarios() {
|
||||
var repository = new AccountRepositoryStub();
|
||||
var repository = new FakeAccountRepository();
|
||||
var processor = new RegisterAccountProcessor(repository, new PasswordEncrypterStub());
|
||||
return Arrays.asList(
|
||||
DynamicTest.dynamicTest("회원가입에 성공한다.", () -> {
|
||||
@@ -51,31 +48,4 @@ class RegisterAccountProcessorTest {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private static class AccountRepositoryStub implements AccountRepository {
|
||||
|
||||
private final Map<Long, Account> data = new ConcurrentHashMap<>();
|
||||
private final AtomicLong idGenerator = new AtomicLong();
|
||||
|
||||
@Override
|
||||
public boolean existsByEmail(String email) {
|
||||
return data.values().stream()
|
||||
.anyMatch(account -> account.getEmail().equals(email));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsByNickname(String nickname) {
|
||||
return data.values().stream()
|
||||
.anyMatch(account -> account.getNickname().equals(nickname));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account save(Account entity) {
|
||||
if (entity.getId() == null) {
|
||||
entity.setId(idGenerator.incrementAndGet());
|
||||
data.put(entity.getId(), entity);
|
||||
}
|
||||
return data.putIfAbsent(entity.getId(), entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.yam.app.account.domain;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@DisplayName("토큰 검증 도메인 테스트")
|
||||
class TokenVerifierTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("입력된 이메일과 토큰값을 예외를 발생시키지 않고 올바르게 검증하는지 테스트한다.")
|
||||
void verify_email_and_token_correctly() throws Exception {
|
||||
//Arrange
|
||||
var accountRepository = new FakeAccountRepository();
|
||||
var tokenVerifier = new TokenVerifier(accountRepository);
|
||||
var account = accountRepository.save(
|
||||
Account.of("jijiwon@gmail.com", "jiwon", "password!"));
|
||||
|
||||
//Act & Assert
|
||||
tokenVerifier.verify(account.getEmailCheckToken(), account.getEmail());
|
||||
}
|
||||
}
|
||||
@@ -53,4 +53,16 @@ class MybatisAccountRepositoryTest {
|
||||
|
||||
assertThat(account.getId()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Account 객체 갱신 테스트")
|
||||
void update() {
|
||||
Account account = accountRepository.save(
|
||||
Account.of("jiwon22@gmail.com", "jiwon2", "password!"));
|
||||
|
||||
account.completeRegister();
|
||||
Account updatedAccount = accountRepository.update(account);
|
||||
|
||||
assertThat(updatedAccount.isEmailVerified()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
package com.yam.app.account.presentation;
|
||||
|
||||
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 org.javaunit.autoparams.AutoSource;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.NullAndEmptySource;
|
||||
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.test.web.servlet.MockMvc;
|
||||
|
||||
@DisplayName("Account Command HTTP API")
|
||||
@WebMvcTest(AccountCommandApi.class)
|
||||
class AccountCommandApiTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
@MockBean
|
||||
private AccountFacade accountFacade;
|
||||
|
||||
@Nested
|
||||
@DisplayName("이메일 검증 HTTP API")
|
||||
class RegisterConfirmApi {
|
||||
|
||||
@ParameterizedTest
|
||||
@NullAndEmptySource
|
||||
@DisplayName("HTTP 파라메타가 비었거나 null인 검증요청을 보낸 경우 400 HTTP Code 리턴한다.")
|
||||
void http_param_is_empty_or_null(String args) throws Exception {
|
||||
// Act
|
||||
var request = new ConfirmRegisterAccountRequest();
|
||||
request.setToken(args);
|
||||
request.setEmail(args);
|
||||
doThrow(IllegalStateException.class).when(accountFacade).registerConfirm(request);
|
||||
|
||||
final var actions = mockMvc.perform(get("/api/accounts/authorize")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.param("token", args)
|
||||
.param("email", args)
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("HTTP 파라메타가 유효하지 않은 값으로 검증요청을 보낸 경우 400 HTTP Code 리턴한다.")
|
||||
void http_param_is_not_valid() throws Exception {
|
||||
// Act
|
||||
var request = new ConfirmRegisterAccountRequest();
|
||||
request.setToken("QWEIUHQWDU");
|
||||
request.setEmail("QWEIOWQJE@naver.com");
|
||||
doThrow(IllegalStateException.class).when(accountFacade).registerConfirm(request);
|
||||
|
||||
final var actions = mockMvc.perform(get("/api/accounts/authorize")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.param("token", "QWEIUHQWDU")
|
||||
.param("email", "QWEIOWQJE@naver.com")
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("토큰과 이메일 정보로 검증요청을 보낸 경우 303 HTTP Code 리턴한다.")
|
||||
void valid_success() throws Exception {
|
||||
// Act
|
||||
var request = new ConfirmRegisterAccountRequest();
|
||||
request.setToken("emailTOken");
|
||||
request.setEmail("jiwonDev@gmail.com");
|
||||
|
||||
final var actions = mockMvc.perform(get("/api/accounts/authorize")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.param("token", "emailTOken")
|
||||
.param("email", "jiwonDev@gmail.com")
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isSeeOther());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("회원가입 등록 HTTP API")
|
||||
class RegisterApi {
|
||||
|
||||
@Test
|
||||
@DisplayName("회원가입에 적절한 파라미터가 입력되고 회원가입이 성공한다.")
|
||||
void register_success() throws Exception {
|
||||
// Arrange
|
||||
var request = new RegisterAccountRequest();
|
||||
request.setEmail("msolo021015@gmail.com");
|
||||
request.setNickname("rebwon");
|
||||
request.setPassword("password!");
|
||||
|
||||
// Act
|
||||
when(accountFacade.register(request)).thenReturn(
|
||||
new AccountResponse(1L, "msolo021015@gmail.com", "rebwon"));
|
||||
|
||||
final var actions = mockMvc.perform(post("/api/accounts")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").isNumber())
|
||||
.andExpect(jsonPath("$.email").isString())
|
||||
.andExpect(jsonPath("$.nickname").isString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Accept와 Content-Type을 지정하지 않아, HttpMediaTypeNotSupportedException 발생.")
|
||||
void register_account_api_not_use_accept_header_and_content_type() throws Exception {
|
||||
// Arrange
|
||||
var request = new RegisterAccountRequest();
|
||||
request.setEmail("msolo021015@gmail.com");
|
||||
request.setNickname("rebwon");
|
||||
request.setPassword("password!");
|
||||
|
||||
// Act
|
||||
final var actions = mockMvc.perform(post("/api/accounts")
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andExpect(status().isUnsupportedMediaType());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@NullAndEmptySource
|
||||
@DisplayName("HTTP 입력이 Null이거나 Emtpy인 경우 검증에 실패하여 에러를 응답한다.")
|
||||
void register_http_parameter_is_null_and_empty(String arg) throws Exception {
|
||||
// Arrange
|
||||
var request = new RegisterAccountRequest();
|
||||
request.setEmail(arg);
|
||||
request.setNickname(arg);
|
||||
request.setPassword(arg);
|
||||
|
||||
// Act
|
||||
final var actions = mockMvc.perform(post("/api/accounts")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@AutoSource
|
||||
@DisplayName("HTTP 입력 파라미터가 이메일, 비밀번호 검증에 실패하여 에러를 응답한다.")
|
||||
void register_http_parameter_is_invalid_email_and_password(String arg) throws Exception {
|
||||
// Arrange
|
||||
var request = new RegisterAccountRequest();
|
||||
request.setEmail(arg);
|
||||
request.setNickname(arg);
|
||||
request.setPassword(arg);
|
||||
|
||||
// Act
|
||||
final var actions = mockMvc.perform(post("/api/accounts")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package com.yam.app.account.presentation;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
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 org.javaunit.autoparams.AutoSource;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.NullAndEmptySource;
|
||||
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.test.web.servlet.MockMvc;
|
||||
|
||||
@DisplayName("회원가입 등록 HTTP API")
|
||||
@WebMvcTest(RegisterAccountApi.class)
|
||||
class RegisterAccountApiTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
@MockBean
|
||||
private AccountFacade accountFacade;
|
||||
|
||||
@Test
|
||||
@DisplayName("회원가입에 적절한 파라미터가 입력되고 회원가입이 성공한다.")
|
||||
void register_success() throws Exception {
|
||||
// Arrange
|
||||
var request = new RegisterAccountRequest();
|
||||
request.setEmail("msolo021015@gmail.com");
|
||||
request.setNickname("rebwon");
|
||||
request.setPassword("password!");
|
||||
|
||||
// Act
|
||||
when(accountFacade.register(request)).thenReturn(
|
||||
new AccountResponse(1L, "msolo021015@gmail.com", "rebwon"));
|
||||
|
||||
final var actions = mockMvc.perform(post("/api/accounts")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").isNumber())
|
||||
.andExpect(jsonPath("$.email").isString())
|
||||
.andExpect(jsonPath("$.nickname").isString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Accept 헤더와 Content-Type을 지정해주지 않았으므로, HttpMediaTypeNotSupportedException이 발생한다.")
|
||||
void register_account_api_not_use_accept_header_and_content_type() throws Exception {
|
||||
// Arrange
|
||||
var request = new RegisterAccountRequest();
|
||||
request.setEmail("msolo021015@gmail.com");
|
||||
request.setNickname("rebwon");
|
||||
request.setPassword("password!");
|
||||
|
||||
// Act
|
||||
final var actions = mockMvc.perform(post("/api/accounts")
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andExpect(status().isUnsupportedMediaType());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@NullAndEmptySource
|
||||
@DisplayName("HTTP 입력이 Null이거나 Emtpy인 경우 검증에 실패하여 에러를 응답한다.")
|
||||
void register_http_parameter_is_null_and_empty(String arg) throws Exception {
|
||||
// Arrange
|
||||
var request = new RegisterAccountRequest();
|
||||
request.setEmail(arg);
|
||||
request.setNickname(arg);
|
||||
request.setPassword(arg);
|
||||
|
||||
// Act
|
||||
final var actions = mockMvc.perform(post("/api/accounts")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@AutoSource
|
||||
@DisplayName("HTTP 입력 파라미터가 이메일, 비밀번호 검증에 실패하여 에러를 응답한다.")
|
||||
void register_http_parameter_is_invalid_email_and_password(String arg) throws Exception {
|
||||
// Arrange
|
||||
var request = new RegisterAccountRequest();
|
||||
request.setEmail(arg);
|
||||
request.setNickname(arg);
|
||||
request.setPassword(arg);
|
||||
|
||||
// Act
|
||||
final var actions = mockMvc.perform(post("/api/accounts")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
);
|
||||
|
||||
// Assert
|
||||
actions
|
||||
.andDo(print())
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user