Skip to content

Commit

Permalink
feat(account) email
Browse files Browse the repository at this point in the history
test: 이메일 등록 API 통합 테스트 추가
test: 이메일 인증번호 발송 API 통합 테스트 추가
test: 이메일 변경 API 통합 테스트 추가
test: 이메일 해제 API 통합 테스트 추가
docs: Email
  • Loading branch information
habinkim committed Jun 1, 2024
1 parent 2f068d2 commit 3681d51
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 25 deletions.
54 changes: 42 additions & 12 deletions src/docs/asciidoc/account-api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ operation::logout-success[snippets='http-request,http-response,request-fields,re

operation::refresh-success[snippets='http-request,http-response,request-fields,response-fields']

=== 이메일 중복확인 API

==== 성공

operation::check-email-duplicate-success[snippets='http-request,http-response,query-parameters,response-fields']

==== 실패

operation::check-email-duplicate-failed[snippets='http-request,http-response,query-parameters,response-fields']
Expand All @@ -45,26 +39,62 @@ operation::check-username-duplicate-success[snippets='http-request,http-response

operation::check-username-duplicate-success-failed[snippets='http-request,http-response']

=== 비밀번호 찾기 OTP 전송 API

==== 성공

operation::send-forgot-password-otp-success[snippets='http-request,http-response,request-fields,response-fields']

=== 비밀번호 재설정 API

==== 성공

operation::set-new-password-success[snippets='http-request,http-response,request-fields,response-fields']

=== 닉네임 변경 API

==== 성공

operation::update-nickname-success[snippets='http-request,http-response,request-fields,response-fields'

== Email API

=== 이메일 중복확인 API

==== 성공

operation::check-email-duplicate-success[snippets='http-request,http-response,query-parameters,response-fields']

==== 실패

operation::check-email-duplicate-failed[snippets='http-request,http-response,query-parameters,response-fields']

=== 이메일 인증여부 확인 API

==== 성공

operation::check-email-verified-success[snippets='http-request,http-response,response-fields']

=== 비밀번호 찾기 OTP 전송 API
=== 이메일 등록 API

==== 성공

operation::send-forgot-password-otp-success[snippets='http-request,http-response,request-fields,response-fields']
operation::register-email-success[snippets='http-request,http-response,request-fields,response-fields']

=== 비밀번호 재설정 API
=== 이메일 인증번호 발송 API

==== 성공

operation::set-new-password-success[snippets='http-request,http-response,request-fields,response-fields']
operation::verify-email-success[snippets='http-request,http-response,response-fields']

=== 닉네임 변경 API
=== 이메일 변경 API

==== 성공

operation::change-email-success[snippets='http-request,http-response,request-fields,response-fields']

=== 이메일 해제 API

==== 성공

operation::update-nickname-success[snippets='http-request,http-response,request-fields,response-fields']
operation::unlink-email-success[snippets='http-request,http-response,response-fields']
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ public class AccountJpaEntity extends BaseEntity implements UserDetails {
@Comment("비밀번호")
private String password;

@NotNull
@Column(name = "email", length = 2000)
@Comment("이메일")
private String email;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
package com.tune_fun.v1.account.adapter.input.rest;

import com.icegreen.greenmail.util.GreenMail;
import com.tune_fun.v1.account.adapter.output.persistence.AccountJpaEntity;
import com.tune_fun.v1.account.application.port.input.command.AccountCommands;
import com.tune_fun.v1.account.application.port.output.LoadAccountPort;
import com.tune_fun.v1.base.ControllerBaseTest;
import com.tune_fun.v1.common.config.Uris;
import com.tune_fun.v1.common.response.MessageCode;
import com.tune_fun.v1.common.util.StringUtil;
import com.tune_fun.v1.dummy.DummyService;
import com.tune_fun.v1.otp.adapter.output.persistence.OtpType;
import com.tune_fun.v1.otp.application.port.output.LoadOtpPort;
import com.tune_fun.v1.otp.application.port.output.VerifyOtpPort;
import com.tune_fun.v1.otp.domain.behavior.LoadOtp;
import com.tune_fun.v1.otp.domain.behavior.VerifyOtp;
import com.tune_fun.v1.otp.domain.value.CurrentDecryptedOtp;
import jakarta.mail.internet.MimeMessage;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junitpioneer.jupiter.Issue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.request.ParameterDescriptor;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.transaction.annotation.Transactional;

import static com.epages.restdocs.apispec.ResourceDocumentation.resource;
import static com.epages.restdocs.apispec.ResourceSnippetParameters.builder;
import static com.tune_fun.v1.base.doc.RestDocsConfig.constraint;
import static com.tune_fun.v1.otp.adapter.output.persistence.OtpType.VERIFY_EMAIL;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;

Expand All @@ -30,8 +46,17 @@ class EmailControllerIT extends ControllerBaseTest {
@Autowired
private DummyService dummyService;

private static final ParameterDescriptor REQUEST_DESCRIPTOR = parameterWithName("email").description("이메일")
.attributes(constraint("NOT BLANK"));
@Autowired
private LoadAccountPort loadAccountPort;

@Autowired
private LoadOtpPort loadOtpPort;

@Autowired
private VerifyOtpPort verifyOtpPort;

@Autowired
private GreenMail greenMail;

@Transactional
@Test
Expand All @@ -41,19 +66,22 @@ class EmailControllerIT extends ControllerBaseTest {
void checkEmailDuplicateSuccess() throws Exception {
dummyService.initAccount();

ParameterDescriptor requestDescriptor = parameterWithName("email").description("이메일")
.attributes(constraint("NOT BLANK"));

mockMvc.perform(
get(Uris.CHECK_EMAIL_DUPLICATE)
.param("email", "test")
)
.andExpectAll(baseAssertion(MessageCode.SUCCESS_EMAIL_UNIQUE))
.andDo(
restDocs.document(
queryParameters(REQUEST_DESCRIPTOR),
queryParameters(requestDescriptor),
responseFields(baseResponseFields),
resource(
builder().
description("이메일 중복확인").
queryParameters(REQUEST_DESCRIPTOR).
queryParameters(requestDescriptor).
responseFields(baseResponseFields)
.build()
)
Expand All @@ -70,19 +98,22 @@ void checkEmailDuplicateFailed() throws Exception {
dummyService.initAccount();
AccountJpaEntity account = dummyService.getDefaultAccount();

ParameterDescriptor requestDescriptor = parameterWithName("email").description("이메일")
.attributes(constraint("NOT BLANK"));

mockMvc.perform(
get(Uris.CHECK_EMAIL_DUPLICATE)
.param("email", account.getEmail())
)
.andExpectAll(baseAssertion(MessageCode.USER_POLICY_EMAIL_REGISTERED))
.andDo(
restDocs.document(
queryParameters(REQUEST_DESCRIPTOR),
queryParameters(requestDescriptor),
responseFields(baseResponseFields),
resource(
builder().
description("이메일 중복확인").
queryParameters(REQUEST_DESCRIPTOR).
queryParameters(requestDescriptor).
responseFields(baseResponseFields)
.build()
)
Expand Down Expand Up @@ -125,4 +156,176 @@ void checkEmailVerifiedSuccess() throws Exception {

}

@Transactional
@Test
@Order(4)
@DisplayName("이메일 등록, 성공")
void registerEmailSuccess() throws Exception {
dummyService.initAndLogin();
dummyService.clearEmail();

String accessToken = dummyService.getDefaultAccessToken();

String email = StringUtil.randomAlphabetic(7) + "@" + StringUtil.randomAlphabetic(5) + ".com";
AccountCommands.SaveEmail command = new AccountCommands.SaveEmail(email);

FieldDescriptor requestDescriptor = fieldWithPath("email").description("이메일").attributes(constraint("NOT BLANK"));

mockMvc.perform(
post(Uris.EMAIL_ROOT)
.header(AUTHORIZATION, bearerToken(accessToken))
.content(toJson(command))
.contentType(APPLICATION_JSON_VALUE)
)
.andExpectAll(baseAssertion(MessageCode.SUCCESS))
.andDo(
restDocs.document(
requestHeaders(authorizationHeader),
requestFields(requestDescriptor),
responseFields(baseResponseFields),
resource(
builder().
description("이메일 등록").
requestHeaders(authorizationHeader).
requestFields(requestDescriptor).
responseFields(baseResponseFields)
.build()
)
)
);

loadAccountPort.currentAccountInfo(dummyService.getDefaultAccount().getUsername())
.ifPresentOrElse(
account -> assertEquals(email, account.email()),
() -> Assertions.fail("계정 정보를 찾을 수 없습니다.")
);
}

@Transactional
@Test
@Order(5)
@DisplayName("이메일 인증번호 발송, 성공")
void verifyEmailSuccess() throws Exception {
dummyService.initAndLogin();
AccountJpaEntity defaultAccount = dummyService.getDefaultAccount();
String accessToken = dummyService.getDefaultAccessToken();

greenMail.purgeEmailFromAllMailboxes();

String username = dummyService.getDefaultUsername();

ResultActions resultActions = mockMvc.perform(
post(Uris.VERIFY_EMAIL)
.header(AUTHORIZATION, bearerToken(accessToken))
)
.andExpectAll(baseAssertion(MessageCode.SUCCESS));


greenMail.waitForIncomingEmail(1);

MimeMessage receivedMessage = greenMail.getReceivedMessages()[0];
assertEquals(defaultAccount.getEmail(), receivedMessage.getAllRecipients()[0].toString());
assertEquals("TuneFun - " + defaultAccount.getNickname() + "님의 인증번호입니다.", receivedMessage.getSubject());

LoadOtp loadOtpBehavior = new LoadOtp(username, VERIFY_EMAIL.getLabel());
CurrentDecryptedOtp decryptedOtp = loadOtpPort.loadOtp(loadOtpBehavior);

VerifyOtp verifyOtpBehavior = new VerifyOtp(username, VERIFY_EMAIL.getLabel(), decryptedOtp.token());
assertDoesNotThrow(() -> verifyOtpPort.verifyOtp(verifyOtpBehavior));

resultActions.andDo(
restDocs.document(
requestHeaders(authorizationHeader),
responseFields(baseResponseFields),
resource(
builder().
description("이메일 인증번호 발송").
requestHeaders(authorizationHeader).
responseFields(baseResponseFields)
.build()
)
)
);
}

@Transactional
@Test
@Order(6)
@DisplayName("이메일 변경, 성공")
void changeEmailSuccess() throws Exception {
dummyService.initAndLogin();

String accessToken = dummyService.getDefaultAccessToken();

String email = StringUtil.randomAlphabetic(7) + "@" + StringUtil.randomAlphabetic(5) + ".com";
AccountCommands.SaveEmail command = new AccountCommands.SaveEmail(email);

FieldDescriptor requestDescriptor = fieldWithPath("email").description("이메일").attributes(constraint("NOT BLANK"));

mockMvc.perform(
patch(Uris.EMAIL_ROOT)
.header(AUTHORIZATION, bearerToken(accessToken))
.content(toJson(command))
.contentType(APPLICATION_JSON_VALUE)
)
.andExpectAll(baseAssertion(MessageCode.SUCCESS))
.andDo(
restDocs.document(
requestHeaders(authorizationHeader),
requestFields(requestDescriptor),
responseFields(baseResponseFields),
resource(
builder().
description("이메일 변경").
requestHeaders(authorizationHeader).
requestFields(requestDescriptor).
responseFields(baseResponseFields)
.build()
)
)
);

loadAccountPort.currentAccountInfo(dummyService.getDefaultAccount().getUsername())
.ifPresentOrElse(
account -> assertEquals(email, account.email()),
() -> Assertions.fail("계정 정보를 찾을 수 없습니다.")
);
}

@Transactional
@Test
@Order(7)
@DisplayName("이메일 해제, 성공")
void unlinkEmailSuccess() throws Exception {
dummyService.initAndLogin();

String accessToken = dummyService.getDefaultAccessToken();

mockMvc.perform(
delete(Uris.EMAIL_ROOT)
.header(AUTHORIZATION, bearerToken(accessToken))
.contentType(APPLICATION_JSON_VALUE)
)
.andExpectAll(baseAssertion(MessageCode.SUCCESS))
.andDo(
restDocs.document(
requestHeaders(authorizationHeader),
responseFields(baseResponseFields),
resource(
builder().
description("이메일 해제").
requestHeaders(authorizationHeader).
responseFields(baseResponseFields)
.build()
)
)
);

loadAccountPort.currentAccountInfo(dummyService.getDefaultAccount().getUsername())
.ifPresentOrElse(
account -> Assertions.assertNull(account.email()),
() -> Assertions.fail("계정 정보를 찾을 수 없습니다.")
);
}

}
Loading

0 comments on commit 3681d51

Please sign in to comment.