Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat : 이메일 인증 기능 구현 #57

Merged
merged 68 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
75b649a
feat : 이메일과 검증 코드를 저장하는 EmailVerificationCode 객체 구현 (#28)
binary-ho Feb 11, 2024
f1476e3
feat : 검증 코드 생성기 구현 (#28)
binary-ho Feb 11, 2024
3d88172
feat : 인증 메일 내용을 작성하는 VerificationMailContentWriter 구현 (#28)
binary-ho Feb 11, 2024
0ab8ff8
feat : EmailVerificationCodeRepository 구현 (#28)
binary-ho Feb 11, 2024
70a7737
feat : 다양한 예외 상황에 대한 ErrorCode 구현 (#28)
binary-ho Feb 11, 2024
59b6610
refactor : 메일 내용을 쓸 때, 시간을 Duration 객체로 전달하도록 변경 (#28)
binary-ho Feb 11, 2024
e4570e1
feat : 홍익대학교 지메일 형식을 검증하는 HongikUnivEmailValidator 구현 (#28)
binary-ho Feb 11, 2024
5a2a58f
refactor : EmailVerificationCode의 time to live 값을 외부에서 주입 받도록 변경 (#28)
binary-ho Feb 11, 2024
686f0d8
feat : mail starter 의존성 추가 (#28)
binary-ho Feb 11, 2024
7361094
feat : Java Mail Sender Config 세팅 (#28)
binary-ho Feb 11, 2024
3f94458
feat : 메일을 전송하는 Java Mail Sender 구현 (#28)
binary-ho Feb 11, 2024
bc80e49
feat : 메일을 전송하고, 인증 코드를 확인하는 UnivEmailVerificationService 구현 (#28)
binary-ho Feb 11, 2024
fc4c5f9
chore : 불필요한 주석 제거 (#28)
binary-ho Feb 11, 2024
94b67dc
chore : 재학생 인증 메일 문구 수정
binary-ho Feb 12, 2024
ce7e858
refactor : CustomException에 Error Message 지정 생성자 추가
binary-ho Feb 12, 2024
bae67e8
refactor : Messaging Exception 상황에 따라 다른 CustomException 적용
binary-ho Feb 12, 2024
fbba831
chore : merge with develop
binary-ho Feb 12, 2024
983477e
refactor : JavaMailSender를 Service에서 Component로 변경
binary-ho Feb 13, 2024
4acedc7
refactor : 홍익대학교 이메일 Regex를 RegexConstant로 이동
binary-ho Feb 13, 2024
cccc127
refactor : 재학생 인증 메일 constant 값을 클래스에 분리
binary-ho Feb 13, 2024
612d335
refactor : 이메일 제목을 외부에서 주입 받도록 변경
binary-ho Feb 13, 2024
98ddd9e
chore : GdscMailConstant 클래스의 이름을 EmailConstant로 변경
binary-ho Feb 13, 2024
d86de05
chore : spotless java apply
binary-ho Feb 13, 2024
039e782
chore : spotless java apply
binary-ho Feb 13, 2024
2146989
refactor : Email Property들을 클래스로 분리
binary-ho Feb 13, 2024
eb2eada
chore: Java Mail Sender Bean 이름 변경
binary-ho Feb 13, 2024
54a1c91
refactor: JavaMailSenderConfig에 EmailProperty 적용
binary-ho Feb 13, 2024
2f43161
chore: HongikUnivEmailValidatorTest에 test Profile 추가
binary-ho Feb 13, 2024
a52fd1e
refactor : 인증 메일 관련 상수를 Constant 클래스로 분리
binary-ho Feb 13, 2024
4ee13e6
refactor : 재학생 인증 메일에 인증 링크를 첨부하도록 변경
binary-ho Feb 13, 2024
2f88bf1
refactor : Email 관련 Property 값들을 Property Class로 관리
binary-ho Feb 14, 2024
28ec39a
refactor : email 설정 yml 파일 분리
binary-ho Feb 14, 2024
85cd859
refactor: verificationLinkUtil의 getLink 메서드의 이름을 createLink로 변경
binary-ho Feb 14, 2024
d2eacbf
refactor: 인증 방식 변경에 따라 EmailVerificationCode 의 key와 value를 서로 변경
binary-ho Feb 14, 2024
69368a5
feat: 인증 token을 통해 유저 Univ Email 인증 상태를 변경하는 메서드 구현
binary-ho Feb 14, 2024
3f0a076
refactor: UnivMailVerificationSevice를 Send 부와 인증 부로 분리
binary-ho Feb 14, 2024
683fb9f
refactor: Mail 및 재학생 인증 관련 클래스들의 패키지를 목적에 맞게 정리
binary-ho Feb 15, 2024
8f22a34
chore: spotless java apply
binary-ho Feb 15, 2024
c051e9a
refactor: MemberUtil의 getCurrentId로 Member를 찾는 부분 getCurrentMemeber로 대체
binary-ho Feb 15, 2024
cd2e358
refactor: integration 패키지의 이름을 email로 변경
binary-ho Feb 15, 2024
d733e00
feat: 학교 인증 메일 API 구현
binary-ho Feb 15, 2024
1400caa
chore: SenderService의 이름을 SendService로 변경
binary-ho Feb 15, 2024
9e65bbc
refactor: 유저 email을 request body를 통해 전달 받도록 변경
binary-ho Feb 15, 2024
993b055
refactor: 레디스에 재학생 인증 정보를 저장할 때 memberId도 함께 저장하도록 변경
binary-ho Feb 15, 2024
adcc718
refactor: VerificationCodeAndUnivEmail의 이름을 UnivEmailVerification으로 변경
binary-ho Feb 15, 2024
1737e57
refactor: email verify 과정에서 Member 조회시 id를 사용하도록 변경
binary-ho Feb 15, 2024
3a8f639
refactor: 재학생 인증 완료시 Member의 Univ Email을 업데이트하도록 변경
binary-ho Feb 15, 2024
6ebde20
refactor: 이미 재학생 인증을 마친 이메일로 인증이 요청되는 경우 예외를 발생시킨다.
binary-ho Feb 15, 2024
4f5033c
chore: email이 univEmail의 맥락으로 쓰인 부분의 이름 변경
binary-ho Feb 15, 2024
bc7137e
chore: univMail이라고 명명한 클래스의 이름을 univEmail로 변경
binary-ho Feb 15, 2024
53aa8a6
chore: univMail이라고 명명한 클래스의 이름을 univEmail로 변경
binary-ho Feb 15, 2024
6224339
refactor: commit naming suggestion
binary-ho Feb 16, 2024
a28566a
refactor: validateUnivEmailNotVerified 로직 잘못된 부분 수정
binary-ho Feb 16, 2024
6796d1b
refactor: UnivEmailVerificationRepository 불필요한 메서드 제거
binary-ho Feb 16, 2024
5c7cb87
refactor: UnivEmailVerificationLinkSendService 파라미터 정정
binary-ho Feb 16, 2024
647a340
refactor: 과한 객체 분리를 제거하기 위한 MailContentWriter 제거
binary-ho Feb 17, 2024
2400cd9
refactor: UnivEmailVerificationService의 Transactional 어노테이션사용을 컨벤션에 맞…
binary-ho Feb 17, 2024
6f3d127
refactor: ErrorCode에서 Univ Email이라는 표현을 맥락에 맞게 변경
binary-ho Feb 17, 2024
ab3fde7
refactor: JavaMailSender의 이름을 JavaEmailSender로 변경
binary-ho Feb 17, 2024
31b3f49
refactor: JavaMailSender 빈의 이름을 빈 네이밍 컨벤션에 맞게 변경
binary-ho Feb 17, 2024
1c5c19f
refactor: Email Property를 inner record로 변경
binary-ho Feb 18, 2024
33b1d3a
refactor: UnivEmailVerificationLinkSendService에서 사용하지 않는 메서드 제거
binary-ho Feb 18, 2024
619f34f
refactor: validateUnivEmailNotVerified에서 ifPresent 제거
binary-ho Feb 20, 2024
1f3d01c
refactor: 온보딩 이메일 인증 컨트롤러 통합
binary-ho Feb 23, 2024
230dc9b
refactor: custom record Pair 삭제
binary-ho Feb 23, 2024
b711279
refactor: email 패키지 유틸 클래스 global로 이동
binary-ho Feb 23, 2024
f0e81be
refactor: merge with develop
binary-ho Feb 24, 2024
ad7ee96
refactor: merge with develop
binary-ho Feb 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ dependencies {

// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'

// Mail
implementation 'org.springframework.boot:spring-boot-starter-mail'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.gdschongik.gdsc.domain.integration;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.TimeToLive;

@Getter
@AllArgsConstructor
@RedisHash(value = "emailVerificationCode")
public class EmailVerificationCode {

@Id
private String email;

private String verificationCode;

@TimeToLive
private long timeToLiveInSeconds;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.gdschongik.gdsc.domain.integration;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

email 패키지로 이름을 지정하지 않은 이유가 있으신가요?


import org.springframework.data.repository.CrudRepository;

public interface EmailVerificationCodeRepository
extends CrudRepository<EmailVerificationCode, String> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.gdschongik.gdsc.domain.integration;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

유틸리티로 빼는 게 맞을 것 같습니다~


import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.exception.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class HongikUnivEmailValidator {


private static final String HONGIK_UNIV_MAIL_DOMAIN = "@g.hongik.ac.kr";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constant로 빼는 게 좋을 것 같습니다


private static final String EMAIL_REGEX = "^[^\\W&=_'-+,<>]+(\\.[^\\W&=_'-+,<>]+)*@g\\.hongik\\.ac\\.kr$";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 정규표현식을 모두 RegexConstant에서 관리하고 있는데 이 경우에는 RegexConstant에서 관리하지 않는 특별한 이유가 있을까요?

Copy link
Member Author

@binary-ho binary-ho Feb 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 필요해서 가지고 있었어요!
패키지 정리하면서 RegexConstant로 옮기겠습니다! ㅎㅎ


public void validate(String email) {
if (!email.contains(HONGIK_UNIV_MAIL_DOMAIN)) {
throw new CustomException(ErrorCode.UNIV_EMAIL_DOMAIN_MISMATCH);
}

if (!email.matches(EMAIL_REGEX)) {
throw new CustomException(ErrorCode.UNIV_EMAIL_FORMAT_MISMATCH);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.gdschongik.gdsc.domain.integration;

import jakarta.mail.Message.RecipientType;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import java.io.UnsupportedEncodingException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

서비스 계층이라고 보는 것이 맞나? 라는 의문이 드네요.
도메인이랑 상호작용하는 부분이 없으니 유틸리티라고 보는 게 맞지 않을까 싶습니다~

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그게 맞는 것 같아요! 말씀 감사합니다 ㅎㅎ

@RequiredArgsConstructor
public class JavaMailSender implements MailSender {

private static final String SENDER_PERSONAL = "GDSC Hongik 운영팀";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constant 클래스로 분리하면 더 좋을 것 같습니다~

private static final String SENDER_ADDRESS = "[email protected]";
private static final String MESSAGE_SUBJECT = "GDSC Hongik 이메일 인증 코드입니다.";

private final org.springframework.mail.javamail.JavaMailSender javaMailSender;

@Override
public void send(String recipient, String content) {
try {
MimeMessage message = writeMimeMessage(recipient, content);
javaMailSender.send(message);
} catch (MessagingException e) {
throw new RuntimeException(e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

커스텀 예외로 변경해주시면 좋을 것 같아요~

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 감사합니다!!

}
}

private MimeMessage writeMimeMessage(String recipient, String content)
throws MessagingException {
MimeMessage message = javaMailSender.createMimeMessage();

message.addRecipients(RecipientType.TO, recipient);
message.setSubject(MESSAGE_SUBJECT);
message.setText(content, "utf-8", "html");
message.setFrom(getInternetAddress());
return message;
}

private InternetAddress getInternetAddress() {
try {
return new InternetAddress(SENDER_ADDRESS, SENDER_PERSONAL);
} catch (UnsupportedEncodingException unsupportedEncodingException) {
return new InternetAddress();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.gdschongik.gdsc.domain.integration;

import java.util.Properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;

@Configuration
public class JavaMailSenderConfig {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config 패키지로 관리해줘야 할 것 같습니다.


@Value("${gmail.id}")
private String id;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MailProperty 로 분리해서 사용하면 좋을 것 같아요~


@Value("${gmail.password}")
private String password;

@Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setHost("smtp.gmail.com");
javaMailSender.setPort(456);
javaMailSender.setUsername(id);
javaMailSender.setPassword(password);
javaMailSender.setJavaMailProperties(getMailProperties());
javaMailSender.setDefaultEncoding("UTF-8");
return javaMailSender;
}

private Properties getMailProperties() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yml 파일에 작성하지 않고 Properties 객체에 하드코딩하신 이유가 궁금합니다~
EmailProperty와 통합이 불가능한가요?

Properties properties = new Properties();
properties.put("mail.smtp.socketFactory.port", 456);
properties.put("mail.smtp.auth", true);
properties.put("mail.smtp.starttls.enable", true);
properties.put("mail.smtp.starttls.required", true);
properties.put("mail.smtp.socketFactory.fallback", false);
properties.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
return properties;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.gdschongik.gdsc.domain.integration;

public interface MailSender {

void send(String recipient, String content);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메일 전송 성공/실패를 전달받을 수 있는 방법은 존재하지 않나요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인해보겠습니다!

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.gdschongik.gdsc.domain.integration;

import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.exception.ErrorCode;
import java.time.Duration;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class UnivEmailVerificationService {

private final HongikUnivEmailValidator hongikUnivEmailValidator;
private final VerificationCodeGenerator verificationCodeGenerator;
private final VerificationMailContentWriter verificationMailContentWriter;

private final MailSender mailSender;
private final EmailVerificationCodeRepository emailVerificationCodeRepository;

public static final Duration VERIFICATION_CODE_TIME_TO_LIVE = Duration.ofMinutes(10);

public void sendEmailVerificationMail(String email) {
hongikUnivEmailValidator.validate(email);

String verificationCode = verificationCodeGenerator.generate();
sendMail(email, verificationCode);

saveVerificationCode(email, verificationCode);
}

private void sendMail(String email, String verificationCode) {
String mailContent = verificationMailContentWriter
.writeContentWithVerificationCode(verificationCode, VERIFICATION_CODE_TIME_TO_LIVE);

mailSender.send(email, mailContent);
}

private void saveVerificationCode(String email, String verificationCode) {
EmailVerificationCode emailVerificationCode = new EmailVerificationCode(
email, verificationCode, VERIFICATION_CODE_TIME_TO_LIVE.toSeconds()
);

emailVerificationCodeRepository.save(emailVerificationCode);
}

public void validateCodeMatch(String email, String userInputCode) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용처가 없는 것 같습니다.

String verificationCode = getVerificationCodeByEmail(email);

if (!Objects.equals(verificationCode, userInputCode)) {
throw new CustomException(ErrorCode.UNIV_EMAIL_VERIFICATION_CODE_NOT_MATCH);
}
}

private String getVerificationCodeByEmail(String email) {
return emailVerificationCodeRepository.findById(email)
.orElseThrow(() -> new CustomException(ErrorCode.VERIFICATION_CODE_NOT_FOUND))
.getVerificationCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.gdschongik.gdsc.domain.integration;

import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Component;

@Component
public class VerificationCodeGenerator {

private static final int VERIFICATION_CODE_LENGTH = 16;
private static final char RANGE_START_CHAR = '0';
private static final char RANGE_END_CHAR = 'z';

public String generate() {
return RandomStringUtils.random(VERIFICATION_CODE_LENGTH, RANGE_START_CHAR, RANGE_END_CHAR, true, true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.gdschongik.gdsc.domain.integration;

import java.time.Duration;
import org.springframework.stereotype.Component;

@Component
public class VerificationMailContentWriter {

private static final String NOTIFICATION_MESSAGE = "<div style='margin:20px;'>"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인증 코드 입력방식 말고, 인증 URL 클릭하는 방식으로 처리하는게 어떨까 싶습니다~
stoplight 명세 확인해보시면 좋을 것 같아요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

죄송합니다 확인해보겠습니다!

+ "<h1> 안녕하세요 GDSC Hongik 이메일 인증 메일입니다. </h1> <br>"
+ "<h3> 아래의 인증 코드를 %d분 안에 입력해주세요. </h3> <br>"
+ "<h3> 감사힙니다. </h3> <br>"
+ "CODE : <strong>";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. "이메일 인증 메일입니다." 대신 "재학생 인증 메일입니다." 어떤가요?

  2. '감사힙니다' 오타있습니다!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

놓쳐서 죄송합니다!
꼼꼼하게 봐주셔서 감사합니다!


public String writeContentWithVerificationCode(String verificationCode, Duration codeAliveTime) {
return String.format(NOTIFICATION_MESSAGE, codeAliveTime.toMinutes())
+ verificationCode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,16 @@ public enum ErrorCode {

// Member
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다."),
MEMBER_DELETED(HttpStatus.CONFLICT, "탈퇴한 회원입니다.");
MEMBER_DELETED(HttpStatus.CONFLICT, "탈퇴한 회원입니다."),

// Univ Email Verification
UNIV_EMAIL_DUPLICATED(HttpStatus.CONFLICT, "이미 가입된 Univ Email 입니다."),
UNIV_EMAIL_FORMAT_MISMATCH(HttpStatus.BAD_REQUEST, "형식에 맞지 않는 Univ Email 입니다."),
UNIV_EMAIL_DOMAIN_MISMATCH(HttpStatus.BAD_REQUEST, "Univ Email의 도메인이 맞지 않습니다."),

VERIFICATION_CODE_NOT_FOUND(HttpStatus.NOT_FOUND, "Univ Email 인증 코드가 존재하지 않습니다."),
UNIV_EMAIL_VERIFICATION_CODE_NOT_MATCH(HttpStatus.BAD_REQUEST, "Email 인증 번호가 불일치합니다."),
;

private final HttpStatus status;
private final String message;
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ spring:
logging:
level:
com.gdschongik.gdsc.domain.*.api.*: debug

gmail:
id: ${GMAIL_ID}
password: ${GMAIL_PASSWORD}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redis, storage처럼 이메일은 yaml 파일을 분리하지 않는게 일반적인가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일반적이라기 보다는 상황에 따라 다를거 같아요 ㅎㅎ
한번 고민해볼게요 감사합니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.gdschongik.gdsc.domain.integration;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.exception.ErrorCode;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class HongikUnivEmailValidatorTest {

@Autowired
private HongikUnivEmailValidator hongikUnivEmailValidator;

@Test
@DisplayName("'g.hongik.ac.kr' 도메인을 가진 이메일을 검증할 수 있다.")
public void validateEmailDomainTest() {
String hongikDomainEmail = "[email protected]";

assertThatCode(
() -> hongikUnivEmailValidator.validate(hongikDomainEmail)
).doesNotThrowAnyException();
}

@ParameterizedTest
@ValueSource(strings = {"[email protected]", "[email protected]", "[email protected]",
"[email protected]"})
@DisplayName("'g.hongik.ac.kr'가 아닌 도메인을 가진 이메일을 입력하면 예외를 발생시킨다.")
public void validateEmailDomainMismatchTest(String email) {
assertThatThrownBy(() -> hongikUnivEmailValidator.validate(email))
.isInstanceOf(CustomException.class)
.hasMessage(ErrorCode.UNIV_EMAIL_DOMAIN_MISMATCH.getMessage());
}

@Test
@DisplayName("Email의 '@' 앞 부분에는 연속되지 않은 점이 포함될 수 있다.")
public void validateEmailFormatWithDotsTest() {
String email = "[email protected]";

assertThatCode(
() -> hongikUnivEmailValidator.validate(email)
).doesNotThrowAnyException();
}

@ParameterizedTest
@ValueSource(strings = {"te&[email protected]", "[email protected]", "[email protected]",
"te'[email protected]", "[email protected]", "[email protected]",
"te,[email protected]", "te<[email protected]", "te>[email protected]"})
@DisplayName("Email의 '@' 앞 부분에 '&', '=', '_', ''', '-', '+', ',', '<', '>'가 포함되는 경우 예외를 발생시킨다.")
public void validateEmailFormatMismatchTest(String email) {
assertThatThrownBy(() -> hongikUnivEmailValidator.validate(email))
.isInstanceOf(CustomException.class)
.hasMessage(ErrorCode.UNIV_EMAIL_FORMAT_MISMATCH.getMessage());
}

@Test
@DisplayName("Email의 '@' 앞 부분에 '.'이 2개 연속 오는 경우 예외를 발생시킨다.")
public void validateEmailFormatMismatchWithDotsTest() {
String email = "[email protected]";
assertThatThrownBy(() -> hongikUnivEmailValidator.validate(email))
.isInstanceOf(CustomException.class)
.hasMessage(ErrorCode.UNIV_EMAIL_FORMAT_MISMATCH.getMessage());
}
}