Skip to content

Commit a9434f7

Browse files
authored
[fix] 메일 발송 성공 시 EMAIL_RECEIVER 테이블의 SENT_AT, SEND_STATUS 컬럼 업데이트 (#103)
* fix : 메일 발송 성공 시 EMAIL_RECEIVER 테이블의 SENT_AT, SEND_STATUS 컬럼 업데이트 * test : 테스트 코드 작성 - emailReceiverRepository.findById에 대한 테스트 - EmailReceiver의 completeSend() 메서드에 대한 테스트
1 parent 36324b4 commit a9434f7

File tree

5 files changed

+131
-4
lines changed

5 files changed

+131
-4
lines changed

src/main/java/gdsc/konkuk/platformcore/application/email/EmailService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ public List<EmailReceiver> registerReceivers(EmailTask task, Set<EmailReceiverIn
7373
return emailReceiverRepository.saveAll(mapToEmailReceiverList(task, receivers));
7474
}
7575

76+
@Transactional
77+
public void completeEmailReceiver(Long receiverId) {
78+
EmailReceiver receiver = emailReceiverRepository.findById(receiverId)
79+
.orElseThrow(() -> EmailNotFoundException.of(EmailErrorCode.EMAIL_NOT_FOUND));
80+
receiver.completeSend();
81+
}
82+
7683
@Transactional
7784
public EmailTask update(Long emailId, EmailTaskUpsertCommand command) {
7885
EmailTask task = findById(emailId);

src/main/java/gdsc/konkuk/platformcore/domain/email/entity/EmailReceiver.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ public void changeStatus(EmailSendStatus newStatus) {
6666
}
6767
}
6868

69+
public void completeSend() {
70+
this.sendStatus = EmailSendStatus.COMPLETED;
71+
this.statusUpdatedAt = LocalDateTime.now();
72+
this.sentAt = LocalDateTime.now();
73+
}
74+
6975
public boolean isPendingTimeout(int timeoutMinutes) {
7076
return this.sendStatus == EmailSendStatus.PENDING &&
7177
this.statusUpdatedAt != null &&

src/main/java/gdsc/konkuk/platformcore/external/email/EmailClient.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
import static gdsc.konkuk.platformcore.global.consts.PlatformConstants.EMAIL_RECEIVER_NAME_REGEXP;
44

5+
import gdsc.konkuk.platformcore.application.email.EmailService;
56
import gdsc.konkuk.platformcore.application.email.dtos.EmailTaskInfo;
67
import gdsc.konkuk.platformcore.domain.email.entity.EmailDetail;
78
import gdsc.konkuk.platformcore.domain.email.entity.EmailReceiver;
9+
import gdsc.konkuk.platformcore.domain.email.entity.EmailSendStatus;
810
import gdsc.konkuk.platformcore.external.email.exceptions.EmailClientErrorCode;
911
import gdsc.konkuk.platformcore.external.email.exceptions.EmailSendingException;
1012
import jakarta.mail.MessagingException;
@@ -23,11 +25,14 @@
2325
public class EmailClient {
2426

2527
private final JavaMailSender javaMailSender;
28+
private final EmailService emailService;
2629

2730
public void sendEmailToReceivers(EmailTaskInfo emailTaskInfo) {
2831
var emailDetail = emailTaskInfo.emailTask().getEmailDetail();
2932
var receivers = emailTaskInfo.emailReceivers();
30-
receivers.forEach(receiver -> sendEmail(receiver, emailDetail));
33+
receivers.stream()
34+
.filter(receiver -> receiver.getSendStatus().equals(EmailSendStatus.WAITING))
35+
.forEach(receiver -> sendEmail(receiver, emailDetail));
3136
}
3237

3338
public String replaceNameToken(String content, String name) {
@@ -48,6 +53,7 @@ private void sendEmail(EmailReceiver to, EmailDetail emailDetail) {
4853
log.info("Sending email to {}", to);
4954
MimeMessage message = generateMimeMessage(to, emailDetail);
5055
javaMailSender.send(message);
56+
emailService.completeEmailReceiver(to.getId());
5157
} catch (MailParseException | MessagingException e) {
5258
throw EmailSendingException.of(EmailClientErrorCode.MAIL_PARSING_ERROR, e.getMessage());
5359
} catch (MailException e) {

src/test/java/gdsc/konkuk/platformcore/application/email/EmailServiceTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import static org.mockito.BDDMockito.verify;
88
import static org.mockito.BDDMockito.when;
99
import static org.mockito.BDDMockito.willDoNothing;
10+
import static org.mockito.Mockito.never;
1011
import static org.mockito.MockitoAnnotations.openMocks;
1112

1213
import gdsc.konkuk.platformcore.application.email.dtos.EmailTaskInfo;
@@ -85,6 +86,40 @@ void should_success_when_getAllTaskInfoAsList() {
8586
assertEquals(emailTask2.getId(), actual.get(1).emailTask().getId());
8687
}
8788

89+
@Test
90+
@DisplayName("이메일 수신자 완료 처리 - 수신자를 찾을 수 없는 경우 예외 발생")
91+
void completeEmailReceiver_EmailNotFound_ThrowsException() {
92+
// given
93+
Long receiverId = 999L;
94+
given(emailReceiverRepository.findById(receiverId))
95+
.willReturn(Optional.empty());
96+
97+
// when & then
98+
EmailNotFoundException exception = assertThrows(
99+
EmailNotFoundException.class,
100+
() -> subject.completeEmailReceiver(receiverId)
101+
);
102+
103+
verify(emailReceiverRepository).findById(receiverId);
104+
105+
}
106+
107+
@Test
108+
@DisplayName("이메일 수신자 완료 처리 - null ID로 호출시 예외 발생")
109+
void completeEmailReceiver_NullId_ThrowsException() {
110+
// given
111+
Long receiverId = null;
112+
given(emailReceiverRepository.findById(receiverId))
113+
.willReturn(Optional.empty());
114+
115+
// when & then
116+
EmailNotFoundException exception = assertThrows(
117+
EmailNotFoundException.class,
118+
() -> subject.completeEmailReceiver(receiverId)
119+
);
120+
121+
verify(emailReceiverRepository).findById(null);
122+
}
88123

89124
@Test
90125
@DisplayName("getTaskDetails : 특정 이메일 전송 작업 조회 성공")

src/test/java/gdsc/konkuk/platformcore/external/email/EmailClientTest.java

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package gdsc.konkuk.platformcore.external.email;
22

3-
import static org.junit.jupiter.api.Assertions.assertEquals;
4-
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
3+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
4+
import static org.junit.jupiter.api.Assertions.*;
55
import static org.mockito.BDDMockito.given;
66
import static org.mockito.Mockito.anyString;
77
import static org.mockito.Mockito.eq;
@@ -11,9 +11,11 @@
1111
import static org.mockito.Mockito.when;
1212
import static org.mockito.MockitoAnnotations.openMocks;
1313

14+
import gdsc.konkuk.platformcore.application.email.EmailService;
1415
import gdsc.konkuk.platformcore.application.email.dtos.EmailTaskInfo;
1516
import gdsc.konkuk.platformcore.domain.email.entity.EmailDetail;
1617
import gdsc.konkuk.platformcore.domain.email.entity.EmailReceiver;
18+
import gdsc.konkuk.platformcore.domain.email.entity.EmailSendStatus;
1719
import gdsc.konkuk.platformcore.domain.email.entity.EmailTask;
1820
import gdsc.konkuk.platformcore.external.email.exceptions.EmailSendingException;
1921
import jakarta.mail.internet.MimeMessage;
@@ -31,13 +33,15 @@ class EmailClientTest {
3133

3234
@Mock
3335
private JavaMailSender javaMailSender;
36+
@Mock
37+
private EmailService emailService;
3438

3539
private EmailClient emailClient;
3640

3741
@BeforeEach
3842
void setUp() {
3943
openMocks(this);
40-
emailClient = spy(new EmailClient(javaMailSender));
44+
emailClient = spy(new EmailClient(javaMailSender, emailService));
4145
}
4246

4347
@Test
@@ -110,4 +114,73 @@ void should_success_when_replace_name_token() {
110114
// then
111115
assertEquals("안녕하세요, guest1님 합격을 축하드립니다!. guest1님과 함께할 수 있어 기쁩니다.", result);
112116
}
117+
118+
@Test
119+
@DisplayName("completeSend() 호출 시 상태가 COMPLETED로 변경되고 시간이 설정된다")
120+
void completeSend_ShouldUpdateStatusAndTimestamps() {
121+
// given
122+
EmailReceiver emailReceiver = EmailReceiver.builder()
123+
.emailTaskId(1L)
124+
.email("test@example.com")
125+
.name("테스트 사용자")
126+
.build();
127+
128+
LocalDateTime beforeCall = LocalDateTime.now().minusSeconds(1);
129+
130+
// when
131+
emailReceiver.completeSend();
132+
133+
// then
134+
LocalDateTime afterCall = LocalDateTime.now().plusSeconds(1);
135+
136+
assertAll(
137+
() -> assertThat(emailReceiver.getSendStatus()).isEqualTo(EmailSendStatus.COMPLETED),
138+
() -> assertThat(emailReceiver.getStatusUpdatedAt()).isNotNull(),
139+
() -> assertThat(emailReceiver.getStatusUpdatedAt()).isAfter(beforeCall),
140+
() -> assertThat(emailReceiver.getStatusUpdatedAt()).isBefore(afterCall),
141+
() -> assertThat(emailReceiver.getSentAt()).isNotNull(),
142+
() -> assertThat(emailReceiver.getSentAt()).isAfter(beforeCall),
143+
() -> assertThat(emailReceiver.getSentAt()).isBefore(afterCall)
144+
);
145+
}
146+
147+
@Test
148+
@DisplayName("WAITING 상태에서 completeSend() 호출 시 정상 동작")
149+
void completeSend_FromWaitingStatus_ShouldWork() {
150+
// given
151+
EmailReceiver emailReceiver = EmailReceiver.builder()
152+
.emailTaskId(1L)
153+
.email("test@example.com")
154+
.name("테스트 사용자")
155+
.build();
156+
157+
// EmailReceiver는 생성시 WAITING 상태
158+
assertThat(emailReceiver.getSendStatus()).isEqualTo(EmailSendStatus.WAITING);
159+
160+
// when
161+
emailReceiver.completeSend();
162+
163+
// then
164+
assertThat(emailReceiver.getSendStatus()).isEqualTo(EmailSendStatus.COMPLETED);
165+
}
166+
167+
@Test
168+
@DisplayName("completeSend() 호출 전후 sentAt 필드 변화 확인")
169+
void completeSend_SentAtField_ShouldBeSetCorrectly() {
170+
// given
171+
EmailReceiver emailReceiver = EmailReceiver.builder()
172+
.emailTaskId(1L)
173+
.email("test@example.com")
174+
.name("테스트 사용자")
175+
.build();
176+
177+
// 초기 상태에서는 sentAt이 null이어야 함
178+
assertThat(emailReceiver.getSentAt()).isNull();
179+
180+
// when
181+
emailReceiver.completeSend();
182+
183+
// then
184+
assertThat(emailReceiver.getSentAt()).isNotNull();
185+
}
113186
}

0 commit comments

Comments
 (0)